From d7cd148bfcad34461489a069913319d47dc568cd Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 1 May 2022 16:25:43 +0200 Subject: [PATCH 001/159] Upgrade gradle to Minecraft 1.16.5 --- build.gradle | 110 +++++++++++++---------- build.properties | 5 +- gradle.properties | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 54417 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 53 ++++++----- gradlew.bat | 43 +++++---- 7 files changed, 123 insertions(+), 91 deletions(-) diff --git a/build.gradle b/build.gradle index 18e2e0553b..63bb4fdc9b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,22 @@ -// For those who want the bleeding edge buildscript { repositories { - jcenter() - maven { - name = "forge" - url = "https://files.minecraftforge.net/maven" - } + maven { url = 'https://maven.minecraftforge.net' } + mavenCentral() } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true } } plugins { - id "com.matthewprenger.cursegradle" version "1.0.9" + id "com.matthewprenger.cursegradle" version "1.4.0" } apply plugin: 'scala' -apply plugin: 'net.minecraftforge.gradle.forge' +apply plugin: 'net.minecraftforge.gradle' apply plugin: 'maven-publish' -sourceCompatibility = JavaVersion.VERSION_1_7 -targetCompatibility = JavaVersion.VERSION_1_7 +java.toolchain.languageVersion = JavaLanguageVersion.of(8) file "build.properties" withReader { def prop = new Properties() @@ -57,13 +52,22 @@ ext.simpleVersion = version version = "MC${config.minecraft.version}-${project.version}" minecraft { - version = "${config.minecraft.version}-${config.forge.version}" - runDir = "run" - - mappings = config.minecraft.mappings - - replace "@VERSION@", project.simpleVersion - replace "/*@MCVERSIONDEP@*/", ", acceptedMinecraftVersions = \"[${config.minecraft.version}]\"" + mappings channel: "official", version: config.minecraft.version + + accessTransformer = file("src/main/resources/oc-at.cfg") + + runs { + client { + workingDirectory project.file("run") + property "forge.logging.markers", "REGISTRIES" + property "forge.logging.console.level", "debug" + mods { + opencomputers { + source sourceSets.main + } + } + } + } } repositories { @@ -73,58 +77,60 @@ repositories { configurations { embedded - compile.extendsFrom provided, embedded + implementation.extendsFrom embedded } dependencies { - deobfCompile ("li.cil.tis3d:TIS-3D:MC1.12-${config.tis3d.version}") { + minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" + + compileOnly fg.deobf("li.cil.tis3d:TIS-3D:MC1.12-${config.tis3d.version}") { exclude module: "jei_1.12" } - deobfCompile "com.mod-buildcraft:buildcraft-api:${config.buildcraft.version}" - deobfCompile "MCMultiPart2:MCMultiPart-exp:${config.mcmp.version}" - provided ("net.sengir.forestry:forestry_1.12.2:${config.forestry.version}") { + compileOnly fg.deobf("com.mod-buildcraft:buildcraft-api:${config.buildcraft.version}") + compileOnly fg.deobf("MCMultiPart2:MCMultiPart-exp:${config.mcmp.version}") + compileOnly ("net.sengir.forestry:forestry_1.12.2:${config.forestry.version}") { exclude module: "jei_1.12" } - deobfCompile "net.industrial-craft:industrialcraft-2:${config.ic2.version}" - deobfCompile "mcp.mobius.waila:Hwyla:${config.hwyla.version}:api" - deobfCompile "dan200.computercraft:ComputerCraft:${config.cc.version}" - deobfCompile "charset:Charset:${config.charset.version}:api" - - provided ("appeng:appliedenergistics2:${config.ae2.version}:api") { + compileOnly fg.deobf("net.industrial-craft:industrialcraft-2:${config.ic2.version}") + compileOnly fg.deobf("mcp.mobius.waila:Hwyla:${config.hwyla.version}:api") + compileOnly fg.deobf("dan200.computercraft:ComputerCraft:${config.cc.version}") + compileOnly fg.deobf("charset:Charset:${config.charset.version}:api") + + compileOnly ("appeng:appliedenergistics2:${config.ae2.version}:api") { transitive = false } - provided "extracells2:ExtraCells-api:${config.extracells.version}" - - provided("mekanism:Mekanism:${config.mekanism.version}:api") { + compileOnly "extracells2:ExtraCells-api:${config.extracells.version}" + + compileOnly("mekanism:Mekanism:${config.mekanism.version}:api") { transitive = false } - provided ("codechicken:ForgeMultipart:${config.minecraft.version}-${config.forgemultipart.version}:universal") { + compileOnly ("codechicken:ForgeMultipart:${config.minecraft.version}-${config.forgemultipart.version}:universal") { exclude module: "jei_1.12" exclude module: "CodeChickenLib" } - provided ("codechicken:ChickenASM:${config.casm.version}") + compileOnly ("codechicken:ChickenASM:${config.casm.version}") - provided "mezz.jei:jei_${config.minecraft.version}:${config.jei.version}" - provided "codechicken:CodeChickenLib:${config.minecraft.version}-${config.ccl.version}:universal" - provided "codechicken:WR-CBE:${config.minecraft.version}-${config.wrcbe.version}:universal" + compileOnly "mezz.jei:jei_${config.minecraft.version}:${config.jei.version}" + compileOnly "codechicken:CodeChickenLib:${config.minecraft.version}-${config.ccl.version}:universal" + compileOnly "codechicken:WR-CBE:${config.minecraft.version}-${config.wrcbe.version}:universal" - provided ("mrtjp:ProjectRed:${config.projred.version}:Base") { + compileOnly ("mrtjp:ProjectRed:${config.projred.version}:Base") { exclude module: "NotEnoughItems" exclude module: "CodeChickenLib" exclude module: "jei_1.12" exclude module: "ForgeMultipart" } - provided ("mrtjp:ProjectRed:${config.projred.version}:integration") { + compileOnly ("mrtjp:ProjectRed:${config.projred.version}:integration") { exclude module: "NotEnoughItems" exclude module: "CodeChickenLib" exclude module: "jei_1.12" exclude module: "ForgeMultipart" } - provided ("mrtjp:MrTJPCore:${config.mrtjpcore.version}:universal") { + compileOnly ("mrtjp:MrTJPCore:${config.mrtjpcore.version}:universal") { exclude module: "NotEnoughItems" exclude module: "CodeChickenLib" exclude module: "jei_1.12" @@ -133,18 +139,16 @@ dependencies { embedded files('libs/OpenComputers-JNLua.jar', 'libs/OpenComputers-LuaJ.jar') - testCompile "org.mockito:mockito-all:1.10.19" - - provided "codechicken:EnderStorage:${config.minecraft.version}-${config.enderstorage.version}:universal" + compileOnly "codechicken:EnderStorage:${config.minecraft.version}-${config.enderstorage.version}:universal" } processResources { inputs.property "version", project.simpleVersion - inputs.property "mcversion", project.minecraft.version + inputs.property "mcversion", config.minecraft.version inputs.property "fversion", config.forge.version from(sourceSets.main.resources.srcDirs) { include 'mcmod.info' - expand 'version':project.simpleVersion, 'mcversion':project.minecraft.version, 'fversion':config.forge.version + expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'fversion':config.forge.version } from(sourceSets.main.resources.srcDirs) { include 'application.conf' @@ -174,9 +178,17 @@ jar { } } manifest { - attributes FMLCorePlugin: "li.cil.oc.common.launch.TransformerLoader" - attributes FMLCorePluginContainsFMLMod: "true" - attributes FMLAT: "oc_at.cfg" + attributes([ + "Specification-Title": "opencomputers", + "Specification-Vendor": "li.cil.oc", + "Specification-Version": "1", + "Implementation-Title": project.name, + "Implementation-Version": "${project.simpleVersion}", + "Implementation-Vendor": "${config.mod.group}", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + "FMLCorePlugin": "li.cil.oc.common.launch.TransformerLoader", + "FMLCorePluginContainsFMLMod": "true" + ]) } } @@ -201,6 +213,8 @@ artifacts { archives javadocJar } +jar.finalizedBy('reobfJar') + publishing { publications { mavenJava(MavenPublication) { diff --git a/build.properties b/build.properties index 790da7a9ff..252b197161 100644 --- a/build.properties +++ b/build.properties @@ -1,6 +1,5 @@ -minecraft.version=1.12.2 -minecraft.mappings=snapshot_20180704 -forge.version=14.23.5.2778 +minecraft.version=1.16.5 +forge.version=36.2.34 mod.name=OpenComputers mod.group=li.cil.oc diff --git a/gradle.properties b/gradle.properties index e69de29bb2..3b11ebc1ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -0,0 +1 @@ +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 758de960ec7947253b058ff79c88ce51f3abe08a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch literal 59203 zcma&O1CT9Y(k9%tZQHhO+qUh#ZQHhO+qmuS+qP|E@9xZO?0h@l{(r>DQ>P;GjjD{w zH}lENr;dU&FbEU?00aa80D$0M0RRB{U*7-#kbjS|qAG&4l5%47zyJ#WrfA#1$1Ctx zf&Z_d{GW=lf^w2#qRJ|CvSJUi(^E3iv~=^Z(zH}F)3Z%V3`@+rNB7gTVU{Bb~90p|f+0(v;nz01EG7yDMX9@S~__vVgv%rS$+?IH+oZ03D5zYrv|^ zC1J)SruYHmCki$jLBlTaE5&dFG9-kq3!^i>^UQL`%gn6)jz54$WDmeYdsBE9;PqZ_ zoGd=P4+|(-u4U1dbAVQrFWoNgNd;0nrghPFbQrJctO>nwDdI`Q^i0XJDUYm|T|RWc zZ3^Qgo_Qk$%Fvjj-G}1NB#ZJqIkh;kX%V{THPqOyiq)d)0+(r9o(qKlSp*hmK#iIY zA^)Vr$-Hz<#SF=0@tL@;dCQsm`V9s1vYNq}K1B)!XSK?=I1)tX+bUV52$YQu*0%fnWEukW>mxkz+%3-S!oguE8u#MGzST8_Dy^#U?fA@S#K$S@9msUiX!gd_ow>08w5)nX{-KxqMOo7d?k2&?Vf z&diGDtZr(0cwPe9z9FAUSD9KC)7(n^lMWuayCfxzy8EZsns%OEblHFSzP=cL6}?J| z0U$H!4S_TVjj<`6dy^2j`V`)mC;cB%* z8{>_%E1^FH!*{>4a7*C1v>~1*@TMcLK{7nEQ!_igZC}ikJ$*<$yHy>7)oy79A~#xE zWavoJOIOC$5b6*q*F_qN1>2#MY)AXVyr$6x4b=$x^*aqF*L?vmj>Mgv+|ITnw_BoW zO?jwHvNy^prH{9$rrik1#fhyU^MpFqF2fYEt(;4`Q&XWOGDH8k6M=%@fics4ajI;st# zCU^r1CK&|jzUhRMv;+W~6N;u<;#DI6cCw-otsc@IsN3MoSD^O`eNflIoR~l4*&-%RBYk@gb^|-JXs&~KuSEmMxB}xSb z@K76cXD=Y|=I&SNC2E+>Zg?R6E%DGCH5J1nU!A|@eX9oS(WPaMm==k2s_ueCqdZw| z&hqHp)47`c{BgwgvY2{xz%OIkY1xDwkw!<0veB#yF4ZKJyabhyyVS`gZepcFIk%e2 zTcrmt2@-8`7i-@5Nz>oQWFuMC_KlroCl(PLSodswHqJ3fn<;gxg9=}~3x_L3P`9Sn zChIf}8vCHvTriz~T2~FamRi?rh?>3bX1j}%bLH+uFX+p&+^aXbOK7clZxdU~6Uxgy z8R=obwO4dL%pmVo*Ktf=lH6hnlz_5k3cG;m8lgaPp~?eD!Yn2kf)tU6PF{kLyn|oI@eQ`F z3IF7~Blqg8-uwUuWZScRKn%c2_}dXB6Dx_&xR*n9M9LXasJhtZdr$vBY!rP{c@=)& z#!?L$2UrkvClwQO>U*fSMs67oSj2mxiJ$t;E|>q%Kh_GzzWWO&3;ufU%2z%ucBU8H z3WIwr$n)cfCXR&>tyB7BcSInK>=ByZA%;cVEJhcg<#6N{aZC4>K41XF>ZgjG`z_u& zGY?;Ad?-sgiOnI`oppF1o1Gurqbi*;#x2>+SSV6|1^G@ooVy@fg?wyf@0Y!UZ4!}nGuLeC^l)6pwkh|oRY`s1Pm$>zZ3u-83T|9 zGaKJIV3_x+u1>cRibsaJpJqhcm%?0-L;2 zitBrdRxNmb0OO2J%Y&Ym(6*`_P3&&5Bw157{o7LFguvxC$4&zTy#U=W*l&(Q2MNO} zfaUwYm{XtILD$3864IA_nn34oVa_g^FRuHL5wdUd)+W-p-iWCKe8m_cMHk+=? zeKX)M?Dt(|{r5t7IenkAXo%&EXIb-i^w+0CX0D=xApC=|Xy(`xy+QG^UyFe z+#J6h_&T5i#sV)hj3D4WN%z;2+jJcZxcI3*CHXGmOF3^)JD5j&wfX)e?-|V0GPuA+ zQFot%aEqGNJJHn$!_}#PaAvQ^{3-Ye7b}rWwrUmX53(|~i0v{}G_sI9uDch_brX&6 zWl5Ndj-AYg(W9CGfQf<6!YmY>Ey)+uYd_JNXH=>|`OH-CDCmcH(0%iD_aLlNHKH z7bcW-^5+QV$jK?R*)wZ>r9t}loM@XN&M-Pw=F#xn(;u3!(3SXXY^@=aoj70;_=QE9 zGghsG3ekq#N||u{4We_25U=y#T*S{4I{++Ku)> zQ!DZW;pVcn>b;&g2;YE#+V`v*Bl&Y-i@X6D*OpNA{G@JAXho&aOk(_j^weW{#3X5Y z%$q_wpb07EYPdmyH(1^09i$ca{O<}7) zRWncXdSPgBE%BM#by!E>tdnc$8RwUJg1*x($6$}ae$e9Knj8gvVZe#bLi!<+&BkFj zg@nOpDneyc+hU9P-;jmOSMN|*H#>^Ez#?;%C3hg_65leSUm;iz)UkW)jX#p)e&S&M z1|a?wDzV5NVnlhRBCd_;F87wp>6c<&nkgvC+!@KGiIqWY4l}=&1w7|r6{oBN8xyzh zG$b#2=RJp_iq6)#t5%yLkKx(0@D=C3w+oiXtSuaQ%I1WIb-eiE$d~!)b@|4XLy!CZ z9p=t=%3ad@Ep+<9003D2KZ5VyP~_n$=;~r&YUg5UZ0KVD&tR1DHy9x)qWtKJp#Kq# zP*8p#W(8JJ_*h_3W}FlvRam?<4Z+-H77^$Lvi+#vmhL9J zJ<1SV45xi;SrO2f=-OB(7#iNA5)x1uNC-yNxUw|!00vcW2PufRm>e~toH;M0Q85MQLWd?3O{i8H+5VkR@l9Dg-ma ze2fZ%>G(u5(k9EHj2L6!;(KZ8%8|*-1V|B#EagbF(rc+5iL_5;Eu)L4Z-V;0HfK4d z*{utLse_rvHZeQ>V5H=f78M3Ntg1BPxFCVD{HbNA6?9*^YIq;B-DJd{Ca2L#)qWP? zvX^NhFmX?CTWw&Ns}lgs;r3i+Bq@y}Ul+U%pzOS0Fcv9~aB(0!>GT0)NO?p=25LjN z2bh>6RhgqD7bQj#k-KOm@JLgMa6>%-ok1WpOe)FS^XOU{c?d5shG(lIn3GiVBxmg`u%-j=)^v&pX1JecJics3&jvPI)mDut52? z3jEA)DM%}BYbxxKrizVYwq?(P&19EXlwD9^-6J+4!}9{ywR9Gk42jjAURAF&EO|~N z)?s>$Da@ikI4|^z0e{r`J8zIs>SpM~Vn^{3fArRu;?+43>lD+^XtUcY1HidJwnR6+ z!;oG2=B6Z_=M%*{z-RaHc(n|1RTKQdNjjV!Pn9lFt^4w|AeN06*j}ZyhqZ^!-=cyGP_ShV1rGxkx8t zB;8`h!S{LD%ot``700d0@Grql(DTt4Awgmi+Yr0@#jbe=2#UkK%rv=OLqF)9D7D1j z!~McAwMYkeaL$~kI~90)5vBhBzWYc3Cj1WI0RS`z000R8-@ET0dA~*r(gSiCJmQMN&4%1D zyVNf0?}sBH8zNbBLn>~(W{d3%@kL_eQ6jEcR{l>C|JK z(R-fA!z|TTRG40|zv}7E@PqCAXP3n`;%|SCQ|ZS%ym$I{`}t3KPL&^l5`3>yah4*6 zifO#{VNz3)?ZL$be;NEaAk9b#{tV?V7 zP|wf5YA*1;s<)9A4~l3BHzG&HH`1xNr#%){4xZ!jq%o=7nN*wMuXlFV{HaiQLJ`5G zBhDi#D(m`Q1pLh@Tq+L;OwuC52RdW7b8}~60WCOK5iYMUad9}7aWBuILb({5=z~YF zt?*Jr5NG+WadM{mDL>GyiByCuR)hd zA=HM?J6l1Xv0Dl+LW@w$OTcEoOda^nFCw*Sy^I@$sSuneMl{4ys)|RY#9&NxW4S)9 zq|%83IpslTLoz~&vTo!Ga@?rj_kw{|k{nv+w&Ku?fyk4Ki4I?);M|5Axm)t+BaE)D zm(`AQ#k^DWrjbuXoJf2{Aj^KT zFb1zMSqxq|vceV+Mf-)$oPflsO$@*A0n0Z!R{&(xh8s}=;t(lIy zv$S8x>m;vQNHuRzoaOo?eiWFe{0;$s`Bc+Osz~}Van${u;g(su`3lJ^TEfo~nERfP z)?aFzpDgnLYiERsKPu|0tq4l2wT)Atr6Qb%m-AUn6HnCue*yWICp7TjW$@sO zm5rm4aTcPQ(rfi7a`xP7cKCFrJD}*&_~xgLyr^-bmsL}y;A5P|al8J3WUoBSjqu%v zxC;mK!g(7r6RRJ852Z~feoC&sD3(6}^5-uLK8o)9{8L_%%rItZK9C){UxB|;G>JbP zsRRtS4-3B*5c+K2kvmgZK8472%l>3cntWUOVHxB|{Ay~aOg5RN;{PJgeVD*H%ac+y!h#wi%o2bF2Ca8IyMyH{>4#{E_8u^@+l-+n=V}Sq?$O z{091@v%Bd*3pk0^2UtiF9Z+(a@wy6 zUdw8J*ze$K#=$48IBi1U%;hmhO>lu!uU;+RS}p&6@rQila7WftH->*A4=5W|Fmtze z)7E}jh@cbmr9iup^i%*(uF%LG&!+Fyl@LFA-}Ca#bxRfDJAiR2dt6644TaYw1Ma79 zt8&DYj31j^5WPNf5P&{)J?WlCe@<3u^78wnd(Ja4^a>{^Tw}W>|Cjt^If|7l^l)^Q zbz|7~CF(k_9~n|h;ysZ+jHzkXf(*O*@5m zLzUmbHp=x!Q|!9NVXyipZ3)^GuIG$k;D)EK!a5=8MFLI_lpf`HPKl=-Ww%z8H_0$j ztJ||IfFG1lE9nmQ0+jPQy zCBdKkjArH@K7jVcMNz);Q(Q^R{d5G?-kk;Uu_IXSyWB)~KGIizZL(^&qF;|1PI7!E zTP`%l)gpX|OFn&)M%txpQ2F!hdA~hX1Cm5)IrdljqzRg!f{mN%G~H1&oqe`5eJCIF zHdD7O;AX-{XEV(a`gBFJ9ews#CVS2y!&>Cm_dm3C8*n3MA*e67(WC?uP@8TXuMroq z{#w$%z@CBIkRM7?}Xib+>hRjy?%G!fiw8! z8(gB+8J~KOU}yO7UGm&1g_MDJ$IXS!`+*b*QW2x)9>K~Y*E&bYMnjl6h!{17_8d!%&9D`a7r&LKZjC<&XOvTRaKJ1 zUY@hl5^R&kZl3lU3njk`3dPzxj$2foOL26r(9zsVF3n_F#v)s5vv3@dgs|lP#eylq62{<-vczqP!RpVBTgI>@O6&sU>W|do17+#OzQ7o5A$ICH z?GqwqnK^n2%LR;$^oZM;)+>$X3s2n}2jZ7CdWIW0lnGK-b#EG01)P@aU`pg}th&J-TrU`tIpb5t((0eu|!u zQz+3ZiOQ^?RxxK4;zs=l8q!-n7X{@jSwK(iqNFiRColuEOg}!7cyZi`iBX4g1pNBj zAPzL?P^Ljhn;1$r8?bc=#n|Ed7wB&oHcw()&*k#SS#h}jO?ZB246EGItsz*;^&tzp zu^YJ0=lwsi`eP_pU8}6JA7MS;9pfD;DsSsLo~ogzMNP70@@;Fm8f0^;>$Z>~}GWRw!W5J3tNX*^2+1f3hz{~rIzJo z6W%J(H!g-eI_J1>0juX$X4Cl6i+3wbc~k146UIX&G22}WE>0ga#WLsn9tY(&29zBvH1$`iWtTe zG2jYl@P!P)eb<5DsR72BdI7-zP&cZNI{7q3e@?N8IKc4DE#UVr->|-ryuJXk^u^>4 z$3wE~=q390;XuOQP~TNoDR?#|NSPJ%sTMInA6*rJ%go|=YjGe!B>z6u$IhgQSwoV* zjy3F2#I>uK{42{&IqP59)Y(1*Z>>#W8rCf4_eVsH)`v!P#^;BgzKDR`ARGEZzkNX+ zJUQu=*-ol=Xqqt5=`=pA@BIn@6a9G8C{c&`i^(i+BxQO9?YZ3iu%$$da&Kb?2kCCo zo7t$UpSFWqmydXf@l3bVJ=%K?SSw)|?srhJ-1ZdFu*5QhL$~-IQS!K1s@XzAtv6*Y zl8@(5BlWYLt1yAWy?rMD&bwze8bC3-GfNH=p zynNFCdxyX?K&G(ZZ)afguQ2|r;XoV^=^(;Cku#qYn4Lus`UeKt6rAlFo_rU`|Rq z&G?~iWMBio<78of-2X(ZYHx~=U0Vz4btyXkctMKdc9UM!vYr~B-(>)(Hc|D zMzkN4!PBg%tZoh+=Gba!0++d193gbMk2&krfDgcbx0jI92cq?FFESVg0D$>F+bil} zY~$)|>1HZsX=5sAZ2WgPB5P=8X#TI+NQ(M~GqyVB53c6IdX=k>Wu@A0Svf5#?uHaF zsYn|koIi3$(%GZ2+G+7Fv^lHTb#5b8sAHSTnL^qWZLM<(1|9|QFw9pnRU{svj}_Al zL)b9>fN{QiA($8peNEJyy`(a{&uh-T4_kdZFIVsKKVM(?05}76EEz?#W za^fiZOAd14IJ4zLX-n7Lq0qlQ^lW8Cvz4UKkV9~P}>sq0?xD3vg+$4vLm~C(+ zM{-3Z#qnZ09bJ>}j?6ry^h+@PfaD7*jZxBEY4)UG&daWb??6)TP+|3#Z&?GL?1i+280CFsE|vIXQbm| zM}Pk!U`U5NsNbyKzkrul-DzwB{X?n3E6?TUHr{M&+R*2%yOiXdW-_2Yd6?38M9Vy^ z*lE%gA{wwoSR~vN0=no}tP2Ul5Gk5M(Xq`$nw#ndFk`tcpd5A=Idue`XZ!FS>Q zG^0w#>P4pPG+*NC9gLP4x2m=cKP}YuS!l^?sHSFftZy{4CoQrb_ z^20(NnG`wAhMI=eq)SsIE~&Gp9Ne0nD4%Xiu|0Fj1UFk?6avDqjdXz{O1nKao*46y zT8~iA%Exu=G#{x=KD;_C&M+Zx4+n`sHT>^>=-1YM;H<72k>$py1?F3#T1*ef9mLZw z5naLQr?n7K;2l+{_uIw*_1nsTn~I|kkCgrn;|G~##hM;9l7Jy$yJfmk+&}W@JeKcF zx@@Woiz8qdi|D%aH3XTx5*wDlbs?dC1_nrFpm^QbG@wM=i2?Zg;$VK!c^Dp8<}BTI zyRhAq@#%2pGV49*Y5_mV4+OICP|%I(dQ7x=6Ob}>EjnB_-_18*xrY?b%-yEDT(wrO z9RY2QT0`_OpGfMObKHV;QLVnrK%mc?$WAdIT`kJQT^n%GuzE7|9@k3ci5fYOh(287 zuIbg!GB3xLg$YN=n)^pHGB0jH+_iIiC=nUcD;G6LuJsjn2VI1cyZx=a?ShCsF==QK z;q~*m&}L<-cb+mDDXzvvrRsybcgQ;Vg21P(uLv5I+eGc7o7tc6`;OA9{soHFOz zT~2?>Ts}gprIX$wRBb4yE>ot<8+*Bv`qbSDv*VtRi|cyWS>)Fjs>fkNOH-+PX&4(~ z&)T8Zam2L6puQl?;5zg9h<}k4#|yH9czHw;1jw-pwBM*O2hUR6yvHATrI%^mvs9q_ z&ccT0>f#eDG<^WG^q@oVqlJrhxH)dcq2cty@l3~|5#UDdExyXUmLQ}f4#;6fI{f^t zDCsgIJ~0`af%YR%Ma5VQq-p21k`vaBu6WE?66+5=XUd%Ay%D$irN>5LhluRWt7 zov-=f>QbMk*G##&DTQyou$s7UqjjW@k6=!I@!k+S{pP8R(2=e@io;N8E`EOB;OGoI zw6Q+{X1_I{OO0HPpBz!X!@`5YQ2)t{+!?M_iH25X(d~-Zx~cXnS9z>u?+If|iNJbx zyFU2d1!ITX64D|lE0Z{dLRqL1Ajj=CCMfC4lD3&mYR_R_VZ>_7_~|<^o*%_&jevU+ zQ4|qzci=0}Jydw|LXLCrOl1_P6Xf@c0$ieK2^7@A9UbF{@V_0p%lqW|L?5k>bVM8|p5v&2g;~r>B8uo<4N+`B zH{J)h;SYiIVx@#jI&p-v3dwL5QNV1oxPr8J%ooezTnLW>i*3Isb49%5i!&ac_dEXv zvXmVUck^QHmyrF8>CGXijC_R-y(Qr{3Zt~EmW)-nC!tiH`wlw5D*W7Pip;T?&j%kX z6DkZX4&}iw>hE(boLyjOoupf6JpvBG8}jIh!!VhnD0>}KSMMo{1#uU6kiFcA04~|7 zVO8eI&x1`g4CZ<2cYUI(n#wz2MtVFHx47yE5eL~8bot~>EHbevSt}LLMQX?odD{Ux zJMnam{d)W4da{l7&y-JrgiU~qY3$~}_F#G7|MxT)e;G{U`In&?`j<5D->}cb{}{T(4DF0BOk-=1195KB-E*o@c?`>y#4=dMtYtSY=&L{!TAjFVcq0y@AH`vH! z$41+u!Ld&}F^COPgL(EE{0X7LY&%D7-(?!kjFF7=qw<;`V{nwWBq<)1QiGJgUc^Vz ztMUlq1bZqKn17|6x6iAHbWc~l1HcmAxr%$Puv!znW)!JiukwIrqQ00|H$Z)OmGG@= zv%A8*4cq}(?qn4rN6o`$Y))(MyXr8R<2S^J+v(wmFmtac!%VOfN?&(8Nr!T@kV`N; z*Q33V3t`^rN&aBiHet)18wy{*wi1=W!B%B-Q6}SCrUl$~Hl{@!95ydml@FK8P=u4s z4e*7gV2s=YxEvskw2Ju!2%{8h01rx-3`NCPc(O zH&J0VH5etNB2KY6k4R@2Wvl^Ck$MoR3=)|SEclT2ccJ!RI9Nuter7u9@;sWf-%um;GfI!=eEIQ2l2p_YWUd{|6EG ze{yO6;lMc>;2tPrsNdi@&1K6(1;|$xe8vLgiouj%QD%gYk`4p{Ktv9|j+!OF-P?@p z;}SV|oIK)iwlBs+`ROXkhd&NK zzo__r!B>tOXpBJMDcv!Mq54P+n4(@dijL^EpO1wdg~q+!DT3lB<>9AANSe!T1XgC=J^)IP0XEZ()_vpu!!3HQyJhwh?r`Ae%Yr~b% zO*NY9t9#qWa@GCPYOF9aron7thfWT`eujS4`t2uG6)~JRTI;f(ZuoRQwjZjp5Pg34 z)rp$)Kr?R+KdJ;IO;pM{$6|2y=k_siqvp%)2||cHTe|b5Ht8&A{wazGNca zX$Ol?H)E_R@SDi~4{d-|8nGFhZPW;Cts1;08TwUvLLv&_2$O6Vt=M)X;g%HUr$&06 zISZb(6)Q3%?;3r~*3~USIg=HcJhFtHhIV(siOwV&QkQe#J%H9&E21!C*d@ln3E@J* zVqRO^<)V^ky-R|%{(9`l-(JXq9J)1r$`uQ8a}$vr9E^nNiI*thK8=&UZ0dsFN_eSl z(q~lnD?EymWLsNa3|1{CRPW60>DSkY9YQ;$4o3W7Ms&@&lv9eH!tk~N&dhqX&>K@} zi1g~GqglxkZ5pEFkllJ)Ta1I^c&Bt6#r(QLQ02yHTaJB~- zCcE=5tmi`UA>@P=1LBfBiqk)HB4t8D?02;9eXj~kVPwv?m{5&!&TFYhu>3=_ zsGmYZ^mo*-j69-42y&Jj0cBLLEulNRZ9vXE)8~mt9C#;tZs;=#M=1*hebkS;7(aGf zcs7zH(I8Eui9UU4L--))yy`&d&$In&VA2?DAEss4LAPCLd>-$i?lpXvn!gu^JJ$(DoUlc6wE98VLZ*z`QGQov5l4Fm_h?V-;mHLYDVOwKz7>e4+%AzeO>P6v}ndPW| zM>m#6Tnp7K?0mbK=>gV}=@k*0Mr_PVAgGMu$j+pWxzq4MAa&jpCDU&-5eH27Iz>m^ zax1?*HhG%pJ((tkR(V(O(L%7v7L%!_X->IjS3H5kuXQT2!ow(;%FDE>16&3r){!ex zhf==oJ!}YU89C9@mfDq!P3S4yx$aGB?rbtVH?sHpg?J5C->!_FHM%Hl3#D4eplxzQ zRA+<@LD%LKSkTk2NyWCg7u=$%F#;SIL44~S_OGR}JqX}X+=bc@swpiClB`Zbz|f!4 z7Ysah7OkR8liXfI`}IIwtEoL}(URrGe;IM8%{>b1SsqXh)~w}P>yiFRaE>}rEnNkT z!HXZUtxUp1NmFm)Dm@-{FI^aRQqpSkz}ZSyKR%Y}YHNzBk)ZIp} zMtS=aMvkgWKm9&oTcU0?S|L~CDqA+sHpOxwnswF-fEG)cXCzUR?ps@tZa$=O)=L+5 zf%m58cq8g_o}3?Bhh+c!w4(7AjxwQ3>WnVi<{{38g7yFboo>q|+7qs<$8CPXUFAN< zG&}BHbbyQ5n|qqSr?U~GY{@GJ{(Jny{bMaOG{|IkUj7tj^9pa9|FB_<+KHLxSxR;@ zHpS$4V)PP+tx}22fWx(Ku9y+}Ap;VZqD0AZW4gCDTPCG=zgJmF{|x;(rvdM|2|9a}cex6xrMkERnkE;}jvU-kmzd%_J50$M`lIPCKf+^*zL=@LW`1SaEc%=m zQ+lT06Gw+wVwvQ9fZ~#qd430v2HndFsBa9WjD0P}K(rZYdAt^5WQIvb%D^Q|pkVE^ zte$&#~zmULFACGfS#g=2OLOnIf2Of-k!(BIHjs77nr!5Q1*I9 z1%?=~#Oss!rV~?-6Gm~BWJiA4mJ5TY&iPm_$)H1_rTltuU1F3I(qTQ^U$S>%$l z)Wx1}R?ij0idp@8w-p!Oz{&*W;v*IA;JFHA9%nUvVDy7Q8woheC#|8QuDZb-L_5@R zOqHwrh|mVL9b=+$nJxM`3eE{O$sCt$UK^2@L$R(r^-_+z?lOo+me-VW=Zw z-Bn>$4ovfWd%SPY`ab-u9{INc*k2h+yH%toDHIyqQ zO68=u`N}RIIs7lsn1D){)~%>ByF<>i@qFb<-axvu(Z+6t7v<^z&gm9McRB~BIaDn$ z#xSGT!rzgad8o>~kyj#h1?7g96tOcCJniQ+*#=b7wPio>|6a1Z?_(TS{)KrPe}(8j z!#&A=k(&Pj^F;r)CI=Z{LVu>uj!_W1q4b`N1}E(i%;BWjbEcnD=mv$FL$l?zS6bW!{$7j1GR5ocn94P2u{ z70tAAcpqtQo<@cXw~@i-@6B23;317|l~S>CB?hR5qJ%J3EFgyBdJd^fHZu7AzHF(BQ!tyAz^L0`X z23S4Fe{2X$W0$zu9gm%rg~A>ijaE#GlYlrF9$ds^QtaszE#4M(OLVP2O-;XdT(XIC zatwzF*)1c+t~c{L=fMG8Z=k5lv>U0;C{caN1NItnuSMp)6G3mbahu>E#sj&oy94KC zpH}8oEw{G@N3pvHhp{^-YaZeH;K+T_1AUv;IKD<=mv^&Ueegrb!yf`4VlRl$M?wsl zZyFol(2|_QM`e_2lYSABpKR{{NlxlDSYQNkS;J66aT#MSiTx~;tUmvs-b*CrR4w=f z8+0;*th6kfZ3|5!Icx3RV11sp=?`0Jy3Fs0N4GZQMN=8HmT6%x9@{Dza)k}UwL6JT zHRDh;%!XwXr6yuuy`4;Xsn0zlR$k%r%9abS1;_v?`HX_hI|+EibVnlyE@3aL5vhQq zlIG?tN^w@0(v9M*&L+{_+RQZw=o|&BRPGB>e5=ys7H`nc8nx)|-g;s7mRc7hg{GJC zAe^vCIJhajmm7C6g! zL&!WAQ~5d_5)00?w_*|*H>3$loHrvFbitw#WvLB!JASO?#5Ig5$Ys10n>e4|3d;tS zELJ0|R4n3Az(Fl3-r^QiV_C;)lQ1_CW{5bKS15U|E9?ZgLec@%kXr84>5jV2a5v=w z?pB1GPdxD$IQL4)G||B_lI+A=08MUFFR4MxfGOu07vfIm+j=z9tp~5i_6jb`tR>qV z$#`=BQ*jpCjm$F0+F)L%xRlnS%#&gro6PiRfu^l!EVan|r3y}AHJQOORGx4~ z&<)3=K-tx518DZyp%|!EqpU!+X3Et7n2AaC5(AtrkW>_57i}$eqs$rupubg0a1+WO zGHZKLN2L0D;ab%{_S1Plm|hx8R?O14*w*f&2&bB050n!R2by zw!@XOQx$SqZ5I<(Qu$V6g>o#A!JVwErWv#(Pjx=KeS0@hxr4?13zj#oWwPS(7Ro|v z>Mp@Kmxo79q|}!5qtX2-O@U&&@6s~!I&)1WQIl?lTnh6UdKT_1R640S4~f=_xoN3- zI+O)$R@RjV$F=>Ti7BlnG1-cFKCC(t|Qjm{SalS~V-tX#+2ekRhwmN zZr`8{QF6y~Z!D|{=1*2D-JUa<(1Z=;!Ei!KiRNH?o{p5o3crFF=_pX9O-YyJchr$~ zRC`+G+8kx~fD2k*ZIiiIGR<8r&M@3H?%JVOfE>)})7ScOd&?OjgAGT@WVNSCZ8N(p zuQG~76GE3%(%h1*vUXg$vH{ua0b`sQ4f0*y=u~lgyb^!#CcPJa2mkSEHGLsnO^kb$ zru5_l#nu=Y{rSMWiYx?nO{8I!gH+?wEj~UM?IrG}E|bRIBUM>UlY<`T1EHpRr36vv zBi&dG8oxS|J$!zoaq{+JpJy+O^W(nt*|#g32bd&K^w-t>!Vu9N!k9eA8r!Xc{utY> zg9aZ(D2E0gL#W0MdjwES-7~Wa8iubPrd?8-$C4BP?*wok&O8+ykOx{P=Izx+G~hM8 z*9?BYz!T8~dzcZr#ux8kS7u7r@A#DogBH8km8Ry4slyie^n|GrTbO|cLhpqgMdsjX zJ_LdmM#I&4LqqsOUIXK8gW;V0B(7^$y#h3h>J0k^WJfAMeYek%Y-Dcb_+0zPJez!GM zAmJ1u;*rK=FNM0Nf}Y!!P9c4)HIkMnq^b;JFd!S3?_Qi2G#LIQ)TF|iHl~WKK6JmK zbv7rPE6VkYr_%_BT}CK8h=?%pk@3cz(UrZ{@h40%XgThP*-Oeo`T0eq9 zA8BnWZKzCy5e&&_GEsU4*;_k}(8l_&al5K-V*BFM=O~;MgRkYsOs%9eOY6s6AtE*<7GQAR2ulC3RAJrG_P1iQK5Z~&B z&f8X<>yJV6)oDGIlS$Y*D^Rj(cszTy5c81a5IwBr`BtnC6_e`ArI8CaTX_%rx7;cn zR-0?J_LFg*?(#n~G8cXut(1nVF0Oka$A$1FGcERU<^ggx;p@CZc?3UB41RY+wLS`LWFNSs~YP zuw1@DNN3lTd|jDL7gjBsd9}wIw}4xT2+8dBQzI00m<@?c2L%>}QLfK5%r!a-iII`p zX@`VEUH)uj^$;7jVUYdADQ2k*!1O3WdfgF?OMtUXNpQ1}QINamBTKDuv19^{$`8A1 zeq%q*O0mi@(%sZU>Xdb0Ru96CFqk9-L3pzLVsMQ`Xpa~N6CR{9Rm2)A|CI21L(%GW zh&)Y$BNHa=FD+=mBw3{qTgw)j0b!Eahs!rZnpu)z!!E$*eXE~##yaXz`KE5(nQM`s zD!$vW9XH)iMxu9R>r$VlLk9oIR%HxpUiW=BK@4U)|1WNQ=mz9a z^!KkO=>GaJ!GBXm{KJj^;kh-MkUlEQ%lza`-G&}C5y1>La1sR6hT=d*NeCnuK%_LV zOXt$}iP6(YJKc9j-Fxq~*ItVUqljQ8?oaysB-EYtFQp9oxZ|5m0^Hq(qV!S+hq#g( z?|i*H2MIr^Kxgz+3vIljQ*Feejy6S4v~jKEPTF~Qhq!(ms5>NGtRgO5vfPPc4Z^AM zTj!`5xEreIN)vaNxa|q6qWdg>+T`Ol0Uz)ckXBXEGvPNEL3R8hB3=C5`@=SYgAju1 z!)UBr{2~=~xa{b8>x2@C7weRAEuatC)3pkRhT#pMPTpSbA|tan%U7NGMvzmF?c!V8 z=pEWxbdXbTAGtWTyI?Fml%lEr-^AE}w#l(<7OIw;ctw}imYax&vR4UYNJZK6P7ZOd zP87XfhnUHxCUHhM@b*NbTi#(-8|wcv%3BGNs#zRCVV(W?1Qj6^PPQa<{yaBwZ`+<`w|;rqUY_C z&AeyKwwf*q#OW-F()lir=T^<^wjK65Lif$puuU5+tk$;e_EJ;Lu+pH>=-8=PDhkBg z8cWt%@$Sc#C6F$Vd+0507;{OOyT7Hs%nKS88q-W!$f~9*WGBpHGgNp}=C*7!RiZ5s zn1L_DbKF@B8kwhDiLKRB@lsXVVLK|ph=w%_`#owlf@s@V(pa`GY$8h%;-#h@TsO|Y8V=n@*!Rog7<7Cid%apR|x zOjhHCyfbIt%+*PCveTEcuiDi%Wx;O;+K=W?OFUV%)%~6;gl?<0%)?snDDqIvkHF{ zyI02)+lI9ov42^hL>ZRrh*HhjF9B$A@=H94iaBESBF=eC_KT$8A@uB^6$~o?3Wm5t1OIaqF^~><2?4e3c&)@wKn9bD? zoeCs;H>b8DL^F&>Xw-xjZEUFFTv>JD^O#1E#)CMBaG4DX9bD(Wtc8Rzq}9soQ8`jf zeSnHOL}<+WVSKp4kkq&?SbETjq6yr@4%SAqOG=9E(3YeLG9dtV+8vmzq+6PFPk{L; z(&d++iu=^F%b+ea$i2UeTC{R*0Isk;vFK!no<;L+(`y`3&H-~VTdKROkdyowo1iqR zbVW(3`+(PQ2>TKY>N!jGmGo7oeoB8O|P_!Ic@ zZ^;3dnuXo;WJ?S+)%P>{Hcg!Jz#2SI(s&dY4QAy_vRlmOh)QHvs_7c&zkJCmJGVvV zX;Mtb>QE+xp`KyciG$Cn*0?AK%-a|=o!+7x&&yzHQOS>8=B*R=niSnta^Pxp1`=md z#;$pS$4WCT?mbiCYU?FcHGZ#)kHVJTTBt^%XE(Q};aaO=Zik0UgLcc0I(tUpt(>|& zcxB_|fxCF7>&~5eJ=Dpn&5Aj{A^cV^^}(7w#p;HG&Q)EaN~~EqrE1qKrMAc&WXIE;>@<&)5;gD2?={Xf@Mvn@OJKw=8Mgn z!JUFMwD+s==JpjhroT&d{$kQAy%+d`a*XxDEVxy3`NHzmITrE`o!;5ClXNPb4t*8P zzAivdr{j_v!=9!^?T3y?gzmqDWX6mkzhIzJ-3S{T5bcCFMr&RPDryMcdwbBuZbsgN zGrp@^i?rcfN7v0NKGzDPGE#4yszxu=I_`MI%Z|10nFjU-UjQXXA?k8Pk|OE<(?ae) zE%vG#eZAlj*E7_3dx#Zz4kMLj>H^;}33UAankJiDy5ZvEhrjr`!9eMD8COp}U*hP+ zF}KIYx@pkccIgyxFm#LNw~G&`;o&5)2`5aogs`1~7cMZQ7zj!%L4E`2yzlQN6REX20&O<9 zKV6fyr)TScJPPzNTC2gL+0x#=u>(({{D7j)c-%tvqls3#Y?Z1m zV5WUE)zdJ{$p>yX;^P!UcXP?UD~YM;IRa#Rs5~l+*$&nO(;Ers`G=0D!twR(0GF@c zHl9E5DQI}Oz74n zfKP>&$q0($T4y$6w(p=ERAFh+>n%iaeRA%!T%<^+pg?M)@ucY<&59$x9M#n+V&>}=nO9wCV{O~lg&v#+jcUj(tQ z`0u1YH)-`U$15a{pBkGyPL0THv1P|4e@pf@3IBZS4dVJPo#H>pWq%Lr0YS-SeWash z8R7=jb28KPMI|_lo#GEO|5B?N_e``H*23{~a!AmUJ+fb4HX-%QI@lSEUxKlGV7z7Q zSKw@-TR>@1RL%w{x}dW#k1NgW+q4yt2Xf1J62Bx*O^WG8OJ|FqI4&@d3_o8Id@*)4 zYrk=>@!wv~mh7YWv*bZhxqSmFh2Xq)o=m;%n$I?GSz49l1$xRpPu_^N(vZ>*>Z<04 z2+rP70oM=NDysd!@fQdM2OcyT?3T^Eb@lIC-UG=Bw{BjQ&P`KCv$AcJ;?`vdZ4){d z&gkoUK{$!$$K`3*O-jyM1~p-7T*qb)Ys>Myt^;#1&a%O@x8A+E>! zY8=eD`ZG)LVagDLBeHg>=atOG?Kr%h4B%E6m@J^C+U|y)XX@f z8oyJDW|9g=<#f<{JRr{y#~euMnv)`7j=%cHWLc}ngjq~7k**6%4u>Px&W%4D94(r* z+akunK}O0DC2A%Xo9jyF;DobX?!1I(7%}@7F>i%&nk*LMO)bMGg2N+1iqtg+r(70q zF5{Msgsm5GS7DT`kBsjMvOrkx&|EU!{{~gL4d2MWrAT=KBQ-^zQCUq{5PD1orxlIL zq;CvlWx#f1NWvh`hg011I%?T_s!e38l*lWVt|~z-PO4~~1g)SrJ|>*tXh=QfXT)%( z+ex+inPvD&O4Ur;JGz>$sUOnWdpSLcm1X%aQDw4{dB!cnj`^muI$CJ2%p&-kULVCE z>$eMR36kN$wCPR+OFDM3-U(VOrp9k3)lI&YVFqd;Kpz~K)@Fa&FRw}L(SoD z9B4a+hQzZT-BnVltst&=kq6Y(f^S4hIGNKYBgMxGJ^;2yrO}P3;r)(-I-CZ)26Y6? z&rzHI_1GCvGkgy-t1E;r^3Le30|%$ebDRu2+gdLG)r=A~Qz`}~&L@aGJ{}vVs_GE* zVUjFnzHiXfKQbpv&bR&}l2bzIjAooB)=-XNcYmrGmBh(&iu@o!^hn0^#}m2yZZUK8 zufVm7Gq0y`Mj;9b>`c?&PZkU0j4>IL=UL&-Lp3j&47B5pAW4JceG{!XCA)kT<%2nqCxj<)uy6XR_uws~>_MEKPOpAQ!H zkn>FKh)<9DwwS*|Y(q?$^N!6(51O0 z^JM~Ax{AI1Oj$fs-S5d4T7Z_i1?{%0SsIuQ&r8#(JA=2iLcTN+?>wOL532%&dMYkT z*T5xepC+V6zxhS@vNbMoi|i)=rpli@R9~P!39tWbSSb904ekv7D#quKbgFEMTb48P zuq(VJ+&L8aWU(_FCD$3^uD!YM%O^K(dvy~Wm2hUuh6bD|#(I39Xt>N1Y{ZqXL`Fg6 zKQ?T2htHN!(Bx;tV2bfTtIj7e)liN-29s1kew>v(D^@)#v;}C4-G=7x#;-dM4yRWm zyY`cS21ulzMK{PoaQ6xChEZ}o_#}X-o}<&0)$1#3we?+QeLt;aVCjeA)hn!}UaKt< zat1fHEx13y-rXNMvpUUmCVzocPmN~-Y4(YJvQ#db)4|%B!rBsgAe+*yor~}FrNH08 z3V!97S}D7d$zbSD{$z;@IYMxM6aHdypIuS*pr_U6;#Y!_?0i|&yU*@16l z*dcMqDQgfNBf}?quiu4e>H)yTVfsp#f+Du0@=Kc41QockXkCkvu>FBd6Q+@FL!(Yx z2`YuX#eMEiLEDhp+9uFqME_E^faV&~9qjBHJkIp~%$x^bN=N)K@kvSVEMdDuzA0sn z88CBG?`RX1@#hQNd`o^V{37)!w|nA)QfiYBE^m=yQKv-fQF+UCMcuEe1d4BH7$?>b zJl-r9@0^Ie=)guO1vOd=i$_4sz>y3x^R7n4ED!5oXL3@5**h(xr%Hv)_gILarO46q+MaDOF%ChaymKoI6JU5Pg;7#2n9-18|S1;AK+ zgsn6;k6-%!QD>D?cFy}8F;r@z8H9xN1jsOBw2vQONVqBVEbkiNUqgw~*!^##ht>w0 zUOykwH=$LwX2j&nLy=@{hr)2O&-wm-NyjW7n~Zs9UlH;P7iP3 zI}S(r0YFVYacnKH(+{*)Tbw)@;6>%=&Th=+Z6NHo_tR|JCI8TJiXv2N7ei7M^Q+RM z?9o`meH$5Yi;@9XaNR#jIK^&{N|DYNNbtdb)XW1Lv2k{E>;?F`#Pq|&_;gm~&~Zc9 zf+6ZE%{x4|{YdtE?a^gKyzr}dA>OxQv+pq|@IXL%WS0CiX!V zm$fCePA%lU{%pTKD7|5NJHeXg=I0jL@$tOF@K*MI$)f?om)D63K*M|r`gb9edD1~Y zc|w7N)Y%do7=0{RC|AziW7#am$)9jciRJ?IWl9PE{G3U+$%FcyKs_0Cgq`=K3@ttV z9g;M!3z~f_?P%y3-ph%vBMeS@p7P&Ea8M@97+%XEj*(1E6vHj==d zjsoviB>j^$_^OI_DEPvFkVo(BGRo%cJeD){6Uckei=~1}>sp299|IRjhXe)%?uP0I zF5+>?0#Ye}T^Y$u_rc4=lPcq4K^D(TZG-w30-YiEM=dcK+4#o*>lJ8&JLi+3UcpZk z!^?95S^C0ja^jwP`|{<+3cBVog$(mRdQmadS+Vh~z zS@|P}=|z3P6uS+&@QsMp0no9Od&27O&14zHXGAOEy zh~OKpymK5C%;LLb467@KgIiVwYbYd6wFxI{0-~MOGfTq$nBTB!{SrWmL9Hs}C&l&l#m?s*{tA?BHS4mVKHAVMqm63H<|c5n0~k)-kbg zXidai&9ZUy0~WFYYKT;oe~rytRk?)r8bptITsWj(@HLI;@=v5|XUnSls7$uaxFRL+ zRVMGuL3w}NbV1`^=Pw*0?>bm8+xfeY(1PikW*PB>>Tq(FR`91N0c2&>lL2sZo5=VD zQY{>7dh_TX98L2)n{2OV=T10~*YzX27i2Q7W86M4$?gZIXZaBq#sA*{PH8){|GUi;oM>e?ua7eF4WFuFYZSG| zze?srg|5Ti8Og{O zeFxuw9!U+zhyk?@w zjsA6(oKD=Ka;A>Ca)oPORxK+kxH#O@zhC!!XS4@=swnuMk>t+JmLmFiE^1aX3f<)D@`%K0FGK^gg1a1j>zi z2KhV>sjU7AX3F$SEqrXSC}fRx64GDoc%!u2Yag68Lw@w9v;xOONf@o)Lc|Uh3<21ctTYu-mFZuHk*+R{GjXHIGq3p)tFtQp%TYqD=j1&y)>@zxoxUJ!G@ zgI0XKmP6MNzw>nRxK$-Gbzs}dyfFzt>#5;f6oR27ql!%+{tr+(`(>%51|k`ML} zY4eE)Lxq|JMas(;JibNQds1bUB&r}ydMQXBY4x(^&fY_&LlQC)3hylc$~8&~|06-D z#T+%66rYbHX%^KuqJED_wuGB+=h`nWA!>1n0)3wZrBG3%`b^Ozv6__dNa@%V14|!D zQ?o$z5u0^8`giv%qE!BzZ!3j;BlDlJDk)h@9{nSQeEk!z9RGW) z${RSF3phEM*ce*>Xdp}585vj$|40=&S{S-GTiE?Op*vY&Lvr9}BO$XWy80IF+6@%n z5*2ueT_g@ofP#u5pxb7n*fv^Xtt7&?SRc{*2Ka-*!BuOpf}neHGCiHy$@Ka1^Dint z;DkmIL$-e)rj4o2WQV%Gy;Xg(_Bh#qeOsTM2f@KEe~4kJ8kNLQ+;(!j^bgJMcNhvklP5Z6I+9Fq@c&D~8Fb-4rmDT!MB5QC{Dsb;BharP*O;SF4& zc$wj-7Oep7#$WZN!1nznc@Vb<_Dn%ga-O#J(l=OGB`dy=Sy&$(5-n3zzu%d7E#^8`T@}V+5B;PP8J14#4cCPw-SQTdGa2gWL0*zKM z#DfSXs_iWOMt)0*+Y>Lkd=LlyoHjublNLefhKBv@JoC>P7N1_#> zv=mLWe96%EY;!ZGSQDbZWb#;tzqAGgx~uk+-$+2_8U`!ypbwXl z^2E-FkM1?lY@yt8=J3%QK+xaZ6ok=-y%=KXCD^0r!5vUneW>95PzCkOPO*t}p$;-> ze5j-BLT_;)cZQzR2CEsm@rU7GZfFtdp*a|g4wDr%8?2QkIGasRfDWT-Dvy*U{?IHT z*}wGnzdlSptl#ZF^sf)KT|BJs&kLG91^A6ls{CzFprZ6-Y!V0Xysh%9p%iMd7HLsS zN+^Un$tDV)T@i!v?3o0Fsx2qI(AX_$dDkBzQ@fRM%n zRXk6hb9Py#JXUs+7)w@eo;g%QQ95Yq!K_d=z{0dGS+pToEI6=Bo8+{k$7&Z zo4>PH(`ce8E-Ps&uv`NQ;U$%t;w~|@E3WVOCi~R4oj5wP?%<*1C%}Jq%a^q~T7u>K zML5AKfQDv6>PuT`{SrKHRAF+^&edg6+5R_#H?Lz3iGoWo#PCEd0DS;)2U({{X#zU^ zw_xv{4x7|t!S)>44J;KfA|DC?;uQ($l+5Vp7oeqf7{GBF9356nx|&B~gs+@N^gSdd zvb*>&W)|u#F{Z_b`f#GVtQ`pYv3#||N{xj1NgB<#=Odt6{eB%#9RLt5v zIi|0u70`#ai}9fJjKv7dE!9ZrOIX!3{$z_K5FBd-Kp-&e4(J$LD-)NMTp^_pB`RT; zftVVlK2g@+1Ahv2$D){@Y#cL#dUj9*&%#6 zd2m9{1NYp>)6=oAvqdCn5#cx{AJ%S8skUgMglu2*IAtd+z1>B&`MuEAS(D(<6X#Lj z?f4CFx$)M&$=7*>9v1ER4b6!SIz-m0e{o0BfkySREchp?WdVPpQCh!q$t>?rL!&Jg zd#heM;&~A}VEm8Dvy&P|J*eAV&w!&Nx6HFV&B8jJFVTmgLaswn!cx$&%JbTsloz!3 zMEz1d`k==`Ueub_JAy_&`!ogbwx27^ZXgFNAbx=g_I~5nO^r)}&myw~+yY*cJl4$I znNJ32M&K=0(2Dj_>@39`3=FX!v3nZHno_@q^!y}%(yw0PqOo=);6Y@&ylVe>nMOZ~ zd>j#QQSBn3oaWd;qy$&5(5H$Ayi)0haAYO6TH>FR?rhqHmNOO+(})NB zLI@B@v0)eq!ug`>G<@htRlp3n!EpU|n+G+AvXFrWSUsLMBfL*ZB`CRsIVHNTR&b?K zxBgsN0BjfB>UVcJ|x%=-zb%OV7lmZc& zxiupadZVF7)6QuhoY;;FK2b*qL0J-Rn-8!X4ZY$-ZSUXV5DFd7`T41c(#lAeLMoeT z4%g655v@7AqT!i@)Edt5JMbN(=Q-6{=L4iG8RA%}w;&pKmtWvI4?G9pVRp|RTw`g0 zD5c12B&A2&P6Ng~8WM2eIW=wxd?r7A*N+&!Be7PX3s|7~z=APxm=A?5 zt>xB4WG|*Td@VX{Rs)PV0|yK`oI3^xn(4c_j&vgxk_Y3o(-`_5o`V zRTghg6%l@(qodXN;dB#+OKJEEvhfcnc#BeO2|E(5df-!fKDZ!%9!^BJ_4)9P+9Dq5 zK1=(v?KmIp34r?z{NEWnLB3Px{XYwy-akun4F7xTRr2^zeYW{gcK9)>aJDdU5;w5@ zak=<+-PLH-|04pelTb%ULpuuuJC7DgyT@D|p{!V!0v3KpDnRjANN12q6SUR3mb9<- z>2r~IApQGhstZ!3*?5V z8#)hJ0TdZg0M-BK#nGFP>$i=qk82DO z7h;Ft!D5E15OgW)&%lej*?^1~2=*Z5$2VX>V{x8SC+{i10BbtUk9@I#Vi&hX)q
Q!LwySI{Bnv%Sm)yh{^sSVJ8&h_D-BJ_YZe5eCaAWU9b$O2c z$T|{vWVRtOL!xC0DTc(Qbe`ItNtt5hr<)VijD0{U;T#bUEp381_y`%ZIav?kuYG{iyYdEBPW=*xNSc;Rlt6~F4M`5G+VtOjc z*0qGzCb@gME5udTjJA-9O<&TWd~}ysBd(eVT1-H82-doyH9RST)|+Pb{o*;$j9Tjs zhU!IlsPsj8=(x3bAKJTopW3^6AKROHR^7wZ185wJGVhA~hEc|LP;k7NEz-@4p5o}F z`AD6naG3(n=NF9HTH81=F+Q|JOz$7wm9I<+#BSmB@o_cLt2GkW9|?7mM;r!JZp89l zbo!Hp8=n!XH1{GwaDU+k)pGp`C|cXkCU5%vcH)+v@0eK>%7gWxmuMu9YLlChA|_D@ zi#5zovN_!a-0?~pUV-Rj*1P)KwdU-LguR>YM&*Nen+ln8Q$?WFCJg%DY%K}2!!1FE zDv-A%Cbwo^p(lzac&_TZ-l#9kq`mhLcY3h9ZTUVCM(Ad&=EriQY5{jJv<5K&g|*Lk zgV%ILnf1%8V2B0E&;Sp4sYbYOvvMebLwYwzkRQ#F8GpTQq#uv=J`uaSJ34OWITeSGo6+-8Xw znCk*n{kdDEi)Hi&u^)~cs@iyCkFWB2SWZU|Uc%^43ZIZQ-vWNExCCtDWjqHs;;tWf$v{}0{p0Rvxkq``)*>+Akq%|Na zA`@~-Vfe|+(AIlqru+7Ceh4nsVmO9p9jc8}HX^W&ViBDXT+uXbT#R#idPn&L>+#b6 zflC-4C5-X;kUnR~L>PSLh*gvL68}RBsu#2l`s_9KjUWRhiqF`j)`y`2`YU(>3bdBj z?>iyjEhe-~$^I5!nn%B6Wh+I`FvLNvauve~eX<+Ipl&04 zT}};W&1a3%W?dJ2=N#0t?e+aK+%t}5q%jSLvp3jZ%?&F}nOOWr>+{GFIa%wO_2`et z=JzoRR~}iKuuR+azPI8;Gf9)z3kyA4EIOSl!sRR$DlW}0>&?GbgPojmjmnln;cTqCt=ADbE zZ8GAnoM+S1(5$i8^O4t`ue;vO4i}z0wz-QEIVe5_u03;}-!G1NyY8;h^}y;tzY}i5 zqQr#Ur3Fy8sSa$Q0ys+f`!`+>9WbvU_I`Sj;$4{S>O3?#inLHCrtLy~!s#WXV=oVP zeE93*Nc`PBi4q@%Ao$x4lw9vLHM!6mn3-b_cebF|n-2vt-zYVF_&sDE--J-P;2WHo z+@n2areE0o$LjvjlV2X7ZU@j+`{*8zq`JR3gKF#EW|#+{nMyo-a>nFFTg&vhyT=b} zDa8+v0(Dgx0yRL@ZXOYIlVSZ0|MFizy0VPW8;AfA5|pe!#j zX}Py^8fl5SyS4g1WSKKtnyP+_PoOwMMwu`(i@Z)diJp~U54*-miOchy7Z35eL>^M z4p<-aIxH4VUZgS783@H%M7P9hX>t{|RU7$n4T(brCG#h9e9p! z+o`i;EGGq3&pF;~5V~eBD}lC)>if$w%Vf}AFxGqO88|ApfHf&Bvu+xdG)@vuF}Yvk z)o;~k-%+0K0g+L`Wala!$=ZV|z$e%>f0%XoLib%)!R^RoS+{!#X?h-6uu zF&&KxORdZU&EwQFITIRLo(7TA3W}y6X{?Y%y2j0It!ekU#<)$qghZtpcS>L3uh`Uj z7GY;6f$9qKynP#oS3$$a{p^{D+0oJQ71`1?OAn_m8)UGZmj3l*ZI)`V-a>MKGGFG< z&^jg#Ok%(hhm>hSrZ5;Qga4u(?^i>GiW_j9%_7M>j(^|Om$#{k+^*ULnEgzW_1gCICtAD^WpC`A z{9&DXkG#01Xo)U$OC(L5Y$DQ|Q4C6CjUKk1UkPj$nXH##J{c8e#K|&{mA*;b$r0E4 zUNo0jthwA(c&N1l=PEe8Rw_8cEl|-eya9z&H3#n`B$t#+aJ03RFMzrV@gowbe8v(c zIFM60^0&lCFO10NU4w@|61xiZ4CVXeaKjd;d?sv52XM*lS8XiVjgWpRB;&U_C0g+`6B5V&w|O6B*_q zsATxL!M}+$He)1eOWECce#eS@2n^xhlB4<_Nn?yCVEQWDs(r`|@2GqLe<#(|&P0U? z$7V5IgpWf09uIf_RazRwC?qEqRaHyL?iiS05UiGesJy%^>-C{{ypTBI&B0-iUYhk> zIk<5xpsuV@g|z(AZD+C-;A!fTG=df1=<%nxy(a(IS+U{ME4ZbDEBtcD_3V=icT6*_ z)>|J?>&6%nvHhZERBtjK+s4xnut*@>GAmA5m*OTp$!^CHTr}vM4n(X1Q*;{e-Rd2BCF-u@1ZGm z!S8hJ6L=Gl4T_SDa7Xx|-{4mxveJg=ctf`BJ*fy!yF6Dz&?w(Q_6B}WQVtNI!BVBC zKfX<>7vd6C96}XAQmF-Jd?1Q4eTfRB3q7hCh0f!(JkdWT5<{iAE#dKy*Jxq&3a1@~ z8C||Dn2mFNyrUV|<-)C^_y7@8c2Fz+2jrae9deBDu;U}tJ{^xAdxCD248(k;dCJ%o z`y3sADe>U%suxwwv~8A1+R$VB=Q?%U?4joI$um;aH+eCrBqpn- z%79D_7rb;R-;-9RTrwi9dPlg8&@tfWhhZ(Vx&1PQ+6(huX`;M9x~LrW~~#3{j0Bh2kDU$}@!fFQej4VGkJv?M4rU^x!RU zEwhu$!CA_iDjFjrJa`aocySDX16?~;+wgav;}Zut6Mg%C4>}8FL?8)Kgwc(Qlj{@#2Pt0?G`$h7P#M+qoXtlV@d}%c&OzO+QYKK`kyXaK{U(O^2DyIXCZlNQjt0^8~8JzNGrIxhj}}M z&~QZlbx%t;MJ(Vux;2tgNKGlAqphLq%pd}JG9uoVHUo?|hN{pLQ6Em%r*+7t^<);X zm~6=qChlNAVXNN*Sow->*4;}T;l;D1I-5T{Bif@4_}=>l`tK;qqDdt5zvisCKhMAH z#r}`)7VW?LZqfdmXQ%zo5bJ00{Xb9^YKrk0Nf|oIW*K@(=`o2Vndz}ZDyk{!u}PVx zzd--+_WC*U{~DH3{?GI64IB+@On&@9X>EUAo&L+G{L^dozaI4C3G#2wr~hseW@K&g zKWs{uHu-9Je!3;4pE>eBltKUXb^*hG8I&413)$J&{D4N%7PcloU6bn%jPxJyQL?g* z9g+YFFEDiE`8rW^laCNzQmi7CTnPfwyg3VDHRAl>h=In6jeaVOP@!-CP60j3+#vpL zEYmh_oP0{-gTe7Or`L6x)6w?77QVi~jD8lWN@3RHcm80iV%M1A!+Y6iHM)05iC64tb$X2lV_%Txk@0l^hZqi^%Z?#- zE;LE0uFx)R08_S-#(wC=dS&}vj6P4>5ZWjhthP=*Hht&TdLtKDR;rXEX4*z0h74FA zMCINqrh3Vq;s%3MC1YL`{WjIAPkVL#3rj^9Pj9Ss7>7duy!9H0vYF%>1jh)EPqvlr6h%R%CxDsk| z!BACz7E%j?bm=pH6Eaw{+suniuY7C9Ut~1cWfOX9KW9=H><&kQlinPV3h9R>3nJvK z4L9(DRM=x;R&d#a@oFY7mB|m8h4692U5eYfcw|QKwqRsshN(q^v$4$)HgPpAJDJ`I zkqjq(8Cd!K!+wCd=d@w%~e$=gdUgD&wj$LQ1r>-E=O@c ze+Z$x{>6(JA-fNVr)X;*)40Eym1TtUZI1Pwwx1hUi+G1Jlk~vCYeXMNYtr)1?qwyg zsX_e*$h?380O00ou?0R@7-Fc59o$UvyVs4cUbujHUA>sH!}L54>`e` zHUx#Q+Hn&Og#YVOuo*niy*GU3rH;%f``nk#NN5-xrZ34NeH$l`4@t);4(+0|Z#I>Y z)~Kzs#exIAaf--65L0UHT_SvV8O2WYeD>Mq^Y6L!Xu8%vnpofG@w!}R7M28?i1*T&zp3X4^OMCY6(Dg<-! zXmcGQrRgHXGYre7GfTJ)rhl|rs%abKT_Nt24_Q``XH{88NVPW+`x4ZdrMuO0iZ0g` z%p}y};~T5gbb9SeL8BSc`SO#ixC$@QhXxZ=B}L`tP}&k?1oSPS=4%{UOHe0<_XWln zwbl5cn(j-qK`)vGHY5B5C|QZd5)W7c@{bNVXqJ!!n$^ufc?N9C-BF2QK1(kv++h!>$QbAjq)_b$$PcJdV+F7hz0Hu@ zqj+}m0qn{t^tD3DfBb~0B36|Q`bs*xs|$i^G4uNUEBl4g;op-;Wl~iThgga?+dL7s zUP(8lMO?g{GcYpDS{NM!UA8Hco?#}eNEioRBHy4`mq!Pd-9@-97|k$hpEX>xoX+dY zDr$wfm^P&}Wu{!%?)U_(%Mn79$(ywvu*kJ9r4u|MyYLI_67U7%6Gd_vb##Nerf@>& z8W11z$$~xEZt$dPG}+*IZky+os5Ju2eRi;1=rUEeIn>t-AzC_IGM-IXWK3^6QNU+2pe=MBn4I*R@A%-iLDCOHTE-O^wo$sL_h{dcPl=^muAQb`_BRm};=cy{qSkui;`WSsj9%c^+bIDQ z0`_?KX0<-=o!t{u(Ln)v>%VGL z0pC=GB7*AQ?N7N{ut*a%MH-tdtNmNC+Yf$|KS)BW(gQJ*z$d{+{j?(e&hgTy^2|AR9vx1Xre2fagGv0YXWqtNkg*v%40v?BJBt|f9wX5 z{QTlCM}b-0{mV?IG>TW_BdviUKhtosrBqdfq&Frdz>cF~yK{P@(w{Vr7z2qKFwLhc zQuogKO@~YwyS9%+d-zD7mJG~@?EFJLSn!a&mhE5$_4xBl&6QHMzL?CdzEnC~C3$X@ zvY!{_GR06ep5;<#cKCSJ%srxX=+pn?ywDwtJ2{TV;0DKBO2t++B(tIO4)Wh`rD13P z4fE$#%zkd=UzOB74gi=-*CuID&Z3zI^-`4U^S?dHxK8fP*;fE|a(KYMgMUo`THIS1f!*6dOI2 zFjC3O=-AL`6=9pp;`CYPTdVX z8(*?V&%QoipuH0>WKlL8A*zTKckD!paN@~hh zmXzm~qZhMGVdQGd=AG8&20HW0RGV8X{$9LldFZYm zE?}`Q3i?xJRz43S?VFMmqRyvWaS#(~Lempg9nTM$EFDP(Gzx#$r)W&lpFKqcAoJh-AxEw$-bjW>`_+gEi z2w`99#UbFZGiQjS8kj~@PGqpsPX`T{YOj`CaEqTFag;$jY z8_{Wzz>HXx&G*Dx<5skhpETxIdhKH?DtY@b9l8$l?UkM#J-Snmts7bd7xayKTFJ(u zyAT&@6cAYcs{PBfpqZa%sxhJ5nSZBPji?Zlf&}#L?t)vC4X5VLp%~fz2Sx<*oN<7` z?ge=k<=X7r<~F7Tvp9#HB{!mA!QWBOf%EiSJ6KIF8QZNjg&x~-%e*tflL(ji_S^sO ztmib1rp09uon}RcsFi#k)oLs@$?vs(i>5k3YN%$T(5Or(TZ5JW9mA6mIMD08=749$ z!d+l*iu{Il7^Yu}H;lgw=En1sJpCKPSqTCHy4(f&NPelr31^*l%KHq^QE>z>Ks_bH zjbD?({~8Din7IvZeJ>8Ey=e;I?thpzD=zE5UHeO|neioJwG;IyLk?xOz(yO&0DTU~ z^#)xcs|s>Flgmp;SmYJ4g(|HMu3v7#;c*Aa8iF#UZo7CvDq4>8#qLJ|YdZ!AsH%^_7N1IQjCro

K7UpUK$>l@ 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 E2O(~oYXATM literal 54417 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d80b69a76..2e6e5897b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d517..4f906e0c81 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ 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="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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 @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +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" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d6a2..ac1b06f938 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @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 DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From 330f7a1db338e26825a50447b88f943956b384e3 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 1 May 2022 19:40:02 +0200 Subject: [PATCH 002/159] Upgrade to Minecraft 1.16.5 mod format --- build.gradle | 13 +++------ src/main/resources/META-INF/mods.toml | 28 +++++++++++++++++++ src/main/resources/{ => META-INF}/oc_at.cfg | 0 src/main/resources/mcmod.info | 17 ----------- src/main/resources/pack.mcmeta | 6 ++++ src/main/resources/pack.png | Bin 0 -> 1456 bytes src/main/scala/li/cil/oc/OpenComputers.scala | 6 +--- 7 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 src/main/resources/META-INF/mods.toml rename src/main/resources/{ => META-INF}/oc_at.cfg (100%) delete mode 100644 src/main/resources/mcmod.info create mode 100644 src/main/resources/pack.mcmeta create mode 100644 src/main/resources/pack.png diff --git a/build.gradle b/build.gradle index 63bb4fdc9b..14af1039cf 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ version = "MC${config.minecraft.version}-${project.version}" minecraft { mappings channel: "official", version: config.minecraft.version - accessTransformer = file("src/main/resources/oc-at.cfg") + accessTransformer = file("src/main/resources/META-INF/oc_at.cfg") runs { client { @@ -147,22 +147,17 @@ processResources { inputs.property "mcversion", config.minecraft.version inputs.property "fversion", config.forge.version from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' + duplicatesStrategy = "include" + include 'META-INF/mods.toml' expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'fversion':config.forge.version } from(sourceSets.main.resources.srcDirs) { + duplicatesStrategy = "include" include 'application.conf' filter { line -> line.replaceAll("@VERSION@", project.simpleVersion) } } - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - exclude 'application.conf' - } - - // Move access transformer to META-INF - rename '(.+_at.cfg)', 'META-INF/$1' } allprojects { diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000000..6a394bc339 --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,28 @@ +modLoader="javafml" +loaderVersion="[${fversion},)" +license="MIT, CC0 (see LICENSE)" +issueTrackerURL="https://github.com/MightyPirates/OpenComputers/issues" + +[[mods]] +modId="opencomputers" +version="${version}" +displayName="OpenComputers" +displayURL="http://oc.cil.li/" +logoFile="pack.png" +credits="Inspired by a couple of other mods, most notably ComputerCraft." +authors="Sangar, Vexatos, payonel, magik6k, Lord Joda and Github Contributors" +description="This mod adds modular computers and robots that can be programmed in Lua." + +[[dependencies.opencomputers]] + modId="minecraft" + mandatory=true + versionRange="[${mcversion}]" + ordering="NONE" + side="BOTH" + +[[dependencies.opencomputers]] + modId="jei" + mandatory=false + versionRange="[7,)" + ordering="AFTER" + side="BOTH" diff --git a/src/main/resources/oc_at.cfg b/src/main/resources/META-INF/oc_at.cfg similarity index 100% rename from src/main/resources/oc_at.cfg rename to src/main/resources/META-INF/oc_at.cfg diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index 15b6e128f8..0000000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,17 +0,0 @@ -[{ - "modid": "opencomputers", - "name": "OpenComputers", - "description": "This mod adds modular computers and robots that can be programmed in Lua.", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "http://oc.cil.li/", - "authorList": ["Sangar", "Vexatos", "payonel", "magik6k", "Lord Joda", "Github Contributors"], - "credits" : "Inspired by a couple of other mods, most notably ComputerCraft.", - "logoFile" : "assets/opencomputers/textures/gui/logo.png", - "requiredMods": [], - "dependencies": [ - "JEI@[4.2.7.240,)" - ], - "dependants": [], - "useDependencyInformation": "true" -}] diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 0000000000..4a3edd6ccb --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "OpenComputers resources", + "pack_format": 6 + } +} diff --git a/src/main/resources/pack.png b/src/main/resources/pack.png new file mode 100644 index 0000000000000000000000000000000000000000..65bb8c54e49ff3e0e4344d3f309afb2682851dd7 GIT binary patch literal 1456 zcmV;h1yA~kP)21;J53sH6?`25(+uKRk9)6|@M?ACoqA2G5%}A5N9ly7iM~ zruD<6jX%2Gu7Ls&p_DS?cszElA-HA)@qG}iGc)7vIp#~>L;C%`S$Jz}ONZ{0)_Hq- zbFT02?^Qy8jR45@_IA*s!{N}3^Yim&%=6sYaaf?;Znr{+!u|X`%gxP=V?o>pJ@Gl# zeSCaul}K^E$MXuyCk_ZA06T^qh9!BY=jP^WalmLaa_;BPXJ==!1GP^Rah+LIATdERON$=;%uoIy*b_as@aCh=~d2=cl=YK#mRx0d;Q# zLS9~8%($|$;$`tH%N!qHUS9U*4HE0aLa{h63AlpUUV3gq0JA16hcTAn@#hN*3&96U z>#zlpZ$N-NAOrb_B)7M>$`?JJpPv%~f*b(091I3t%dl^(d^@M;R0r!;G_PQefIhKkmJlEz;QIR7xrWcM#Xj`d z$da*hg&0C4j;ebefy^=?plL@4JBqIBv}A8@&x}`BSKjZry9;@JeRUAVzAaUykf*07 zGp??#rYPV=K)>HNBcBIAPESvrPR-KNlGpcOf#c(2GoGKHJJ%m9e*4ejy>8FD4)lUA zx7x?iAP*#qwZ3HkfrX^TkT6c4E)A8YlhX)`+*?LYP z(@dws!Hy7o8>g%i+cteqfRP}^=Sc34`LS~b{=fLlF(E*T|IDf^Kn6z-Mgp#->W=3( zt@~L&<``UyCs8R1P!xb=rTESrBk7F~sk0RGQGPBg$x&cqW5bE>O*tS%0SN*BtE4>O z3!=d9Z=!P(J>Suy>TbXrP2kD6<}vi}R$vX$EonYWZr#W0@!Gz%l(YdOD+;KRRWjQNSosTINlJY4NyfC&}9^`xxu#q+UB|<3TSBq@^9h- z`Dby!C=iD~xJHQ-UA}Xx%Z#hCKxuhJc|m1yAf{N~u0oY36J%F=*Hf4M3Pt+8>!Bi< zptGL60mE;CfZ=B$U^q!NDAfRbzm%zQRp{Z2;08#UE3Yf*XKz6EO%RZM76LMh{VRVJ zszK!mchRiV@=>t67CJ%-(Zz#=%;pXu+|>@f5&~3%e{7OiE^bgw1W5Nb*89T-NIk)6 zb%jyIVO{PD@9gY2>!2sddgA2S5_I2kI|GaEqf{@AsIR}X$eG;x5m z0kGKa?ryCGru>FTp5F{fHFuqaBzkZ8PEy4| z9 Date: Sat, 7 May 2022 03:15:27 +0200 Subject: [PATCH 003/159] Update dependencies to MC 1.16.5 versions --- build.gradle | 90 ++++--- build.properties | 30 +-- libs/factorization-api.zip | Bin 54734 -> 0 bytes libs/ic2classic-api.zip | Bin 6685 -> 0 bytes .../charset/CapabilitiesCharset.java | 19 -- .../bcbuilder/blocks/adapter.json | 12 - .../bcbuilder/blocks/assembler.json | 12 - .../opencomputers/bcbuilder/blocks/cable.json | 3 - .../bcbuilder/blocks/capacitor.json | 3 - .../opencomputers/bcbuilder/blocks/case.json | 9 - .../opencomputers/bcbuilder/blocks/case1.json | 3 - .../opencomputers/bcbuilder/blocks/case2.json | 3 - .../opencomputers/bcbuilder/blocks/case3.json | 3 - .../bcbuilder/blocks/caseCreative.json | 3 - .../bcbuilder/blocks/charger.json | 16 -- .../bcbuilder/blocks/disassembler.json | 14 - .../bcbuilder/blocks/diskDrive.json | 7 - .../bcbuilder/blocks/geolyzer.json | 3 - .../bcbuilder/blocks/hologram.json | 7 - .../bcbuilder/blocks/hologram1.json | 3 - .../bcbuilder/blocks/hologram2.json | 3 - .../bcbuilder/blocks/keyboard.json | 8 - .../bcbuilder/blocks/microcontroller.json | 18 -- .../bcbuilder/blocks/motionSensor.json | 3 - .../bcbuilder/blocks/netSplitter.json | 6 - .../bcbuilder/blocks/powerConverter.json | 6 - .../bcbuilder/blocks/powerDistributor.json | 7 - .../bcbuilder/blocks/printer.json | 16 -- .../opencomputers/bcbuilder/blocks/raid.json | 7 - .../bcbuilder/blocks/redstone.json | 8 - .../opencomputers/bcbuilder/blocks/relay.json | 12 - .../opencomputers/bcbuilder/blocks/robot.json | 24 -- .../bcbuilder/blocks/screen.json | 14 - .../bcbuilder/blocks/screen1.json | 3 - .../bcbuilder/blocks/screen2.json | 3 - .../bcbuilder/blocks/screen3.json | 3 - .../bcbuilder/blocks/serverRack.json | 15 -- .../bcbuilder/blocks/traits/computer.json | 7 - .../bcbuilder/blocks/traits/environment.json | 8 - .../bcbuilder/blocks/traits/externalData.json | 9 - .../bcbuilder/blocks/traits/hub.json | 8 - .../bcbuilder/blocks/traits/inventory.json | 8 - .../blocks/traits/powerAcceptor.json | 9 - .../bcbuilder/blocks/traits/redstone.json | 11 - .../bcbuilder/blocks/traits/rotatable.json | 7 - .../bcbuilder/blocks/transposer.json | 3 - .../bcbuilder/blocks/waypoint.json | 7 - .../scala/li/cil/oc/common/EventHandler.scala | 14 - .../cil/oc/common/block/PowerConverter.scala | 28 -- .../li/cil/oc/common/block/Redstone.scala | 3 - .../li/cil/oc/common/item/RedstoneCard.scala | 10 - .../scala/li/cil/oc/common/item/Wrench.scala | 6 - .../oc/common/item/traits/Chargeable.scala | 8 - .../cil/oc/common/tileentity/RobotProxy.scala | 20 -- .../traits/BundledRedstoneAware.scala | 1 - .../traits/power/Factorization.scala | 95 ------- .../traits/power/Galacticraft.scala | 44 ---- .../power/IndustrialCraft2Classic.scala | 101 ------- .../traits/power/IndustrialCraft2Common.scala | 5 - .../power/IndustrialCraft2Experimental.scala | 96 ------- .../tileentity/traits/power/RotaryCraft.scala | 90 ------- .../scala/li/cil/oc/integration/Mods.scala | 23 -- .../oc/integration/charset/ModCharset.scala | 54 ---- .../integration/ec/DriverBlockInterface.scala | 36 --- .../oc/integration/ec/DriverController.scala | 38 --- .../li/cil/oc/integration/ec/ECUtil.scala | 13 - .../cil/oc/integration/ec/ModExtraCells.scala | 18 -- .../oc/integration/ec/NetworkControl.scala | 30 --- .../forestry/ConverterIAlleles.java | 41 --- .../forestry/ConverterIIndividual.java | 249 ------------------ .../forestry/ConverterItemStack.scala | 17 -- .../integration/forestry/DriverAnalyzer.scala | 35 --- .../integration/forestry/DriverBeeHouse.java | 163 ------------ .../oc/integration/forestry/ModForestry.scala | 17 -- .../ic2/ConverterElectricItem.java | 27 -- .../cil/oc/integration/ic2/DriverEnergy.java | 61 ----- .../ic2/DriverEnergyConductor.java | 50 ---- .../cil/oc/integration/ic2/DriverMassFab.java | 47 ---- .../cil/oc/integration/ic2/DriverReactor.java | 67 ----- .../integration/ic2/DriverReactorChamber.java | 87 ------ .../ic2/DriverReactorRedstonePort.java | 102 ------- .../integration/ic2/ElectricItemManager.scala | 54 ---- .../ic2/EventHandlerIndustrialCraft2.scala | 80 ------ .../integration/ic2/ModIndustrialCraft2.scala | 43 --- .../{gas => }/ConverterGasStack.scala | 2 +- .../oc/integration/mekanism/ModMekanism.scala | 1 + .../mekanism/gas/ModMekanismGas.scala | 14 - .../thaumcraft/ConverterThaumcraftItems.scala | 47 ---- .../thaumcraft/ModThaumcraft.scala | 13 - .../cil/oc/integration/wrcbe/ModWRCBE.scala | 13 - .../wrcbe/WirelessRedstoneCBE.scala | 43 --- .../server/component/RedstoneWireless.scala | 29 -- 92 files changed, 65 insertions(+), 2443 deletions(-) delete mode 100644 libs/factorization-api.zip delete mode 100644 libs/ic2classic-api.zip delete mode 100644 src/main/java/li/cil/oc/integration/charset/CapabilitiesCharset.java delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/adapter.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/assembler.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/cable.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/capacitor.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/case.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/case1.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/case2.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/case3.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/caseCreative.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/charger.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/disassembler.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/diskDrive.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/geolyzer.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram1.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram2.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/keyboard.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/microcontroller.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/motionSensor.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/netSplitter.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/powerConverter.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/powerDistributor.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/printer.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/raid.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/redstone.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/relay.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/robot.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/screen.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/screen1.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/screen2.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/screen3.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/serverRack.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/computer.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/environment.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/externalData.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/hub.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/inventory.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/powerAcceptor.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/redstone.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/rotatable.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/transposer.json delete mode 100644 src/main/resources/assets/opencomputers/bcbuilder/blocks/waypoint.json delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/Factorization.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/Galacticraft.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Classic.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Common.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Experimental.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/RotaryCraft.scala delete mode 100644 src/main/scala/li/cil/oc/integration/charset/ModCharset.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ec/DriverBlockInterface.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ec/DriverController.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ec/ECUtil.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ec/ModExtraCells.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ec/NetworkControl.scala delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/ConverterIAlleles.java delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/ConverterIIndividual.java delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/ConverterItemStack.scala delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/DriverAnalyzer.scala delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/DriverBeeHouse.java delete mode 100644 src/main/scala/li/cil/oc/integration/forestry/ModForestry.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/ConverterElectricItem.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverEnergy.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverEnergyConductor.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverMassFab.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverReactor.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverReactorChamber.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/DriverReactorRedstonePort.java delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/ElectricItemManager.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala delete mode 100644 src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala rename src/main/scala/li/cil/oc/integration/mekanism/{gas => }/ConverterGasStack.scala (93%) delete mode 100644 src/main/scala/li/cil/oc/integration/mekanism/gas/ModMekanismGas.scala delete mode 100644 src/main/scala/li/cil/oc/integration/thaumcraft/ConverterThaumcraftItems.scala delete mode 100644 src/main/scala/li/cil/oc/integration/thaumcraft/ModThaumcraft.scala delete mode 100644 src/main/scala/li/cil/oc/integration/wrcbe/ModWRCBE.scala delete mode 100644 src/main/scala/li/cil/oc/integration/wrcbe/WirelessRedstoneCBE.scala diff --git a/build.gradle b/build.gradle index 14af1039cf..9bd1c9b4fc 100644 --- a/build.gradle +++ b/build.gradle @@ -71,8 +71,44 @@ minecraft { } repositories { - maven { url "https://maven.cil.li/" } - maven { url "https://minecraft.curseforge.com/api/maven/" } + maven { + url "https://maven.cil.li/" + content { + includeGroup "li.cil.tis3d" + } + } + maven { + url "https://dvs1.progwml6.com/files/maven" + content { + includeGroup "mezz.jei" + } + } + maven { + url "https://maven.tehnut.info/" + content { + includeGroup "mcp.mobius.waila" + } + } + maven { + url "https://chickenbones.net/maven/" + content { + includeGroup "codechicken" + includeGroup "mrtjp" + } + } + maven { + url "https://squiddev.cc/maven/" + content { + includeGroup "org.squiddev" + } + } + maven { + url "https://modmaven.dev/" + content { + includeGroup "appeng" + includeGroup "mekanism" + } + } } configurations { @@ -81,65 +117,43 @@ configurations { } dependencies { + var jeiSlug = "jei-${config.minecraft.version}" + minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" + implementation "com.typesafe:config:1.2.1" + implementation "org.scala-lang:scala-library:2.11.12" - compileOnly fg.deobf("li.cil.tis3d:TIS-3D:MC1.12-${config.tis3d.version}") { - exclude module: "jei_1.12" - } - compileOnly fg.deobf("com.mod-buildcraft:buildcraft-api:${config.buildcraft.version}") - compileOnly fg.deobf("MCMultiPart2:MCMultiPart-exp:${config.mcmp.version}") - compileOnly ("net.sengir.forestry:forestry_1.12.2:${config.forestry.version}") { - exclude module: "jei_1.12" - } - compileOnly fg.deobf("net.industrial-craft:industrialcraft-2:${config.ic2.version}") + compileOnly fg.deobf("li.cil.tis3d:tis3d-1.16.5-forge:${config.tis3d.version}") compileOnly fg.deobf("mcp.mobius.waila:Hwyla:${config.hwyla.version}:api") - compileOnly fg.deobf("dan200.computercraft:ComputerCraft:${config.cc.version}") - compileOnly fg.deobf("charset:Charset:${config.charset.version}:api") + compileOnly fg.deobf("org.squiddev:cc-tweaked-${config.minecraft.version}:${config.cct.version}") compileOnly ("appeng:appliedenergistics2:${config.ae2.version}:api") { transitive = false } - compileOnly "extracells2:ExtraCells-api:${config.extracells.version}" compileOnly("mekanism:Mekanism:${config.mekanism.version}:api") { transitive = false } - compileOnly ("codechicken:ForgeMultipart:${config.minecraft.version}-${config.forgemultipart.version}:universal") { - exclude module: "jei_1.12" + compileOnly ("codechicken:CBMultipart:${config.cbmultipart.version}:universal") { + exclude module: jeiSlug exclude module: "CodeChickenLib" } compileOnly ("codechicken:ChickenASM:${config.casm.version}") - compileOnly "mezz.jei:jei_${config.minecraft.version}:${config.jei.version}" - compileOnly "codechicken:CodeChickenLib:${config.minecraft.version}-${config.ccl.version}:universal" - compileOnly "codechicken:WR-CBE:${config.minecraft.version}-${config.wrcbe.version}:universal" - - compileOnly ("mrtjp:ProjectRed:${config.projred.version}:Base") { - exclude module: "NotEnoughItems" - exclude module: "CodeChickenLib" - exclude module: "jei_1.12" - exclude module: "ForgeMultipart" - } - - compileOnly ("mrtjp:ProjectRed:${config.projred.version}:integration") { - exclude module: "NotEnoughItems" - exclude module: "CodeChickenLib" - exclude module: "jei_1.12" - exclude module: "ForgeMultipart" - } + compileOnly "mezz.jei:${jeiSlug}:${config.jei.version}" + compileOnly "codechicken:CodeChickenLib:${config.ccl.version}:universal" - compileOnly ("mrtjp:MrTJPCore:${config.mrtjpcore.version}:universal") { - exclude module: "NotEnoughItems" + compileOnly ("mrtjp:ProjectRed:${config.projred.version}:core") { exclude module: "CodeChickenLib" - exclude module: "jei_1.12" - exclude module: "ForgeMultipart" + exclude module: jeiSlug + exclude module: "CBMultipart" } embedded files('libs/OpenComputers-JNLua.jar', 'libs/OpenComputers-LuaJ.jar') - compileOnly "codechicken:EnderStorage:${config.minecraft.version}-${config.enderstorage.version}:universal" + compileOnly "codechicken:EnderStorage:${config.enderstorage.version}:universal" } processResources { diff --git a/build.properties b/build.properties index 252b197161..38a034c3e0 100644 --- a/build.properties +++ b/build.properties @@ -5,25 +5,17 @@ mod.name=OpenComputers mod.group=li.cil.oc mod.version=1.7.5 -ae2.version=rv6-stable-5 -buildcraft.version=7.99.17 -cc.version=1.80pr1-build0 -charset.version=0.5.6.2 -extracells.version=1.12.2:2.5.3a25 -forestry.version=5.8.0.311 -hwyla.version=1.8.26-B41_1.12.2 -ic2.version=2.8.91-ex112 -jei.version=4.15.0.268 -mcmp.version=2.5.1_66 -mekanism.version=1.12.2-9.4.3.330 -tis3d.version=1.3.1.18 -projred.version=1.12.2-4.9.3.118 -mrtjpcore.version=1.12.2-2.1.4.43 -ccl.version=3.2.3.358 -forgemultipart.version=2.6.2.83 -casm.version=1.12-1.0.2.7 -enderstorage.version=2.4.5.135 -wrcbe.version=2.3.2.33 +ae2.version=8.4.7 +cct.version=1.100.5 +hwyla.version=1.10.11-B78_1.16.2 +jei.version=7.7.1.152 +mekanism.version=1.16.5-10.1.2.457 +tis3d.version=1.6.8+30 +projred.version=1.16.5-4.12.0-beta-0 +ccl.version=1.16.5-4.0.7.445 +cbmultipart.version=1.16.5-3.0.4.123 +casm.version=2.0.1.14 +enderstorage.version=1.16.5-2.8.0.170 curse.project.id=223008 curse.project.releaseType=release diff --git a/libs/factorization-api.zip b/libs/factorization-api.zip deleted file mode 100644 index 44566c78ce0dbc57595793814a15b713c9e34965..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54734 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

u|teKN)920HNJ+dCAs~{4| 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 BUNDLED_EMITTER; - @CapabilityInject(IBundledReceiver.class) - public static Capability BUNDLED_RECEIVER; - @CapabilityInject(IRedstoneEmitter.class) - public static Capability REDSTONE_EMITTER; - @CapabilityInject(IRedstoneReceiver.class) - public static Capability REDSTONE_RECEIVER; -} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/adapter.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/adapter.json deleted file mode 100644 index 039cc63217..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/adapter.json +++ /dev/null @@ -1,12 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/powerAcceptor.json" - ], - "nbt": { - "blacklist": [ - "oc:adapter.blocks" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/assembler.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/assembler.json deleted file mode 100644 index 9db233b831..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/assembler.json +++ /dev/null @@ -1,12 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/powerAcceptor.json" - ], - "nbt": { - "blacklist": [ - "oc:remaining" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/cable.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/cable.json deleted file mode 100644 index 2a5da92da9..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/cable.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["traits/environment.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/capacitor.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/capacitor.json deleted file mode 100644 index 2a5da92da9..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/capacitor.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["traits/environment.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/case.json deleted file mode 100644 index d0a7fe6c55..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case.json +++ /dev/null @@ -1,9 +0,0 @@ -[{ - "includes": [ - "traits/computer.json", - "traits/inventory.json", - "traits/powerAcceptor.json", - "traits/redstone.json", - "traits/rotatable.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case1.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/case1.json deleted file mode 100644 index 5236537c8f..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case1.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["case.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case2.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/case2.json deleted file mode 100644 index 5236537c8f..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case2.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["case.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case3.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/case3.json deleted file mode 100644 index 5236537c8f..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/case3.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["case.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/caseCreative.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/caseCreative.json deleted file mode 100644 index 5236537c8f..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/caseCreative.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["case.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/charger.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/charger.json deleted file mode 100644 index 5c05e72697..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/charger.json +++ /dev/null @@ -1,16 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/powerAcceptor.json", - "traits/redstone.json", - "traits/rotatable.json" - ], - "nbt": { - "blacklist": [ - "chargeSpeed", - "hasPower", - "invertSignal" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/disassembler.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/disassembler.json deleted file mode 100644 index 9ab51a0fc5..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/disassembler.json +++ /dev/null @@ -1,14 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/powerAcceptor.json" - ], - "nbt": { - "blacklist": [ - "oc:buffer", - "oc:queue", - "oc:total" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/diskDrive.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/diskDrive.json deleted file mode 100644 index 7a52c2ef3b..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/diskDrive.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/rotatable.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/geolyzer.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/geolyzer.json deleted file mode 100644 index 2a5da92da9..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/geolyzer.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["traits/environment.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram.json deleted file mode 100644 index 5e40db6c34..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/externalData.json", - "traits/rotatable.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram1.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram1.json deleted file mode 100644 index e7bedbd909..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram1.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["hologram.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram2.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram2.json deleted file mode 100644 index e7bedbd909..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/hologram2.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["hologram.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/keyboard.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/keyboard.json deleted file mode 100644 index a1a9760b28..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/keyboard.json +++ /dev/null @@ -1,8 +0,0 @@ -[{ - "includes": ["traits/rotatable.json"], - "nbt": { - "blacklist": [ - "oc:keyboard" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/microcontroller.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/microcontroller.json deleted file mode 100644 index de8fbc3846..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/microcontroller.json +++ /dev/null @@ -1,18 +0,0 @@ -[{ - "includes": [ - "traits/computer.json", - "traits/hub.json", - "traits/inventory.json", - "traits/powerAcceptor.json", - "traits/redstone.json", - "traits/rotatable.json" - ], - "nbt": { - "blacklist": [ - "oc:info", - "oc:snooper", - "oc:componentNodes", - "oc:outputs" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/motionSensor.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/motionSensor.json deleted file mode 100644 index 2a5da92da9..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/motionSensor.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["traits/environment.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/netSplitter.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/netSplitter.json deleted file mode 100644 index 5c133025fa..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/netSplitter.json +++ /dev/null @@ -1,6 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/redstone.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerConverter.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerConverter.json deleted file mode 100644 index 4b10cadc24..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerConverter.json +++ /dev/null @@ -1,6 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/powerAcceptor.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerDistributor.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerDistributor.json deleted file mode 100644 index 0ce5d96fcc..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/powerDistributor.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "oc:connector" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/printer.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/printer.json deleted file mode 100644 index 7c1dca4816..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/printer.json +++ /dev/null @@ -1,16 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json" - ], - "nbt": { - "blacklist": [ - "oc:active", - "oc:amountInk", - "oc:amountMaterial", - "oc:limit", - "oc:remaining", - "oc:total" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/raid.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/raid.json deleted file mode 100644 index 7a52c2ef3b..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/raid.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/inventory.json", - "traits/rotatable.json" - ] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/redstone.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/redstone.json deleted file mode 100644 index f2be27863f..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/redstone.json +++ /dev/null @@ -1,8 +0,0 @@ -[{ - "includes": ["traits/redstone.json"], - "nbt": { - "blacklist": [ - "oc:redstone/node" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/relay.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/relay.json deleted file mode 100644 index 8853509610..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/relay.json +++ /dev/null @@ -1,12 +0,0 @@ -[{ - "includes": [ - "traits/hub.json", - "traits/inventory.json", - "traits/powerAcceptor.json" - ], - "nbt": { - "blacklist": [ - "oc:componentNodes" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/robot.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/robot.json deleted file mode 100644 index 53641eb9c1..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/robot.json +++ /dev/null @@ -1,24 +0,0 @@ -[{ - "includes": [ - "traits/computer.json", - "traits/environment.json", - "traits/inventory.json", - "traits/redstone.json", - "traits/rotatable.json" - ], - "nbt": { - "blacklist": [ - "display", - "oc:components", - "oc:containers", - "oc:lightColor", - "oc:owner", - "oc:ownerUuid", - "oc:robot", - "oc:robotEnergy", - "oc:selectedSlot", - "oc:selectedTank", - "oc:storedEnergy" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen.json deleted file mode 100644 index 369d342734..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen.json +++ /dev/null @@ -1,14 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/externalData.json", - "traits/redstone.json", - "traits/rotatable.json" - ], - "nbt": { - "blacklist": [ - "oc:hasPower", - "oc:hadRedstoneInput" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen1.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen1.json deleted file mode 100644 index dadd2bf74b..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen1.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["screen.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen2.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen2.json deleted file mode 100644 index dadd2bf74b..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen2.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["screen.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen3.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen3.json deleted file mode 100644 index dadd2bf74b..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/screen3.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["screen.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/serverRack.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/serverRack.json deleted file mode 100644 index 06dc5c6697..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/serverRack.json +++ /dev/null @@ -1,15 +0,0 @@ -[{ - "includes": [ - "traits/hub.json", - "traits/inventory.json", - "traits/powerAcceptor.json", - "traits/redstone.json", - "traits/rotatable.json" - ], - "nbt": { - "blacklist": [ - "oc:servers", - "oc:terminals" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/computer.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/computer.json deleted file mode 100644 index d90950be10..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/computer.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "oc:computer" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/environment.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/environment.json deleted file mode 100644 index b91cf6986e..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/environment.json +++ /dev/null @@ -1,8 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "node", - "oc:node" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/externalData.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/externalData.json deleted file mode 100644 index c579dec594..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/externalData.json +++ /dev/null @@ -1,9 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "chunkX", - "chunkZ", - "dimension" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/hub.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/hub.json deleted file mode 100644 index 6994b8091a..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/hub.json +++ /dev/null @@ -1,8 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "oc:plugs", - "oc:queue" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/inventory.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/inventory.json deleted file mode 100644 index f16d956de6..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/inventory.json +++ /dev/null @@ -1,8 +0,0 @@ -[{ - "ignoreInventoryContents": true, - "nbt": { - "blacklist": [ - "oc:items" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/powerAcceptor.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/powerAcceptor.json deleted file mode 100644 index 7500c660bc..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/powerAcceptor.json +++ /dev/null @@ -1,9 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "oc:ae2power", - "oc:ic2power", - "oc:ic2cpower" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/redstone.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/redstone.json deleted file mode 100644 index 6b28180698..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/redstone.json +++ /dev/null @@ -1,11 +0,0 @@ -[{ - "nbt": { - "blacklist": [ - "oc:rs.input", - "oc:rs.output", - "oc:rs.bundledInput", - "oc:rs.bundledOutput", - "oc:rs.rednetInput" - ] - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/rotatable.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/rotatable.json deleted file mode 100644 index 2168077cf8..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/traits/rotatable.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "rotation": { - "type": "nbtField", - "tag": "oc:yaw", - "format": "ForgeDirection" - } -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/transposer.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/transposer.json deleted file mode 100644 index 2a5da92da9..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/transposer.json +++ /dev/null @@ -1,3 +0,0 @@ -[{ - "includes": ["traits/environment.json"] -}] \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/bcbuilder/blocks/waypoint.json b/src/main/resources/assets/opencomputers/bcbuilder/blocks/waypoint.json deleted file mode 100644 index 556f25dc77..0000000000 --- a/src/main/resources/assets/opencomputers/bcbuilder/blocks/waypoint.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "includes": [ - "traits/environment.json", - "traits/redstone.json", - "traits/rotatable.json" - ] -}] \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 6a4faddeb3..1775b799e2 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -113,20 +113,6 @@ object EventHandler { } } - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def scheduleIC2Add(tileEntity: power.IndustrialCraft2Experimental) { - if (SideTracker.isServer) pendingServer.synchronized { - tileEntity match { - case tile: ic2.api.energy.tile.IEnergyTile => - pendingServer += (() => if (!tileEntity.addedToIC2PowerGrid && !tileEntity.isInvalid) { - MinecraftForge.EVENT_BUS.post(new ic2.api.energy.event.EnergyTileLoadEvent(tile)) - tileEntity.addedToIC2PowerGrid = true - }) - case _ => - } - } - } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) def scheduleAE2Add(tileEntity: power.AppliedEnergistics2): Unit = { if (SideTracker.isServer) pendingServer.synchronized { diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index 75eee1da08..df3a58674e 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -18,34 +18,6 @@ class PowerConverter extends SimpleBlock with traits.PowerAcceptor { ItemBlacklist.hide(this) } - private val formatter = new DecimalFormat("#.#") - - // ----------------------------------------------------------------------- // - - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, advanced) -// TODO more generic way of integration modules of power providing mods to provide tooltip lines -// if (Mods.Factorization.isAvailable) { -// addRatio(tooltip, "Factorization", Settings.get.ratioFactorization) -// } - if (Mods.IndustrialCraft2.isModAvailable) { - addRatio(tooltip, "IndustrialCraft2", Settings.get.ratioIndustrialCraft2) - } - } - - private def addExtension(x: Double) = - if (x >= 1e9) formatter.format(x / 1e9) + "G" - else if (x >= 1e6) formatter.format(x / 1e6) + "M" - else if (x >= 1e3) formatter.format(x / 1e3) + "K" - else formatter.format(x) - - private def addRatio(tooltip: util.List[String], name: String, ratio: Double) { - val (a, b) = - if (ratio > 1) (1.0, ratio) - else (1.0 / ratio, 1.0) - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase + "." + name, addExtension(a), addExtension(b))) - } - // ----------------------------------------------------------------------- // override def energyThroughput: Double = Settings.get.powerConverterRate diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index 3e4288b422..8e3fef5e3d 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -17,9 +17,6 @@ class Redstone extends RedstoneAware { if (Mods.ProjectRedTransmission.isModAvailable) { tooltip.addAll(Tooltip.get("RedstoneCard.ProjectRed")) } - if (Mods.Charset.isModAvailable) { - tooltip.addAll(Tooltip.get("RedstoneCard.Charset")) - } } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala index db8c0c34e6..a53a6fbf3e 100644 --- a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala +++ b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala @@ -12,14 +12,4 @@ class RedstoneCard(val parent: Delegator, val tier: Int) extends traits.Delegate // Note: T2 is enabled in mod integration, if it makes sense. showInItemList = tier == Tier.One - - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) { - super.tooltipExtended(stack, tooltip) - if (tier == Tier.Two) { - // TODO Generic system for redstone integration modules to register in a list of tooltip lines. - // if (Mods.MOD_NAME.isAvailable) { - // tooltip.addAll(Tooltip.get(super.unlocalizedName + ".MOD_NAME")) - // } - } - } } diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index d2741a32e8..3a05dbcc1c 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -14,9 +14,6 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockAccess import net.minecraft.world.World -@Injectable.InterfaceList(Array( - new Injectable.Interface(value = "ic2.api.item.IBoxable", modid = Mods.IDs.IndustrialCraft2) -)) class Wrench extends traits.SimpleItem with api.internal.Wrench { setHarvestLevel("wrench", 1) setMaxStackSize(1) @@ -39,7 +36,4 @@ class Wrench extends traits.SimpleItem with api.internal.Wrench { if (!simulate) player.swingArm(EnumHand.MAIN_HAND) true } - - // IndustrialCraft 2 - def canBeStoredInToolbox(stack: ItemStack): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala b/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala index c32f1c5aa1..0b92efcfe4 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala @@ -1,10 +1,8 @@ package li.cil.oc.common.item.traits -import ic2.api.item.IElectricItemManager import li.cil.oc.{Settings, api} import li.cil.oc.common.asm.Injectable import li.cil.oc.integration.Mods -import li.cil.oc.integration.ic2.ElectricItemManager import li.cil.oc.integration.opencomputers.ModOpenComputers import net.minecraft.util.{EnumFacing, ResourceLocation} import net.minecraftforge.fml.common.Optional @@ -13,9 +11,6 @@ import net.minecraftforge.common.capabilities.{Capability, ICapabilityProvider} import net.minecraftforge.energy.{CapabilityEnergy, IEnergyStorage} // TODO Forge power capabilities. -@Injectable.InterfaceList(Array( - new Injectable.Interface(value = "ic2.api.item.ISpecialElectricItem", modid = Mods.IDs.IndustrialCraft2) -)) trait Chargeable extends api.driver.item.Chargeable { def maxCharge(stack: ItemStack): Double @@ -25,9 +20,6 @@ trait Chargeable extends api.driver.item.Chargeable { def setCharge(stack: ItemStack, amount: Double): Unit def canExtract(stack: ItemStack): Boolean = false - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def getManager(stack: ItemStack): IElectricItemManager = ElectricItemManager } object Chargeable { diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 3b41bd9c3b..815659d344 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -223,26 +223,6 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo override def checkRedstoneInputChanged(): Unit = robot.checkRedstoneInputChanged() - /* TORO RedLogic - @Optional.Method(modid = Mods.IDs.RedLogic) - override def connects(wire: IWire, blockFace: Int, fromDirection: Int) = robot.connects(wire, blockFace, fromDirection) - - @Optional.Method(modid = Mods.IDs.RedLogic) - override def connectsAroundCorner(wire: IWire, blockFace: Int, fromDirection: Int) = robot.connectsAroundCorner(wire, blockFace, fromDirection) - - @Optional.Method(modid = Mods.IDs.RedLogic) - override def getBundledCableStrength(blockFace: Int, toDirection: Int) = robot.getBundledCableStrength(blockFace, toDirection) - - @Optional.Method(modid = Mods.IDs.RedLogic) - override def getEmittedSignalStrength(blockFace: Int, toDirection: Int) = robot.getEmittedSignalStrength(blockFace, toDirection) - - @Optional.Method(modid = Mods.IDs.RedLogic) - override def onBundledInputChanged() = robot.onBundledInputChanged() - - @Optional.Method(modid = Mods.IDs.RedLogic) - override def onRedstoneInputChanged() = robot.onRedstoneInputChanged() - */ - // ----------------------------------------------------------------------- // override def pitch: EnumFacing = robot.pitch diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index ae3e6c75a9..904beb9ec2 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -12,7 +12,6 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.common.Optional import java.util -import li.cil.oc.integration.charset.{CapabilitiesCharset, ModCharset} import net.minecraftforge.common.capabilities.Capability trait BundledRedstoneAware extends RedstoneAware with IBundledTile { diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Factorization.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Factorization.scala deleted file mode 100644 index 2d2371c4f1..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Factorization.scala +++ /dev/null @@ -1,95 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power -/* TODO Factorization - -import cpw.mods.fml.common.Optional -import factorization.api.Charge -import factorization.api.Coord -import factorization.api.IChargeConductor -import li.cil.oc.OpenComputers -import li.cil.oc.Settings -import li.cil.oc.common.asm.Injectable -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import net.minecraft.nbt.NBTTagCompound - -@Injectable.Interface(value = "factorization.api.IChargeConductor", modid = Mods.IDs.Factorization) -trait Factorization extends Common { - private lazy val useFactorizationPower = isServer && Mods.Factorization.isAvailable - - @Optional.Method(modid = Mods.IDs.Factorization) - private lazy val charge: AnyRef = this match { - case conductor: IChargeConductor => new Charge(conductor) - case _ => - OpenComputers.log.warn("Failed setting up Factorization power, which most likely means the class transformer did not run. You're probably running in an incorrectly configured development environment. Try adding `-Dfml.coreMods.load=li.cil.oc.common.launch.TransformerLoader` to the VM options of your run configuration.") - null - } - - // ----------------------------------------------------------------------- // - - override def updateEntity() { - if (useFactorizationPower) updateEnergy() - super.updateEntity() - } - - @Optional.Method(modid = Mods.IDs.Factorization) - private def updateEnergy() { - getCharge.update() - if (world.getTotalWorldTime % Settings.get.tickFrequency == 0) { - tryAllSides((demand, _) => getCharge.deplete(demand.toInt), Power.fromCharge, Power.toCharge) - } - } - - override def invalidate() { - if (useFactorizationPower) invalidateCharge() - super.invalidate() - } - - @Optional.Method(modid = Mods.IDs.Factorization) - private def invalidateCharge() { - getCharge.invalidate() - } - - override def onChunkUnload() { - if (useFactorizationPower) removeCharge() - super.onChunkUnload() - } - - @Optional.Method(modid = Mods.IDs.Factorization) - private def removeCharge() { - if (!isInvalid) getCharge.remove() - } - - // ----------------------------------------------------------------------- // - - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (useFactorizationPower) loadCharge(nbt) - } - - @Optional.Method(modid = Mods.IDs.Factorization) - private def loadCharge(nbt: NBTTagCompound) { - getCharge.readFromNBT(nbt, "fzpower") - } - - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - if (useFactorizationPower) saveCharge(nbt) - } - - @Optional.Method(modid = Mods.IDs.Factorization) - private def saveCharge(nbt: NBTTagCompound) { - getCharge.writeToNBT(nbt, "fzpower") - } - - // ----------------------------------------------------------------------- // - - @Optional.Method(modid = Mods.IDs.Factorization) - def getCharge = if (Mods.Factorization.isAvailable) charge.asInstanceOf[Charge] else null - - @Optional.Method(modid = Mods.IDs.Factorization) - def getInfo = "" - - @Optional.Method(modid = Mods.IDs.Factorization) - def getCoord = new Coord(this) -} -*/ \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Galacticraft.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Galacticraft.scala deleted file mode 100644 index 449c1be224..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Galacticraft.scala +++ /dev/null @@ -1,44 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power -/* TODO Galacticraft - -import cpw.mods.fml.common.Optional -import li.cil.oc.common.asm.Injectable -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import micdoodle8.mods.galacticraft.api.power.EnergySource -import micdoodle8.mods.galacticraft.api.transmission.NetworkType -import net.minecraftforge.common.util.ForgeDirection - -import scala.language.implicitConversions - -@Injectable.InterfaceList(Array( - new Injectable.Interface(value = "micdoodle8.mods.galacticraft.api.power.IEnergyHandlerGC", modid = Mods.IDs.Galacticraft), - new Injectable.Interface(value = "micdoodle8.mods.galacticraft.api.transmission.tile.IConnector", modid = Mods.IDs.Galacticraft) -)) -trait Galacticraft extends Common { - private implicit def toDirection(source: EnergySource): ForgeDirection = source match { - case adjacent: EnergySource.EnergySourceAdjacent => adjacent.direction - case _ => ForgeDirection.UNKNOWN - } - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def nodeAvailable(from: EnergySource) = Mods.Galacticraft.isAvailable && canConnectPower(from) - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def receiveEnergyGC(from: EnergySource, amount: Float, simulate: Boolean) = - if (!Mods.Galacticraft.isAvailable) 0f - else Power.toGC(tryChangeBuffer(from, Power.fromGC(amount), !simulate)) - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def getEnergyStoredGC(from: EnergySource) = Power.toGC(globalBuffer(from)) - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def getMaxEnergyStoredGC(from: EnergySource) = Power.toGC(globalBufferSize(from)) - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def extractEnergyGC(from: EnergySource, amount: Float, simulate: Boolean) = 0f - - @Optional.Method(modid = Mods.IDs.Galacticraft) - def canConnect(from: ForgeDirection, networkType: NetworkType): Boolean = networkType == NetworkType.POWER && canConnectPower(from) -} -*/ \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Classic.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Classic.scala deleted file mode 100644 index 52d32871ac..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Classic.scala +++ /dev/null @@ -1,101 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power -/* TODO IC2 Classic - -import cpw.mods.fml.common.Optional -import cpw.mods.fml.common.eventhandler.Event -import ic2classic.api.Direction -import li.cil.oc.OpenComputers -import li.cil.oc.Settings -import li.cil.oc.common.EventHandler -import li.cil.oc.common.asm.Injectable -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntity -import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.common.util.ForgeDirection - -@Injectable.Interface(value = "ic2classic.api.energy.tile.IEnergySink", modid = Mods.IDs.IndustrialCraft2Classic) -trait IndustrialCraft2Classic extends Common with IndustrialCraft2Common { - private var conversionBuffer = 0.0 - - private lazy val useIndustrialCraft2ClassicPower = isServer && Mods.IndustrialCraft2Classic.isAvailable - - // ----------------------------------------------------------------------- // - - override def updateEntity() { - super.updateEntity() - if (useIndustrialCraft2ClassicPower && world.getTotalWorldTime % Settings.get.tickFrequency == 0) { - updateEnergy() - } - } - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - private def updateEnergy() { - tryAllSides((demand, _) => { - val result = math.min(demand, conversionBuffer) - conversionBuffer -= result - result - }, Power.fromEU, Power.toEU) - } - - override def validate() { - super.validate() - if (useIndustrialCraft2ClassicPower && !addedToIC2PowerGrid) EventHandler.scheduleIC2Add(this) - } - - override def invalidate() { - super.invalidate() - if (useIndustrialCraft2ClassicPower && addedToIC2PowerGrid) removeFromIC2Grid() - } - - override def onChunkUnload() { - super.onChunkUnload() - if (useIndustrialCraft2ClassicPower && addedToIC2PowerGrid) removeFromIC2Grid() - } - - private def removeFromIC2Grid() { - try MinecraftForge.EVENT_BUS.post(Class.forName("ic2classic.api.energy.event.EnergyTileUnloadEvent").getConstructor(Class.forName("ic2classic.api.energy.tile.IEnergyTile")).newInstance(this).asInstanceOf[Event]) catch { - case t: Throwable => OpenComputers.log.warn("Error removing node from IC2 grid.", t) - } - addedToIC2PowerGrid = false - } - - // ----------------------------------------------------------------------- // - - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - conversionBuffer = nbt.getDouble(Settings.namespace + "ic2cpower") - } - - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setDouble(Settings.namespace + "ic2cpower", conversionBuffer) - } - - // ----------------------------------------------------------------------- // - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - def isAddedToEnergyNet: Boolean = addedToIC2PowerGrid - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - def getMaxSafeInput: Int = Int.MaxValue - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - def acceptsEnergyFrom(emitter: TileEntity, direction: Direction) = useIndustrialCraft2ClassicPower && canConnectPower(direction.toForgeDirection) - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - def injectEnergy(directionFrom: Direction, amount: Int): Boolean = { - conversionBuffer += amount - true - } - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2Classic) - def demandsEnergy: Int = { - if (!useIndustrialCraft2ClassicPower) 0 - else if (conversionBuffer < energyThroughput * Settings.get.tickFrequency) - math.min(ForgeDirection.VALID_DIRECTIONS.map(globalDemand).max, Power.toEU(energyThroughput)).toInt - else 0 - } -} -*/ \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Common.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Common.scala deleted file mode 100644 index 368b2ff19b..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Common.scala +++ /dev/null @@ -1,5 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power - -trait IndustrialCraft2Common { - var addedToIC2PowerGrid = false -} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Experimental.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Experimental.scala deleted file mode 100644 index b1b7eecea8..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/IndustrialCraft2Experimental.scala +++ /dev/null @@ -1,96 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power - -import ic2.api.energy.tile.IEnergyEmitter -import li.cil.oc.OpenComputers -import li.cil.oc.Settings -import li.cil.oc.common.EventHandler -import li.cil.oc.common.asm.Injectable -import li.cil.oc.common.tileentity.traits -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.Optional -import net.minecraftforge.fml.common.eventhandler.Event - -@Injectable.Interface(value = "ic2.api.energy.tile.IEnergySink", modid = Mods.IDs.IndustrialCraft2) -trait IndustrialCraft2Experimental extends Common with IndustrialCraft2Common with traits.Tickable { - private var conversionBuffer = 0.0 - - private lazy val useIndustrialCraft2Power = isServer && Mods.IndustrialCraft2.isModAvailable - - // ----------------------------------------------------------------------- // - - override def updateEntity() { - super.updateEntity() - if (useIndustrialCraft2Power && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { - updateEnergy() - } - } - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - private def updateEnergy() { - tryAllSides((demand, _) => { - val result = math.min(demand, conversionBuffer) - conversionBuffer -= result - result - }, Power.fromEU, Power.toEU) - } - - override def validate() { - super.validate() - if (useIndustrialCraft2Power && !addedToIC2PowerGrid) EventHandler.scheduleIC2Add(this) - } - - override def invalidate() { - super.invalidate() - if (useIndustrialCraft2Power && addedToIC2PowerGrid) removeFromIC2Grid() - } - - override def onChunkUnload() { - super.onChunkUnload() - if (useIndustrialCraft2Power && addedToIC2PowerGrid) removeFromIC2Grid() - } - - private def removeFromIC2Grid() { - try MinecraftForge.EVENT_BUS.post(Class.forName("ic2.api.energy.event.EnergyTileUnloadEvent").getConstructor(Class.forName("ic2.api.energy.tile.IEnergyTile")).newInstance(this).asInstanceOf[Event]) catch { - case t: Throwable => OpenComputers.log.warn("Error removing node from IC2 grid.", t) - } - addedToIC2PowerGrid = false - } - - // ----------------------------------------------------------------------- // - - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - conversionBuffer = nbt.getDouble(Settings.namespace + "ic2power") - } - - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setDouble(Settings.namespace + "ic2power", conversionBuffer) - } - - // ----------------------------------------------------------------------- // - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def getSinkTier: Int = Int.MaxValue - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def acceptsEnergyFrom(emitter: IEnergyEmitter, direction: EnumFacing): Boolean = useIndustrialCraft2Power && canConnectPower(direction) - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def injectEnergy(directionFrom: EnumFacing, amount: Double, voltage: Double): Double = { - conversionBuffer += amount - 0.0 - } - - @Optional.Method(modid = Mods.IDs.IndustrialCraft2) - def getDemandedEnergy: Double = { - if (!useIndustrialCraft2Power) 0.0 - else if (conversionBuffer < energyThroughput * Settings.get.tickFrequency) - math.min(EnumFacing.VALUES.map(globalDemand).max, Power.toEU(energyThroughput)) - else 0 - } -} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/RotaryCraft.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/RotaryCraft.scala deleted file mode 100644 index 576757427d..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/RotaryCraft.scala +++ /dev/null @@ -1,90 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power -/* TODO RotaryCraft -import li.cil.oc.OpenComputers -import li.cil.oc.Settings -import li.cil.oc.common.asm.Injectable -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.common.Optional - -@Injectable.Interface(value = "Reika.RotaryCraft.API.Power.ShaftPowerReceiver", modid = Mods.IDs.RotaryCraft) -trait RotaryCraft extends Common { - private lazy val useRotaryCraftPower = isServer && Mods.RotaryCraft.isAvailable - - private var omega = 0 - private var torque = 0 - private var power = 0L - private var alpha = 0 - - // ----------------------------------------------------------------------- // - - override def updateEntity() { - if (useRotaryCraftPower) updateEnergy() - super.updateEntity() - } - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - private def updateEnergy() { - if (world.getTotalWorldTime % Settings.get.tickFrequency == 0) { - tryAllSides((demand, _) => { - val consumed = demand.toLong min power - power -= consumed - consumed - }, Power.fromWA, Power.toWA) - } - } - - // ----------------------------------------------------------------------- // - // ShaftMachine - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getOmega: Int = omega - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getTorque: Int = torque - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getPower: Long = power - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getName: String = OpenComputers.Name - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getIORenderAlpha: Int = alpha - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def setIORenderAlpha(value: Int): Unit = alpha = value - - // ----------------------------------------------------------------------- // - // ShaftPowerReceiver - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def setOmega(value: Int): Unit = omega = value - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def setTorque(value: Int): Unit = torque = value - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def setPower(value: Long): Unit = power = value - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def noInputMachine(): Unit = { - omega = 0 - torque = 0 - power = 0 - } - - // ----------------------------------------------------------------------- // - // PowerAcceptor - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def canReadFrom(forgeDirection: EnumFacing): Boolean = true - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def isReceiving: Boolean = true - - @Optional.Method(modid = Mods.IDs.RotaryCraft) - def getMinTorque(available: Int): Int = 0 -} -*/ \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index d961d8d763..8f1a159c1e 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -20,44 +20,29 @@ object Mods { def All: ArrayBuffer[ModBase] = knownMods.clone() val AppliedEnergistics2 = new ClassBasedMod(IDs.AppliedEnergistics2, "appeng.api.storage.channels.IItemStorageChannel") val ComputerCraft = new SimpleMod(IDs.ComputerCraft) - val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.5.2,)") - val Forestry = new SimpleMod(IDs.Forestry, version = "@[5.2,)") - val IndustrialCraft2 = new SimpleMod(IDs.IndustrialCraft2) val Forge = new SimpleMod(IDs.Forge) val JustEnoughItems = new SimpleMod(IDs.JustEnoughItems) val Mekanism = new SimpleMod(IDs.Mekanism) - val MekanismGas = new SimpleMod(IDs.MekanismGas) val Minecraft = new SimpleMod(IDs.Minecraft) val OpenComputers = new SimpleMod(IDs.OpenComputers) val TIS3D = new SimpleMod(IDs.TIS3D, version = "@[0.9,)") val Waila = new SimpleMod(IDs.Waila) - val ProjectRedBase = new SimpleMod((IDs.ProjectRedCore)) val ProjectRedTransmission = new SimpleMod((IDs.ProjectRedTransmission)) val DraconicEvolution = new SimpleMod(IDs.DraconicEvolution) val EnderStorage = new SimpleMod(IDs.EnderStorage) - val Thaumcraft = new SimpleMod(IDs.Thaumcraft) - val Charset = new SimpleMod(IDs.Charset) - val WirelessRedstoneCBE = new SimpleMod(IDs.WirelessRedstoneCBE) // ----------------------------------------------------------------------- // val Proxies = Array( integration.appeng.ModAppEng, - integration.ec.ModExtraCells, - integration.forestry.ModForestry, - integration.ic2.ModIndustrialCraft2, integration.minecraftforge.ModMinecraftForge, integration.tis3d.ModTIS3D, integration.mekanism.ModMekanism, - integration.mekanism.gas.ModMekanismGas, integration.minecraft.ModMinecraft, integration.waila.ModWaila, integration.projectred.ModProjectRed, integration.computercraft.ModComputerCraft, integration.enderstorage.ModEnderStorage, - integration.thaumcraft.ModThaumcraft, - integration.charset.ModCharset, - integration.wrcbe.ModWRCBE, // We go late to ensure all other mod integration is done, e.g. to // allow properly checking if wireless redstone is present. @@ -87,24 +72,16 @@ object Mods { object IDs { final val AppliedEnergistics2 = "appliedenergistics2" final val ComputerCraft = "computercraft" - final val ExtraCells = "extracells" - final val Forestry = "forestry" final val Forge = "forge" - final val IndustrialCraft2 = "ic2" final val JustEnoughItems = "jei" final val Mekanism = "mekanism" - final val MekanismGas = "MekanismAPI|gas" final val Minecraft = "minecraft" final val OpenComputers = "opencomputers" final val TIS3D = "tis3d" final val Waila = "waila" - final val ProjectRedCore = "projectred-core" final val ProjectRedTransmission = "projectred-transmission" final val DraconicEvolution = "draconicevolution" final val EnderStorage = "enderstorage" - final val Thaumcraft = "thaumcraft" - final val Charset = "charset" - final val WirelessRedstoneCBE = "wrcbe" } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/integration/charset/ModCharset.scala b/src/main/scala/li/cil/oc/integration/charset/ModCharset.scala deleted file mode 100644 index 4c452c54b9..0000000000 --- a/src/main/scala/li/cil/oc/integration/charset/ModCharset.scala +++ /dev/null @@ -1,54 +0,0 @@ -package li.cil.oc.integration.charset - -import li.cil.oc.integration.{ModProxy, Mods} -import li.cil.oc.integration.util.BundledRedstone -import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider -import li.cil.oc.util.BlockPosition -import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import pl.asie.charset.api.wires.{IBundledEmitter, IBundledReceiver, IRedstoneEmitter} - -object ModCharset extends ModProxy with RedstoneProvider { - class BundledRedstoneView(val data: Array[Int], val onChange: () => Unit) extends IBundledEmitter with IBundledReceiver { - override def getBundledSignal: Array[Byte] = data.map(i => i.toByte) - - override def onBundledInputChange(): Unit = { onChange() } - } - - override def getMod = Mods.Charset - - override def initialize(): Unit = { - BundledRedstone.addProvider(this) - } - - override def computeInput(pos: BlockPosition, side: EnumFacing): Int = { - val world = pos.world.get - val npos = pos.toBlockPos.offset(side) - world.getTileEntity(npos) match { - case tile: TileEntity => - if (tile.hasCapability(CapabilitiesCharset.REDSTONE_EMITTER, side.getOpposite)) { - tile.getCapability(CapabilitiesCharset.REDSTONE_EMITTER, side.getOpposite) match { - case emitter: IRedstoneEmitter => return math.min(emitter.getRedstoneSignal, 15) - } - } - 0 - case _ => 0 - } - } - - def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = { - val world = pos.world.get - val npos = pos.toBlockPos.offset(side) - world.getTileEntity(npos) match { - case tile: TileEntity => - if (tile.hasCapability(CapabilitiesCharset.BUNDLED_EMITTER, side.getOpposite)) { - tile.getCapability(CapabilitiesCharset.BUNDLED_EMITTER, side.getOpposite) match { - case emitter: IBundledEmitter => - return emitter.getBundledSignal.map(i => i.toInt & 0xFF) - } - } - null - case _ => null - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ec/DriverBlockInterface.scala b/src/main/scala/li/cil/oc/integration/ec/DriverBlockInterface.scala deleted file mode 100644 index 5d98f3c682..0000000000 --- a/src/main/scala/li/cil/oc/integration/ec/DriverBlockInterface.scala +++ /dev/null @@ -1,36 +0,0 @@ -package li.cil.oc.integration.ec - - -import appeng.api.implementations.tiles.ISegmentedInventory -import appeng.api.networking.IGridHost -import appeng.api.networking.security.IActionHost -import appeng.api.util.AEPartLocation -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.network.ManagedEnvironment -import li.cil.oc.api.prefab.DriverSidedTileEntity -import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.AEUtil -import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.BlockPos -import net.minecraft.world.World - -object DriverBlockInterface extends DriverSidedTileEntity { - def getTileEntityClass: Class[_] = AEUtil.interfaceClass - - def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntity with ISegmentedInventory with IActionHost with IGridHost]) - - final class Environment(val tile: TileEntity with ISegmentedInventory with IActionHost with IGridHost) extends ManagedTileEntityEnvironment[TileEntity with ISegmentedInventory with IActionHost](tile, "me_interface") with NetworkControl[TileEntity with ISegmentedInventory with IActionHost with IGridHost]{ - override def pos: AEPartLocation = AEPartLocation.INTERNAL - } - - object Provider extends EnvironmentProvider { - override def getEnvironment(stack: ItemStack): Class[_] = - if (AEUtil.isBlockInterface(stack)) - classOf[Environment] - else null - } - -} diff --git a/src/main/scala/li/cil/oc/integration/ec/DriverController.scala b/src/main/scala/li/cil/oc/integration/ec/DriverController.scala deleted file mode 100644 index c6891356f0..0000000000 --- a/src/main/scala/li/cil/oc/integration/ec/DriverController.scala +++ /dev/null @@ -1,38 +0,0 @@ -package li.cil.oc.integration.ec - -import appeng.api.networking.IGridHost -import appeng.api.networking.security.IActionHost -import appeng.api.util.AEPartLocation -import li.cil.oc.api.driver.EnvironmentProvider -import li.cil.oc.api.network.ManagedEnvironment -import li.cil.oc.api.prefab.DriverSidedTileEntity -import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.integration.appeng.AEUtil -import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.BlockPos -import net.minecraft.world.World - -import scala.language.existentials - -object DriverController extends DriverSidedTileEntity { - private type TileController = TileEntity with IActionHost with IGridHost - - def getTileEntityClass = AEUtil.controllerClass - - def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileController]) - - final class Environment(val tile: TileController) extends ManagedTileEntityEnvironment[TileController](tile, "me_controller") with NetworkControl[TileController]{ - override def pos: AEPartLocation = AEPartLocation.INTERNAL - } - - object Provider extends EnvironmentProvider { - override def getEnvironment(stack: ItemStack): Class[_] = - if (AEUtil.isController(stack)) - classOf[Environment] - else null - } - -} diff --git a/src/main/scala/li/cil/oc/integration/ec/ECUtil.scala b/src/main/scala/li/cil/oc/integration/ec/ECUtil.scala deleted file mode 100644 index 7d0b4dbf0f..0000000000 --- a/src/main/scala/li/cil/oc/integration/ec/ECUtil.scala +++ /dev/null @@ -1,13 +0,0 @@ -package li.cil.oc.integration.ec - -import appeng.api.AEApi -import appeng.api.storage.data.IAEFluidStack -import extracells.api.ECApi -import extracells.api.gas.{IAEGasStack, IGasStorageChannel} - -object ECUtil { - val isGasSystemEnabled = ECApi.instance.isGasSystemEnabled - val gasStorageChannel = if (isGasSystemEnabled) AEApi.instance.storage.getStorageChannel[IAEGasStack, IGasStorageChannel](classOf[IGasStorageChannel]) else null - - def canSeeFluidInNetwork(fluid: IAEFluidStack) = fluid != null && ECApi.instance.canFluidSeeInTerminal(fluid.getFluid) -} diff --git a/src/main/scala/li/cil/oc/integration/ec/ModExtraCells.scala b/src/main/scala/li/cil/oc/integration/ec/ModExtraCells.scala deleted file mode 100644 index 0e383bc8b1..0000000000 --- a/src/main/scala/li/cil/oc/integration/ec/ModExtraCells.scala +++ /dev/null @@ -1,18 +0,0 @@ -package li.cil.oc.integration.ec - -import li.cil.oc.api.Driver -import li.cil.oc.integration.Mod -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods - -object ModExtraCells extends ModProxy { - override def getMod: Mod = Mods.ExtraCells - - override def initialize(): Unit = { - Driver.add(DriverController) - Driver.add(DriverBlockInterface) - - Driver.add(DriverController.Provider) - Driver.add(DriverBlockInterface.Provider) - } -} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/ec/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/ec/NetworkControl.scala deleted file mode 100644 index 910c91288d..0000000000 --- a/src/main/scala/li/cil/oc/integration/ec/NetworkControl.scala +++ /dev/null @@ -1,30 +0,0 @@ -package li.cil.oc.integration.ec - -import appeng.api.networking.IGridHost -import appeng.api.networking.security.IActionHost -import appeng.api.util.AEPartLocation -import extracells.api.ECApi -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context -import li.cil.oc.integration.appeng.AEUtil -import li.cil.oc.util.ResultWrapper._ -import net.minecraft.tileentity.TileEntity - -import scala.collection.convert.WrapAsScala._ - -// Note to self: this class is used by ExtraCells (and potentially others), do not rename / drastically change it. -trait NetworkControl[AETile >: Null <: TileEntity with IActionHost with IGridHost] { - def tile: AETile - def pos: AEPartLocation - - @Callback(doc = "function():table -- Get a list of the stored gases in the network.") - def getGasesInNetwork(context: Context, args: Arguments): Array[AnyRef] = { - if (ECUtil.isGasSystemEnabled) - result(AEUtil.getGridStorage(tile.getGridNode(pos).getGrid).getInventory(ECUtil.gasStorageChannel).getStorageList.filter(stack => - stack != null). - map(_.getGasStack).toArray) - else - result(Array.empty) - } -} diff --git a/src/main/scala/li/cil/oc/integration/forestry/ConverterIAlleles.java b/src/main/scala/li/cil/oc/integration/forestry/ConverterIAlleles.java deleted file mode 100644 index 14d1d6b400..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/ConverterIAlleles.java +++ /dev/null @@ -1,41 +0,0 @@ -package li.cil.oc.integration.forestry; - -import com.google.common.collect.Maps; -import forestry.api.genetics.IAlleleSpecies; -import forestry.api.genetics.IMutation; -import li.cil.oc.api.driver.Converter; - -import java.util.Map; - -public class ConverterIAlleles implements Converter { - @Override - public void convert(final Object value, final Map output) { - if (value instanceof IMutation) { - final IMutation mutation = (IMutation) value; - - final IAlleleSpecies allele1 = mutation.getAllele0(); - if (allele1 != null) { - final Map allelMap1 = Maps.newHashMap(); - convert(allele1, allelMap1); - output.put("allele1", allelMap1); - } - final IAlleleSpecies allele2 = mutation.getAllele1(); - if (allele2 != null) { - final Map allelMap2 = Maps.newHashMap(); - convert(allele2, allelMap2); - output.put("allele2", allelMap2); - } - output.put("chance", mutation.getBaseChance()); - output.put("specialConditions", mutation.getSpecialConditions().toArray()); - } - - if (value instanceof IAlleleSpecies) { - convertAlleleSpecies((IAlleleSpecies) value, output); - } - } - - private void convertAlleleSpecies(final IAlleleSpecies value, final Map output) { - output.put("name", value.getAlleleName()); - output.put("uid", value.getUID()); - } -} diff --git a/src/main/scala/li/cil/oc/integration/forestry/ConverterIIndividual.java b/src/main/scala/li/cil/oc/integration/forestry/ConverterIIndividual.java deleted file mode 100644 index ee364286db..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/ConverterIIndividual.java +++ /dev/null @@ -1,249 +0,0 @@ -package li.cil.oc.integration.forestry; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import forestry.api.apiculture.EnumBeeChromosome; -import forestry.api.apiculture.IAlleleBeeEffect; -import forestry.api.apiculture.IAlleleBeeSpecies; -import forestry.api.apiculture.IBee; -import forestry.api.apiculture.IBeeGenome; -import forestry.api.arboriculture.EnumTreeChromosome; -import forestry.api.arboriculture.IAlleleFruit; -import forestry.api.arboriculture.IAlleleLeafEffect; -import forestry.api.arboriculture.IAlleleTreeSpecies; -import forestry.api.arboriculture.ITree; -import forestry.api.arboriculture.ITreeGenome; -import forestry.api.genetics.IAllele; -import forestry.api.genetics.IAlleleArea; -import forestry.api.genetics.IAlleleBoolean; -import forestry.api.genetics.IAlleleFloat; -import forestry.api.genetics.IAlleleFlowers; -import forestry.api.genetics.IAlleleInteger; -import forestry.api.genetics.IAlleleTolerance; -import forestry.api.genetics.IChromosome; -import forestry.api.genetics.IChromosomeType; -import forestry.api.genetics.IGenome; -import forestry.api.genetics.IIndividual; -import forestry.api.genetics.IIndividualLiving; -import forestry.api.lepidopterology.*; -import li.cil.oc.api.driver.Converter; - -import java.util.Map; - -/* - * Partially copied from: - * https://github.com/OpenMods/OpenPeripheral - */ -public class ConverterIIndividual implements Converter { - private abstract static class GenomeAccess { - public IAllele getAllele(IGenome genome, int chromosome) { - IChromosome[] genotype = genome.getChromosomes(); - IChromosome ch = genotype[chromosome]; - if (ch == null) return null; - return getAllele(ch); - } - - protected abstract IAllele getAllele(IChromosome chromosome); - } - - private static final GenomeAccess ACTIVE = new GenomeAccess() { - @Override - protected IAllele getAllele(IChromosome chromosome) { - return chromosome.getActiveAllele(); - } - }; - - private static final GenomeAccess INACTIVE = new GenomeAccess() { - @Override - protected IAllele getAllele(IChromosome chromosome) { - return chromosome.getInactiveAllele(); - } - }; - - private interface IAlleleConverter { - Object convert(A allele); - } - - private static final Map, IAlleleConverter> converters = - ImmutableMap., IAlleleConverter>builder() - .put(IAlleleFloat.class, new IAlleleConverter() { - @Override - public Object convert(IAlleleFloat allele) { - return allele.getValue(); - } - }) - .put(IAlleleInteger.class, new IAlleleConverter() { - @Override - public Object convert(IAlleleInteger allele) { - return allele.getValue(); - } - }) - .put(IAlleleBoolean.class, new IAlleleConverter() { - @Override - public Object convert(IAlleleBoolean allele) { - return allele.getValue(); - } - }) - .put(IAlleleArea.class, new IAlleleConverter() { - @Override - public Object convert(IAlleleArea allele) { - return allele.getValue(); - } - }) - .build(); - - private abstract static class GenomeReader & IChromosomeType> { - private final G genome; - - public GenomeReader(G genome) { - this.genome = genome; - } - - @SuppressWarnings("unchecked") - protected A getAllele(GenomeAccess access, Class cls, E chromosome) { - Preconditions.checkArgument(chromosome.getAlleleClass() == cls); - IAllele allele = access.getAllele(genome, chromosome.ordinal()); - return (A) allele; - } - - protected Object convertAllele(GenomeAccess access, Class cls, E chromosome) { - A allele = getAllele(access, cls, chromosome); - if (allele == null) return "missing"; - @SuppressWarnings("unchecked") - IAlleleConverter converter = (IAlleleConverter) converters.get(cls); - return converter != null ? converter.convert(allele) : allele.getAlleleName(); - } - - protected abstract void addAlleleInfo(GenomeAccess access, Map result); - - public Map getActiveInfo() { - Map result = Maps.newHashMap(); - addAlleleInfo(ACTIVE, result); - return result; - } - - public Map getInactiveInfo() { - Map result = Maps.newHashMap(); - addAlleleInfo(INACTIVE, result); - return result; - } - } - - private static class BeeGenomeReader extends GenomeReader { - - public BeeGenomeReader(IBeeGenome genome) { - super(genome); - } - - @Override - protected void addAlleleInfo(GenomeAccess access, Map result) { - result.put("species", convertAllele(access, IAlleleBeeSpecies.class, EnumBeeChromosome.SPECIES)); - result.put("speed", convertAllele(access, IAlleleFloat.class, EnumBeeChromosome.SPEED)); - result.put("lifespan", convertAllele(access, IAlleleInteger.class, EnumBeeChromosome.LIFESPAN)); - result.put("fertility", convertAllele(access, IAlleleInteger.class, EnumBeeChromosome.FERTILITY)); - result.put("temperatureTolerance", convertAllele(access, IAlleleTolerance.class, EnumBeeChromosome.TEMPERATURE_TOLERANCE)); - result.put("neverSleeps", convertAllele(access, IAlleleBoolean.class, EnumBeeChromosome.NEVER_SLEEPS)); - result.put("humidityTolerance", convertAllele(access, IAlleleTolerance.class, EnumBeeChromosome.HUMIDITY_TOLERANCE)); - result.put("toleratesRain", convertAllele(access, IAlleleBoolean.class, EnumBeeChromosome.TOLERATES_RAIN)); - result.put("caveDwelling", convertAllele(access, IAlleleBoolean.class, EnumBeeChromosome.CAVE_DWELLING)); - result.put("flowerProvider", convertAllele(access, IAlleleFlowers.class, EnumBeeChromosome.FLOWER_PROVIDER)); - result.put("flowering", convertAllele(access, IAlleleInteger.class, EnumBeeChromosome.FLOWERING)); - result.put("effect", convertAllele(access, IAlleleBeeEffect.class, EnumBeeChromosome.EFFECT)); - result.put("territory", convertAllele(access, IAlleleArea.class, EnumBeeChromosome.TERRITORY)); - } - } - - private static class ButterflyGenomeReader extends GenomeReader { - - public ButterflyGenomeReader(IButterflyGenome genome) { - super(genome); - } - - @Override - protected void addAlleleInfo(GenomeAccess access, Map result) { - result.put("species", convertAllele(access, IAlleleButterflySpecies.class, EnumButterflyChromosome.SPECIES)); - result.put("size", convertAllele(access, IAlleleFloat.class, EnumButterflyChromosome.SIZE)); - result.put("speed", convertAllele(access, IAlleleFloat.class, EnumButterflyChromosome.SPEED)); - result.put("lifespan", convertAllele(access, IAlleleInteger.class, EnumButterflyChromosome.LIFESPAN)); - result.put("metabolism", convertAllele(access, IAlleleInteger.class, EnumButterflyChromosome.METABOLISM)); - result.put("fertility", convertAllele(access, IAlleleInteger.class, EnumButterflyChromosome.FERTILITY)); - result.put("temperatureTolerance", convertAllele(access, IAlleleTolerance.class, EnumButterflyChromosome.TEMPERATURE_TOLERANCE)); - result.put("humidityTolerance", convertAllele(access, IAlleleTolerance.class, EnumButterflyChromosome.HUMIDITY_TOLERANCE)); - result.put("nocturnal", convertAllele(access, IAlleleBoolean.class, EnumButterflyChromosome.NOCTURNAL)); - result.put("tolerantFlyer", convertAllele(access, IAlleleBoolean.class, EnumButterflyChromosome.TOLERANT_FLYER)); - result.put("fireResist", convertAllele(access, IAlleleBoolean.class, EnumButterflyChromosome.FIRE_RESIST)); - result.put("flowerProvider", convertAllele(access, IAlleleFlowers.class, EnumButterflyChromosome.FLOWER_PROVIDER)); - result.put("effect", convertAllele(access, IAlleleButterflyEffect.class, EnumButterflyChromosome.EFFECT)); - result.put("cocoon", convertAllele(access, IAlleleButterflyCocoon.class, EnumButterflyChromosome.COCOON)); - } - } - - private static class TreeGenomeReader extends GenomeReader { - - public TreeGenomeReader(ITreeGenome genome) { - super(genome); - } - - @Override - protected void addAlleleInfo(GenomeAccess access, Map result) { - result.put("species", convertAllele(access, IAlleleTreeSpecies.class, EnumTreeChromosome.SPECIES)); - result.put("fireproof", convertAllele(access, IAlleleBoolean.class, EnumTreeChromosome.FIREPROOF)); - result.put("height", convertAllele(access, IAlleleFloat.class, EnumTreeChromosome.HEIGHT)); - result.put("fertility", convertAllele(access, IAlleleFloat.class, EnumTreeChromosome.FERTILITY)); - result.put("fruits", convertAllele(access, IAlleleFruit.class, EnumTreeChromosome.FRUITS)); - result.put("yield", convertAllele(access, IAlleleFloat.class, EnumTreeChromosome.YIELD)); - result.put("sappiness", convertAllele(access, IAlleleFloat.class, EnumTreeChromosome.SAPPINESS)); - result.put("effect", convertAllele(access, IAlleleLeafEffect.class, EnumTreeChromosome.EFFECT)); - result.put("maturation", convertAllele(access, IAlleleInteger.class, EnumTreeChromosome.MATURATION)); - result.put("girth", convertAllele(access, IAlleleInteger.class, EnumTreeChromosome.GIRTH)); - } - } - - @Override - public void convert(final Object value, final Map output) { - if (value instanceof IIndividual) { - IIndividual individual = (IIndividual) value; - output.put("displayName", individual.getDisplayName()); - output.put("ident", individual.getIdent()); - - final boolean isAnalyzed = individual.isAnalyzed(); - output.put("isAnalyzed", isAnalyzed); - output.put("isSecret", individual.isSecret()); - GenomeReader genomeReader = null; - - if (individual instanceof IIndividualLiving) { - IIndividualLiving living = (IIndividualLiving) individual; - output.put("health", living.getHealth()); - output.put("maxHealth", living.getMaxHealth()); - } - - if (individual instanceof IBee) { - IBee bee = (IBee) individual; - output.put("type", "bee"); - output.put("canSpawn", bee.canSpawn()); - output.put("generation", bee.getGeneration()); - output.put("hasEffect", bee.hasEffect()); - output.put("isAlive", bee.isAlive()); - output.put("isNatural", bee.isNatural()); - - if (isAnalyzed) genomeReader = new BeeGenomeReader(bee.getGenome()); - } else if (individual instanceof IButterfly) { - IButterfly butterfly = (IButterfly) individual; - output.put("type", "butterfly"); - output.put("size", butterfly.getSize()); - if (isAnalyzed) genomeReader = new ButterflyGenomeReader(butterfly.getGenome()); - } else if (individual instanceof ITree) { - ITree tree = (ITree) individual; - output.put("type", "tree"); - output.put("plantType", tree.getDisplayName()); - if (isAnalyzed) genomeReader = new TreeGenomeReader(tree.getGenome()); - } - - if (genomeReader != null) { - output.put("active", genomeReader.getActiveInfo()); - output.put("inactive", genomeReader.getInactiveInfo()); - } - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/forestry/ConverterItemStack.scala b/src/main/scala/li/cil/oc/integration/forestry/ConverterItemStack.scala deleted file mode 100644 index fd2e64f677..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/ConverterItemStack.scala +++ /dev/null @@ -1,17 +0,0 @@ -package li.cil.oc.integration.forestry - -import java.util - -import forestry.api.genetics.AlleleManager -import li.cil.oc.api.driver.Converter -import net.minecraft.item.ItemStack - -import scala.collection.convert.WrapAsScala._ - -object ConverterItemStack extends Converter { - override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { - case stack: ItemStack if AlleleManager.alleleRegistry.isIndividual(stack) => - output += "individual" -> AlleleManager.alleleRegistry.getIndividual(stack) - case _ => - } -} diff --git a/src/main/scala/li/cil/oc/integration/forestry/DriverAnalyzer.scala b/src/main/scala/li/cil/oc/integration/forestry/DriverAnalyzer.scala deleted file mode 100644 index 6cf3f9dfe3..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/DriverAnalyzer.scala +++ /dev/null @@ -1,35 +0,0 @@ -package li.cil.oc.integration.forestry -import forestry.core.tiles.TileAnalyzer -import li.cil.oc.api.driver.NamedBlock -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context -import li.cil.oc.api.prefab.DriverSidedTileEntity -import li.cil.oc.integration.ManagedTileEntityEnvironment -import li.cil.oc.util.ResultWrapper._ -import net.minecraft.world.World -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.BlockPos -import forestry.api.genetics.AlleleManager - -class DriverAnalyzer extends DriverSidedTileEntity { - override def getTileEntityClass = classOf[TileAnalyzer] - - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing) = new Environment(world.getTileEntity(pos).asInstanceOf[TileAnalyzer]) - - final class Environment(tileEntity: TileAnalyzer) extends ManagedTileEntityEnvironment[TileAnalyzer](tileEntity, "forestry_analyzer") with NamedBlock { - override def preferredName = "forestry_analyzer" - - override def priority = 0 - - @Callback(doc = "function():boolean -- Get whether the analyzer can work.") - def isWorking(context: Context, args: Arguments): Array[AnyRef] = result(tileEntity.hasWork) - - @Callback(doc = "function():double -- Get the progress of the current operation.") - def getProgress(context: Context, args: Arguments): Array[AnyRef] = result(1.0 - tileEntity.getProgressScaled(100) / 100.0) - - @Callback(doc = "function():table -- Get info on the currently present bee.") - def getIndividualOnDisplay(context: Context, args: Arguments): Array[AnyRef] = result(AlleleManager.alleleRegistry.getIndividual(tileEntity.getIndividualOnDisplay)) - } - -} diff --git a/src/main/scala/li/cil/oc/integration/forestry/DriverBeeHouse.java b/src/main/scala/li/cil/oc/integration/forestry/DriverBeeHouse.java deleted file mode 100644 index 673ca6c9e0..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/DriverBeeHouse.java +++ /dev/null @@ -1,163 +0,0 @@ -package li.cil.oc.integration.forestry; - -import com.google.common.collect.Sets; -import forestry.api.apiculture.IBeeHousing; -import forestry.api.genetics.AlleleManager; -import forestry.api.genetics.IAllele; -import forestry.api.genetics.IAlleleSpecies; -import forestry.api.genetics.IMutation; -import forestry.api.genetics.ISpeciesRoot; -import li.cil.oc.api.driver.NamedBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class DriverBeeHouse extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return IBeeHousing.class; - } - - @Override - public ManagedEnvironment createEnvironment(World world, BlockPos pos, EnumFacing side) { - return new Environment((IBeeHousing) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { - public Environment(final IBeeHousing tileEntity) { - super(tileEntity, "bee_housing"); - } - - @Override - public String preferredName() { - return "bee_housing"; - } - - @Override - public int priority() { - return 0; - } - - @Callback(doc = "function():boolean -- Can the bees breed?") - public Object[] canBreed(final Context context, final Arguments args) { - return new Object[]{tileEntity.getBeekeepingLogic().canWork()}; - } - - @Callback(doc = "function():table -- Get the drone") - public Object[] getDrone(final Context context, final Arguments args) { - final ItemStack drone = tileEntity.getBeeInventory().getDrone(); - if (drone != null) { - return new Object[]{AlleleManager.alleleRegistry.getIndividual(drone)}; - } - return null; - } - - @Callback(doc = "function():table -- Get the queen") - public Object[] getQueen(final Context context, final Arguments args) { - final ItemStack queen = tileEntity.getBeeInventory().getQueen(); - if (queen != null) { - return new Object[]{AlleleManager.alleleRegistry.getIndividual(queen)}; - } - return null; - } - - @Callback(doc = "function():table -- Get the full breeding list thingy.") - public Object[] getBeeBreedingData(final Context context, final Arguments args) { - final ISpeciesRoot beeRoot = AlleleManager.alleleRegistry.getSpeciesRoot("rootBees"); - if (beeRoot == null) { - return null; - } - - final Set> result = Sets.newHashSet(); - for (IMutation mutation : beeRoot.getMutations(false)) { - final HashMap mutationMap = new HashMap(); - - final IAllele allele1 = mutation.getAllele0(); - if (allele1 != null) { - mutationMap.put("allele1", allele1.getAlleleName()); - } - - final IAllele allele2 = mutation.getAllele1(); - if (allele2 != null) { - mutationMap.put("allele2", allele2.getAlleleName()); - } - - mutationMap.put("chance", mutation.getBaseChance()); - mutationMap.put("specialConditions", mutation - .getSpecialConditions().toArray()); - - final IAllele[] template = mutation.getTemplate(); - if (template != null && template.length > 0) { - mutationMap.put("result", template[0].getAlleleName()); - } - result.add(mutationMap); - } - return new Object[]{result}; - } - - @Callback(doc = "function():table -- Get all known bees mutations") - public Object[] listAllSpecies(final Context context, final Arguments args) { - final ISpeciesRoot beeRoot = AlleleManager.alleleRegistry.getSpeciesRoot("rootBees"); - if (beeRoot == null) { - return null; - } - - final Set result = Sets.newHashSet(); - for (IMutation mutation : beeRoot.getMutations(false)) { - final IAllele[] template = mutation.getTemplate(); - if (template == null || template.length <= 0) { - continue; - } - - final IAllele allele = template[0]; - if (!(allele instanceof IAlleleSpecies)) { - continue; - } - - result.add((IAlleleSpecies) allele); - } - return new Object[]{result}; - } - - @Callback(doc = "function(beeName:string):table -- Get the parents for a particular mutation") - public Object[] getBeeParents(final Context context, final Arguments args) { - final ISpeciesRoot beeRoot = AlleleManager.alleleRegistry.getSpeciesRoot("rootBees"); - if (beeRoot == null) { - return null; - } - - final Set result = Sets.newHashSet(); - final String childType = args.checkString(0).toLowerCase(); - for (IMutation mutation : beeRoot.getMutations(false)) { - final IAllele[] template = mutation.getTemplate(); - if (template == null || template.length < 1) { - continue; - } - - final IAllele allele = template[0]; - if (!(allele instanceof IAlleleSpecies)) { - continue; - } - - final IAlleleSpecies species = (IAlleleSpecies) allele; - final String uid = species.getUID().toLowerCase(); - final String localizedName = species.getAlleleName().toLowerCase(); - if (localizedName.equals(childType) || uid.equals(childType)) { - result.add(mutation); - } - } - return new Object[]{result}; - } - } -} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/forestry/ModForestry.scala b/src/main/scala/li/cil/oc/integration/forestry/ModForestry.scala deleted file mode 100644 index c23dc4e9a4..0000000000 --- a/src/main/scala/li/cil/oc/integration/forestry/ModForestry.scala +++ /dev/null @@ -1,17 +0,0 @@ -package li.cil.oc.integration.forestry - -import li.cil.oc.api.Driver -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods - -object ModForestry extends ModProxy { - override def getMod = Mods.Forestry - - override def initialize() { - Driver.add(new ConverterIAlleles) - Driver.add(new ConverterIIndividual) - Driver.add(ConverterItemStack) - Driver.add(new DriverAnalyzer) - Driver.add(new DriverBeeHouse) - } -} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/ic2/ConverterElectricItem.java b/src/main/scala/li/cil/oc/integration/ic2/ConverterElectricItem.java deleted file mode 100644 index 981a3faf69..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/ConverterElectricItem.java +++ /dev/null @@ -1,27 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.api.item.ElectricItem; -import ic2.api.item.IElectricItem; -import li.cil.oc.api.driver.Converter; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; - -import java.util.Map; - -public class ConverterElectricItem implements Converter { - @Override - public void convert(final Object value, final Map output) { - if (value instanceof ItemStack) { - final ItemStack stack = (ItemStack) value; - final Item item = stack.getItem(); - if (item instanceof IElectricItem) { - final IElectricItem electricItem = (IElectricItem) item; - output.put("canProvideEnergy", electricItem.canProvideEnergy(stack)); - output.put("charge", ElectricItem.manager.getCharge(stack)); - output.put("maxCharge", electricItem.getMaxCharge(stack)); - output.put("tier", electricItem.getTier(stack)); - output.put("transferLimit", electricItem.getTransferLimit(stack)); - } - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverEnergy.java b/src/main/scala/li/cil/oc/integration/ic2/DriverEnergy.java deleted file mode 100644 index aa8cc52bc9..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverEnergy.java +++ /dev/null @@ -1,61 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.core.block.TileEntityBlock; -import ic2.core.block.comp.Energy; -import li.cil.oc.api.driver.DriverBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverEnergy implements DriverBlock { - @Override - public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) { - final TileEntity tileEntity = world.getTileEntity(pos); - if (tileEntity instanceof TileEntityBlock) { - final TileEntityBlock tileEntityBlock = (TileEntityBlock) tileEntity; - return tileEntityBlock.hasComponent(Energy.class); - } - return false; - } - - @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((TileEntityBlock) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment { - public Environment(final TileEntityBlock tileEntity) { - super(tileEntity, "ic2_energy"); - } - - @Callback - public Object[] getCapacity(final Context context, final Arguments args) { - final Energy energy = tileEntity.getComponent(Energy.class); - return new Object[]{energy.getCapacity()}; - } - - @Callback - public Object[] getEnergy(final Context context, final Arguments args) { - final Energy energy = tileEntity.getComponent(Energy.class); - return new Object[]{energy.getEnergy()}; - } - - @Callback - public Object[] getSinkTier(final Context context, final Arguments args) { - final Energy energy = tileEntity.getComponent(Energy.class); - return new Object[]{energy.getSinkTier()}; - } - - @Callback - public Object[] getSourceTier(final Context context, final Arguments args) { - final Energy energy = tileEntity.getComponent(Energy.class); - return new Object[]{energy.getSourceTier()}; - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverEnergyConductor.java b/src/main/scala/li/cil/oc/integration/ic2/DriverEnergyConductor.java deleted file mode 100644 index 8485265cb5..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverEnergyConductor.java +++ /dev/null @@ -1,50 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.api.energy.tile.IEnergyConductor; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverEnergyConductor extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return IEnergyConductor.class; - } - - @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((IEnergyConductor) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment { - public Environment(final IEnergyConductor tileEntity) { - super(tileEntity, "energy_conductor"); - } - - @Callback - public Object[] getConductionLoss(final Context context, final Arguments args) { - return new Object[]{tileEntity.getConductionLoss()}; - } - - @Callback - public Object[] getConductorBreakdownEnergy(final Context context, final Arguments args) { - return new Object[]{tileEntity.getConductorBreakdownEnergy()}; - } - - @Callback - public Object[] getInsulationBreakdownEnergy(final Context context, final Arguments args) { - return new Object[]{tileEntity.getInsulationBreakdownEnergy()}; - } - - @Callback - public Object[] getInsulationEnergyAbsorption(final Context context, final Arguments args) { - return new Object[]{tileEntity.getInsulationEnergyAbsorption()}; - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverMassFab.java b/src/main/scala/li/cil/oc/integration/ic2/DriverMassFab.java deleted file mode 100644 index a32e3e45e7..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverMassFab.java +++ /dev/null @@ -1,47 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.core.block.comp.Energy; -import ic2.core.block.machine.tileentity.TileEntityMatter; -import li.cil.oc.api.driver.NamedBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.prefab.AbstractManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverMassFab extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return TileEntityMatter.class; - } - - @Override - public AbstractManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((TileEntityMatter) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { - public Environment(final TileEntityMatter tileEntity) { - super(tileEntity, "mass_fab"); - } - - @Override - public String preferredName() { - return "mass_fab"; - } - - @Override - public int priority() { - return 0; - } - - @Callback - public Object[] getProgress(final Context context, final Arguments args) { - return new Object[]{Math.min(100 * tileEntity.getComponent(Energy.class).getFillRatio(), 100)}; - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverReactor.java b/src/main/scala/li/cil/oc/integration/ic2/DriverReactor.java deleted file mode 100644 index 0d7231ea8a..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverReactor.java +++ /dev/null @@ -1,67 +0,0 @@ -package li.cil.oc.integration.ic2; - - -import ic2.api.reactor.IReactor; -import li.cil.oc.api.driver.NamedBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverReactor extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return IReactor.class; - } - - @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((IReactor) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { - public Environment(final IReactor tileEntity) { - super(tileEntity, "reactor"); - } - - @Override - public String preferredName() { - return "reactor"; - } - - @Override - public int priority() { - return 0; - } - - @Callback(doc = "function():number -- Get the reactor's heat.") - public Object[] getHeat(final Context context, final Arguments args) { - return new Object[]{tileEntity.getHeat()}; - } - - @Callback(doc = "function():number -- Get the reactor's maximum heat before exploding.") - public Object[] getMaxHeat(final Context context, final Arguments args) { - return new Object[]{tileEntity.getMaxHeat()}; - } - - @Callback(doc = "function():number -- Get the reactor's energy output. Not multiplied with the base EU/t value.") - public Object[] getReactorEnergyOutput(final Context context, final Arguments args) { - return new Object[]{tileEntity.getReactorEnergyOutput()}; - } - - @Callback(doc = "function():number -- Get the reactor's base EU/t value.") - public Object[] getReactorEUOutput(final Context context, final Arguments args) { - return new Object[]{tileEntity.getReactorEUEnergyOutput()}; - } - - @Callback(doc = "function():boolean -- Get whether the reactor is active and supposed to produce energy.") - public Object[] producesEnergy(final Context context, final Arguments args) { - return new Object[]{tileEntity.produceEnergy()}; - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverReactorChamber.java b/src/main/scala/li/cil/oc/integration/ic2/DriverReactorChamber.java deleted file mode 100644 index d6d2a5a559..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverReactorChamber.java +++ /dev/null @@ -1,87 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.api.reactor.IReactor; -import ic2.api.reactor.IReactorChamber; -import li.cil.oc.api.driver.NamedBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverReactorChamber extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return IReactorChamber.class; - } - - @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((IReactorChamber) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { - public Environment(final IReactorChamber tileEntity) { - super(tileEntity, "reactor_chamber"); - } - - @Override - public String preferredName() { - return "reactor_chamber"; - } - - @Override - public int priority() { - return 0; - } - - @Callback(doc = "function():number -- Get the reactor's heat.") - public Object[] getHeat(final Context context, final Arguments args) { - final IReactor reactor = tileEntity.getReactorInstance(); - if (reactor != null) { - return new Object[]{reactor.getHeat()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's maximum heat before exploding.") - public Object[] getMaxHeat(final Context context, final Arguments args) { - final IReactor reactor = tileEntity.getReactorInstance(); - if (reactor != null) { - return new Object[]{tileEntity.getReactorInstance().getMaxHeat()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's energy output. Not multiplied with the base EU/t value.") - public Object[] getReactorEnergyOutput(final Context context, final Arguments args) { - final IReactor reactor = tileEntity.getReactorInstance(); - if (reactor != null) { - return new Object[]{tileEntity.getReactorInstance().getReactorEnergyOutput()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's base EU/t value.") - public Object[] getReactorEUOutput(final Context context, final Arguments args) { - return new Object[]{tileEntity.getReactorInstance().getReactorEUEnergyOutput()}; - } - - @Callback(doc = "function():boolean -- Get whether the reactor is active and supposed to produce energy.") - public Object[] producesEnergy(final Context context, final Arguments args) { - final IReactor reactor = tileEntity.getReactorInstance(); - if (reactor != null) { - return new Object[]{tileEntity.getReactorInstance().produceEnergy()}; - } else { - return new Object[]{false}; - } - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/DriverReactorRedstonePort.java b/src/main/scala/li/cil/oc/integration/ic2/DriverReactorRedstonePort.java deleted file mode 100644 index 103a7adecf..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/DriverReactorRedstonePort.java +++ /dev/null @@ -1,102 +0,0 @@ -package li.cil.oc.integration.ic2; - -import ic2.api.reactor.IReactor; -import ic2.core.block.comp.FluidReactorLookup; -import ic2.core.block.reactor.tileentity.TileEntityReactorRedstonePort; -import li.cil.oc.api.driver.NamedBlock; -import li.cil.oc.api.machine.Arguments; -import li.cil.oc.api.machine.Callback; -import li.cil.oc.api.machine.Context; -import li.cil.oc.api.network.ManagedEnvironment; -import li.cil.oc.api.prefab.DriverSidedTileEntity; -import li.cil.oc.integration.ManagedTileEntityEnvironment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.World; - -public final class DriverReactorRedstonePort extends DriverSidedTileEntity { - @Override - public Class getTileEntityClass() { - return TileEntityReactorRedstonePort.class; - } - - @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((TileEntityReactorRedstonePort) world.getTileEntity(pos)); - } - - public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { - public Environment(final TileEntityReactorRedstonePort tileEntity) { - super(tileEntity, "reactor_redstone_port"); - } - - @Override - public String preferredName() { - return "reactor_redstone_port"; - } - - @Override - public int priority() { - return 0; - } - - private IReactor getReactor() { - final FluidReactorLookup lookup = tileEntity.getComponent(FluidReactorLookup.class); - if (lookup != null) { - return lookup.getReactor(); - } else { - return null; - } - } - - @Callback(doc = "function():number -- Get the reactor's heat.") - public Object[] getHeat(final Context context, final Arguments args) { - final IReactor reactor = getReactor(); - if (reactor != null) { - return new Object[]{reactor.getHeat()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's maximum heat before exploding.") - public Object[] getMaxHeat(final Context context, final Arguments args) { - final IReactor reactor = getReactor(); - if (reactor != null) { - return new Object[]{reactor.getMaxHeat()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's energy output. Not multiplied with the base EU/t value.") - public Object[] getReactorEnergyOutput(final Context context, final Arguments args) { - final IReactor reactor = getReactor(); - if (reactor != null) { - return new Object[]{reactor.getReactorEnergyOutput()}; - } else { - return new Object[]{0}; - } - } - - @Callback(doc = "function():number -- Get the reactor's base EU/t value.") - public Object[] getReactorEUOutput(final Context context, final Arguments args) { - final IReactor reactor = getReactor(); - if (reactor != null) { - return new Object[]{getReactor().getReactorEUEnergyOutput()}; - } else { - return new Object[]{false}; - } - } - - @Callback(doc = "function():boolean -- Get whether the reactor is active and supposed to produce energy.") - public Object[] producesEnergy(final Context context, final Arguments args) { - final IReactor reactor = getReactor(); - if (reactor != null) { - return new Object[]{reactor.produceEnergy()}; - } else { - return new Object[]{false}; - } - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/ElectricItemManager.scala b/src/main/scala/li/cil/oc/integration/ic2/ElectricItemManager.scala deleted file mode 100644 index cb0f135116..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/ElectricItemManager.scala +++ /dev/null @@ -1,54 +0,0 @@ -package li.cil.oc.integration.ic2 - -import ic2.api.item.IElectricItem -import ic2.api.item.IElectricItemManager -import li.cil.oc.Settings -import li.cil.oc.api.driver.item.Chargeable -import li.cil.oc.integration.util.Power -import net.minecraft.entity.EntityLivingBase -import net.minecraft.item.ItemStack - -object ElectricItemManager extends IElectricItemManager { - override def getCharge(stack: ItemStack): Double = { - if (stack == null) 0 - else stack.getItem match { - case chargeable: Chargeable => - Power.toEU(Int.MaxValue + chargeable.charge(stack, -Int.MaxValue, true)) - case _ => 0 - } - } - - override def charge(stack: ItemStack, amount: Double, tier: Int, ignoreTransferLimit: Boolean, simulate: Boolean): Double = { - if (stack == null) 0 - else stack.getItem match { - case chargeable: Chargeable => - val limitedAmount = if (ignoreTransferLimit) math.min(Int.MaxValue, amount) else math.min(amount, Settings.get.chargeRateTablet) - limitedAmount - Power.toEU(chargeable.charge(stack, Power.fromEU(limitedAmount), simulate)) - case _ => 0 - } - } - - override def discharge(stack: ItemStack, amount: Double, tier: Int, ignoreTransferLimit: Boolean, externally: Boolean, simulate: Boolean): Double = { - 0.0 // TODO if we ever need it... - } - - override def chargeFromArmor(stack: ItemStack, entity: EntityLivingBase): Unit = {} - - override def canUse(stack: ItemStack, amount: Double): Boolean = getCharge(stack) >= amount - - override def use(stack: ItemStack, amount: Double, entity: EntityLivingBase): Boolean = canUse(stack, amount) && { - false // TODO if we ever need it... - } - - override def getToolTip(stack: ItemStack): String = "" - - override def getMaxCharge(stack: ItemStack): Double = Option(stack).map(_.getItem) match { - case Some(item: IElectricItem) => item.getMaxCharge(stack) - case _ => 0 - } - - override def getTier(stack: ItemStack): Int = Option(stack).map(_.getItem) match { - case Some(item: IElectricItem) => item.getTier(stack) - case _ => 0 - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala b/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala deleted file mode 100644 index 6aae5711b9..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala +++ /dev/null @@ -1,80 +0,0 @@ -package li.cil.oc.integration.ic2 - -import ic2.api.item.ElectricItem -import ic2.api.item.IElectricItem -import ic2.api.item.ISpecialElectricItem -import ic2.core.item.tool.ItemToolWrench -import li.cil.oc.api.event.RobotUsedToolEvent -import li.cil.oc.integration.util.Power -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.ItemStack -import net.minecraft.util.math.BlockPos -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -object EventHandlerIndustrialCraft2 { - @SubscribeEvent - def onRobotApplyDamageRate(e: RobotUsedToolEvent.ApplyDamageRate) { - val optManagerBefore = e.toolBeforeUse.getItem match { - case item: ISpecialElectricItem => Option(item.getManager(e.toolBeforeUse)) - case item: IElectricItem => Option(ElectricItem.manager) - case _ => None - } - val optManagerAfter = e.toolAfterUse.getItem match { - case item: ISpecialElectricItem => Option(item.getManager(e.toolAfterUse)) - case item: IElectricItem => Option(ElectricItem.manager) - case _ => None - } - (optManagerBefore, optManagerAfter) match { - case (Some(managerBefore), Some(managerAfter)) => - val damage = managerBefore.getCharge(e.toolBeforeUse) - managerAfter.getCharge(e.toolAfterUse) - if (damage > 0) { - val actualDamage = damage * e.getDamageRate - val repairedDamage = - if (e.agent.player.getRNG.nextDouble() > 0.5) - damage - math.floor(actualDamage).toInt - else - damage - math.ceil(actualDamage).toInt - managerAfter.charge(e.toolAfterUse, repairedDamage, Int.MaxValue, true, false) - } - case _ => - } - } - - def getDurability(stack: ItemStack): Double = { - stack.getItem match { - case item: ISpecialElectricItem => item.getManager(stack).getCharge(stack) / item.getManager(stack).getMaxCharge(stack) - case item: IElectricItem => ElectricItem.manager.getCharge(stack) / item.getMaxCharge(stack) - case _ => Double.NaN - } - } - - def useWrench(player: EntityPlayer, pos: BlockPos, changeDurability: Boolean): Boolean = { - player.getHeldItemMainhand.getItem match { - case wrench: ItemToolWrench => - if (changeDurability) { - wrench.damage(player.getHeldItemMainhand, 1, player) - true - } - else wrench.canTakeDamage(player.getHeldItemMainhand, 1) - case _ => false - } - } - - def isWrench(stack: ItemStack): Boolean = stack.getItem.isInstanceOf[ItemToolWrench] - - def canCharge(stack: ItemStack): Boolean = stack.getItem match { - case chargeable: IElectricItem => chargeable.getMaxCharge(stack) > 0 - case _ => false - } - - def charge(stack: ItemStack, amount: Double, simulate: Boolean): Double = { - (stack.getItem match { - case item: ISpecialElectricItem => Option(item.getManager(stack)) - case item: IElectricItem => Option(ElectricItem.manager) - case _ => None - }) match { - case Some(manager) => amount - Power.fromEU(manager.charge(stack, Power.toEU(amount), Int.MaxValue, true, false)) - case _ => amount - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala b/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala deleted file mode 100644 index bd93491027..0000000000 --- a/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala +++ /dev/null @@ -1,43 +0,0 @@ -package li.cil.oc.integration.ic2 - -import li.cil.oc.api -import li.cil.oc.api.Driver -import li.cil.oc.api.prefab.DriverSidedTileEntity -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods -import net.minecraftforge.common.MinecraftForge - -object ModIndustrialCraft2 extends ModProxy { - override def getMod = Mods.IndustrialCraft2 - - def tryAddDriver(driver: DriverSidedTileEntity): Unit = { - try { - if (driver.getTileEntityClass != null) - Driver.add(driver) - } catch { - case _: Exception => - } - } - - override def initialize() { - api.IMC.registerToolDurabilityProvider("li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.getDurability") - api.IMC.registerWrenchTool("li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.useWrench") - api.IMC.registerWrenchToolCheck("li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.isWrench") - api.IMC.registerItemCharge( - "IndustrialCraft2", - "li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.canCharge", - "li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.charge") - - MinecraftForge.EVENT_BUS.register(EventHandlerIndustrialCraft2) - - tryAddDriver(new DriverReactorRedstonePort) - tryAddDriver(new DriverMassFab) - - Driver.add(new DriverEnergyConductor) - Driver.add(new DriverEnergy) - Driver.add(new DriverReactor) - Driver.add(new DriverReactorChamber) - - Driver.add(new ConverterElectricItem) - } -} diff --git a/src/main/scala/li/cil/oc/integration/mekanism/gas/ConverterGasStack.scala b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala similarity index 93% rename from src/main/scala/li/cil/oc/integration/mekanism/gas/ConverterGasStack.scala rename to src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala index dd7a1e0561..a2a78094ec 100644 --- a/src/main/scala/li/cil/oc/integration/mekanism/gas/ConverterGasStack.scala +++ b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala @@ -1,4 +1,4 @@ -package li.cil.oc.integration.mekanism.gas +package li.cil.oc.integration.mekanism import java.util diff --git a/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala b/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala index fd312da907..35b9ddc816 100644 --- a/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala +++ b/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala @@ -10,5 +10,6 @@ object ModMekanism extends ModProxy { override def initialize(): Unit = { api.IMC.registerWrenchTool("li.cil.oc.integration.mekanism.EventHandlerMekanism.useWrench") api.IMC.registerWrenchToolCheck("li.cil.oc.integration.mekanism.EventHandlerMekanism.isWrench") + api.Driver.add(ConverterGasStack) } } diff --git a/src/main/scala/li/cil/oc/integration/mekanism/gas/ModMekanismGas.scala b/src/main/scala/li/cil/oc/integration/mekanism/gas/ModMekanismGas.scala deleted file mode 100644 index ec52e082ba..0000000000 --- a/src/main/scala/li/cil/oc/integration/mekanism/gas/ModMekanismGas.scala +++ /dev/null @@ -1,14 +0,0 @@ -package li.cil.oc.integration.mekanism.gas - -import li.cil.oc.api.Driver -import li.cil.oc.integration.Mod -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods - -object ModMekanismGas extends ModProxy { - override def getMod: Mod = Mods.MekanismGas - - override def initialize(): Unit = { - Driver.add(ConverterGasStack) - } -} diff --git a/src/main/scala/li/cil/oc/integration/thaumcraft/ConverterThaumcraftItems.scala b/src/main/scala/li/cil/oc/integration/thaumcraft/ConverterThaumcraftItems.scala deleted file mode 100644 index 6adf8baffc..0000000000 --- a/src/main/scala/li/cil/oc/integration/thaumcraft/ConverterThaumcraftItems.scala +++ /dev/null @@ -1,47 +0,0 @@ -package li.cil.oc.integration.thaumcraft - -import java.util - -import li.cil.oc.api.driver.Converter -import net.minecraft.item.{Item, ItemStack} -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.common.util.Constants.NBT - -import scala.collection.convert.WrapAsScala._ -import scala.collection.mutable - -object ConverterThaumcraftItems extends Converter { - override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { - case stack: ItemStack => - val name = Item.REGISTRY.getNameForObject(stack.getItem).toString - - // Handle essentia/vis contents for Thaumcraft jars, phials, and crystals - if ((name == "thaumcraft:jar_normal") || - (name == "thaumcraft:jar_void") || - (name == "thaumcraft:phial") || - (name == "thaumcraft:crystal_essence")) { - if (stack.hasTagCompound && - stack.getTagCompound.hasKey("Aspects", NBT.TAG_LIST)) { - val aspects = mutable.ArrayBuffer.empty[mutable.Map[String, Any]] - val nbtAspects = stack.getTagCompound.getTagList("Aspects", NBT.TAG_COMPOUND).map { - case tag: NBTTagCompound => tag - } - for (nbtAspect <- nbtAspects) { - val key = nbtAspect.getString("key") - val amount = nbtAspect.getInteger("amount") - val aspect = mutable.Map[String, Any]( - "aspect" -> key, - "amount" -> amount - ) - aspects += aspect - } - output += "aspects" -> aspects - } - if (stack.hasTagCompound && stack.getTagCompound.hasKey("AspectFilter", NBT.TAG_STRING)) { - output += "aspectFilter" -> stack.getTagCompound.getString("AspectFilter") - } - } - - case _ => - } -} diff --git a/src/main/scala/li/cil/oc/integration/thaumcraft/ModThaumcraft.scala b/src/main/scala/li/cil/oc/integration/thaumcraft/ModThaumcraft.scala deleted file mode 100644 index 4e1954adfc..0000000000 --- a/src/main/scala/li/cil/oc/integration/thaumcraft/ModThaumcraft.scala +++ /dev/null @@ -1,13 +0,0 @@ -package li.cil.oc.integration.thaumcraft - -import li.cil.oc.api.Driver -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods - -object ModThaumcraft extends ModProxy { - override def getMod: Mods.ModBase = Mods.Thaumcraft - - override def initialize() { - Driver.add(ConverterThaumcraftItems) - } -} diff --git a/src/main/scala/li/cil/oc/integration/wrcbe/ModWRCBE.scala b/src/main/scala/li/cil/oc/integration/wrcbe/ModWRCBE.scala deleted file mode 100644 index 4bf9de59d8..0000000000 --- a/src/main/scala/li/cil/oc/integration/wrcbe/ModWRCBE.scala +++ /dev/null @@ -1,13 +0,0 @@ -package li.cil.oc.integration.wrcbe - -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.WirelessRedstone - -object ModWRCBE extends ModProxy { - override def getMod: Mods.SimpleMod = Mods.WirelessRedstoneCBE - - override def initialize() { - WirelessRedstone.systems += WirelessRedstoneCBE - } -} diff --git a/src/main/scala/li/cil/oc/integration/wrcbe/WirelessRedstoneCBE.scala b/src/main/scala/li/cil/oc/integration/wrcbe/WirelessRedstoneCBE.scala deleted file mode 100644 index 5ee787d48c..0000000000 --- a/src/main/scala/li/cil/oc/integration/wrcbe/WirelessRedstoneCBE.scala +++ /dev/null @@ -1,43 +0,0 @@ -package li.cil.oc.integration.wrcbe - -import codechicken.wirelessredstone.manager.RedstoneEther -import li.cil.oc.integration.util.WirelessRedstone.WirelessRedstoneSystem -import li.cil.oc.server.component.RedstoneWireless - -import scala.language.reflectiveCalls - -object WirelessRedstoneCBE extends WirelessRedstoneSystem { - def addTransmitter(rs: RedstoneWireless) { - if (rs.wirelessOutput && rs.wirelessFrequency > 0) { - RedstoneEther.server.addTransmittingDevice(rs) - } - } - - def removeTransmitter(rs: RedstoneWireless) { - if (rs.wirelessFrequency > 0) { - RedstoneEther.server.removeTransmittingDevice(rs) - } - } - - def addReceiver(rs: RedstoneWireless) { - RedstoneEther.server.addReceivingDevice(rs) - if (rs.wirelessFrequency > 0) { - rs.wirelessInput = RedstoneEther.server.isFreqOn(rs.wirelessFrequency) - } - } - - def removeReceiver(rs: RedstoneWireless) { - RedstoneEther.server.removeReceivingDevice(rs) - } - - def updateOutput(rs: RedstoneWireless) { - if (rs.wirelessOutput) { - addTransmitter(rs) - } - else { - removeTransmitter(rs) - } - } - - def getInput(rs: RedstoneWireless): Boolean = rs.wirelessInput -} diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index 5b85bb8c02..e2d384ee5a 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -1,8 +1,5 @@ package li.cil.oc.server.component -import codechicken.lib.vec.Vector3 -import codechicken.wirelessredstone.api.WirelessReceivingDevice -import codechicken.wirelessredstone.api.WirelessTransmittingDevice import li.cil.oc.Constants import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass @@ -22,10 +19,6 @@ import net.minecraftforge.fml.common.Optional import scala.collection.convert.WrapAsJava._ -@Optional.InterfaceList(Array( - new Optional.Interface(iface = "codechicken.wirelessredstone.api.WirelessReceivingDevice", modid = Mods.IDs.WirelessRedstoneCBE), - new Optional.Interface(iface = "codechicken.wirelessredstone.api.WirelessTransmittingDevice", modid = Mods.IDs.WirelessRedstoneCBE) -)) trait RedstoneWireless extends RedstoneSignaller with WirelessReceivingDevice with WirelessTransmittingDevice with DeviceInfo { def redstone: EnvironmentHost @@ -102,28 +95,6 @@ trait RedstoneWireless extends RedstoneSignaller with WirelessReceivingDevice wi // ----------------------------------------------------------------------- // - @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def updateDevice(frequency: Int, on: Boolean) { - if (frequency == wirelessFrequency && on != wirelessInput) { - wirelessInput = on - onRedstoneChanged(RedstoneChangedEventArgs(null, if (on) 0 else 1, if (on) 1 else 0)) - } - } - - @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getTransmitPos = new Vector3(redstone.xPosition, redstone.yPosition, redstone.zPosition) - - @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getDimension: Int = redstone.world.provider.getDimension - - @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getFreq: Int = wirelessFrequency - - @Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE) - override def getAttachedEntity = null - - // ----------------------------------------------------------------------- // - override def onConnect(node: Node) { super.onConnect(node) if (node == this.node) { From 05587c37149a24be3344b134aa3c508ae7b5e9f7 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 02:55:22 +0200 Subject: [PATCH 004/159] Upgrade code to Minecraft 1.16.5 Includes renaming and ports of removed functionality --- src/main/java/li/cil/oc/api/CreativeTab.java | 4 +- src/main/java/li/cil/oc/api/Driver.java | 8 +- src/main/java/li/cil/oc/api/IMC.java | 124 ++--- src/main/java/li/cil/oc/api/Items.java | 4 +- src/main/java/li/cil/oc/api/Manual.java | 4 +- src/main/java/li/cil/oc/api/Nanomachines.java | 10 +- src/main/java/li/cil/oc/api/Network.java | 14 +- src/main/java/li/cil/oc/api/Persistable.java | 8 +- .../cil/oc/api/component/RackMountable.java | 10 +- .../li/cil/oc/api/component/package-info.java | 4 - .../java/li/cil/oc/api/detail/DriverAPI.java | 8 +- .../java/li/cil/oc/api/detail/ItemAPI.java | 4 +- .../java/li/cil/oc/api/detail/ManualAPI.java | 4 +- .../li/cil/oc/api/detail/NanomachinesAPI.java | 10 +- .../java/li/cil/oc/api/detail/NetworkAPI.java | 14 +- .../li/cil/oc/api/driver/DriverBlock.java | 6 +- .../java/li/cil/oc/api/driver/DriverItem.java | 4 +- .../cil/oc/api/driver/InventoryProvider.java | 8 +- .../li/cil/oc/api/driver/MethodWhitelist.java | 2 +- .../java/li/cil/oc/api/driver/NamedBlock.java | 2 +- .../oc/api/driver/item/UpgradeRenderer.java | 7 +- .../cil/oc/api/driver/item/package-info.java | 4 - .../li/cil/oc/api/driver/package-info.java | 4 - .../oc/api/event/FileSystemAccessEvent.java | 32 +- .../li/cil/oc/api/event/GeolyzerEvent.java | 4 +- .../oc/api/event/NetworkActivityEvent.java | 30 +- .../api/event/RackMountableRenderEvent.java | 80 ++- .../cil/oc/api/event/RobotAnalyzeEvent.java | 6 +- .../oc/api/event/RobotAttackEntityEvent.java | 2 +- .../oc/api/event/RobotBreakBlockEvent.java | 2 +- .../java/li/cil/oc/api/event/RobotEvent.java | 2 +- .../li/cil/oc/api/event/RobotMoveEvent.java | 12 +- .../oc/api/event/RobotPlaceBlockEvent.java | 2 +- .../li/cil/oc/api/event/RobotRenderEvent.java | 6 +- .../li/cil/oc/api/event/SignChangeEvent.java | 14 +- .../li/cil/oc/api/event/package-info.java | 4 - .../java/li/cil/oc/api/fs/package-info.java | 4 - .../java/li/cil/oc/api/internal/Agent.java | 4 +- .../java/li/cil/oc/api/internal/Drone.java | 8 +- .../java/li/cil/oc/api/internal/Keyboard.java | 4 +- .../java/li/cil/oc/api/internal/Rack.java | 4 +- .../java/li/cil/oc/api/internal/Robot.java | 6 +- .../li/cil/oc/api/internal/Rotatable.java | 8 +- .../java/li/cil/oc/api/internal/Tablet.java | 4 +- .../li/cil/oc/api/internal/TextBuffer.java | 33 +- .../java/li/cil/oc/api/internal/Wrench.java | 4 +- .../li/cil/oc/api/internal/package-info.java | 4 - .../li/cil/oc/api/machine/Architecture.java | 8 +- .../java/li/cil/oc/api/machine/Context.java | 4 +- .../li/cil/oc/api/machine/package-info.java | 4 - .../li/cil/oc/api/manual/ImageRenderer.java | 9 +- .../li/cil/oc/api/manual/TabIconRenderer.java | 6 +- .../li/cil/oc/api/manual/package-info.java | 4 - .../oc/api/nanomachines/BehaviorProvider.java | 12 +- .../li/cil/oc/api/network/Analyzable.java | 6 +- .../li/cil/oc/api/network/Environment.java | 12 +- .../java/li/cil/oc/api/network/Network.java | 4 +- .../java/li/cil/oc/api/network/Packet.java | 4 +- .../li/cil/oc/api/network/SidedComponent.java | 4 +- .../cil/oc/api/network/SidedEnvironment.java | 12 +- .../cil/oc/api/network/SimpleComponent.java | 5 +- .../li/cil/oc/api/network/package-info.java | 4 - src/main/java/li/cil/oc/api/package-info.java | 6 +- .../cil/oc/api/prefab/AbstractBehavior.java | 6 +- .../prefab/AbstractManagedEnvironment.java | 20 +- .../cil/oc/api/prefab/AbstractProvider.java | 16 +- .../li/cil/oc/api/prefab/AbstractValue.java | 6 +- .../java/li/cil/oc/api/prefab/DriverItem.java | 17 +- .../cil/oc/api/prefab/DriverSidedBlock.java | 34 +- .../oc/api/prefab/DriverSidedTileEntity.java | 8 +- .../oc/api/prefab/ItemStackArrayValue.java | 42 +- .../api/prefab/ItemStackTabIconRenderer.java | 20 +- .../api/prefab/ResourceContentProvider.java | 2 +- .../oc/api/prefab/TextureTabIconRenderer.java | 23 +- .../oc/api/prefab/TileEntityEnvironment.java | 40 +- .../prefab/TileEntitySidedEnvironment.java | 49 +- .../li/cil/oc/api/prefab/package-info.java | 4 - .../asm/SimpleComponentTickHandler.java | 4 +- .../asm/template/SimpleComponentImpl.java | 12 +- .../asm/template/SimpleEnvironment.java | 40 +- .../asm/template/StaticSimpleEnvironment.java | 21 +- src/main/java/li/cil/oc/util/SideTracker.java | 4 +- src/main/resources/META-INF/oc_at.cfg | 22 +- .../assets/opencomputers/sounds/preload.cfg | 16 - src/main/scala/li/cil/oc/CreativeTab.scala | 8 +- src/main/scala/li/cil/oc/Localization.scala | 91 +-- src/main/scala/li/cil/oc/OpenComputers.scala | 74 +-- src/main/scala/li/cil/oc/Settings.scala | 18 +- .../scala/li/cil/oc/client/ColorHandler.scala | 48 +- .../li/cil/oc/client/CommandHandler.scala | 32 -- .../li/cil/oc/client/ComponentTracker.scala | 2 +- .../scala/li/cil/oc/client/GuiHandler.scala | 87 ++- .../scala/li/cil/oc/client/KeyBindings.scala | 42 +- src/main/scala/li/cil/oc/client/Manual.scala | 13 +- .../li/cil/oc/client/PacketHandler.scala | 256 ++++----- .../scala/li/cil/oc/client/PacketSender.scala | 22 +- src/main/scala/li/cil/oc/client/Proxy.scala | 85 +-- src/main/scala/li/cil/oc/client/Sound.scala | 163 ++---- .../scala/li/cil/oc/client/Textures.scala | 77 ++- .../scala/li/cil/oc/client/gui/Adapter.scala | 12 +- .../li/cil/oc/client/gui/Assembler.scala | 75 +-- .../scala/li/cil/oc/client/gui/Case.scala | 50 +- .../scala/li/cil/oc/client/gui/Charger.scala | 12 +- .../oc/client/gui/CustomGuiContainer.scala | 109 ++-- .../scala/li/cil/oc/client/gui/Database.scala | 26 +- .../li/cil/oc/client/gui/Disassembler.scala | 24 +- .../li/cil/oc/client/gui/DiskDrive.scala | 14 +- .../scala/li/cil/oc/client/gui/Drive.scala | 69 ++- .../scala/li/cil/oc/client/gui/Drone.scala | 102 ++-- .../oc/client/gui/DynamicGuiContainer.scala | 164 +++--- .../li/cil/oc/client/gui/ImageButton.scala | 85 +-- .../scala/li/cil/oc/client/gui/Manual.scala | 146 ++--- .../scala/li/cil/oc/client/gui/Printer.scala | 47 +- .../scala/li/cil/oc/client/gui/Rack.scala | 155 +++--- .../scala/li/cil/oc/client/gui/Raid.scala | 25 +- .../scala/li/cil/oc/client/gui/Relay.scala | 69 +-- .../scala/li/cil/oc/client/gui/Robot.scala | 193 +++---- .../scala/li/cil/oc/client/gui/Screen.scala | 67 ++- .../scala/li/cil/oc/client/gui/Server.scala | 62 +-- .../scala/li/cil/oc/client/gui/Tablet.scala | 15 +- .../scala/li/cil/oc/client/gui/Waypoint.scala | 109 ++-- .../oc/client/gui/traits/DisplayBuffer.scala | 23 +- .../oc/client/gui/traits/InputBuffer.scala | 152 +++--- .../oc/client/gui/traits/LockedHotbar.scala | 17 +- .../li/cil/oc/client/gui/traits/Window.scala | 47 +- .../oc/client/gui/widget/ProgressBar.scala | 19 +- .../li/cil/oc/client/gui/widget/Widget.scala | 5 +- .../client/gui/widget/WidgetContainer.scala | 9 +- .../client/renderer/HighlightRenderer.scala | 296 +++++----- .../client/renderer/MFUTargetRenderer.scala | 148 ++--- .../cil/oc/client/renderer/PetRenderer.scala | 83 +-- .../renderer/TextBufferRenderCache.scala | 28 +- .../WirelessNetworkDebugRenderer.scala | 87 +-- .../oc/client/renderer/block/CableModel.scala | 142 ++--- .../oc/client/renderer/block/DroneModel.scala | 32 +- .../renderer/block/ModelInitialization.scala | 181 +++--- .../renderer/block/NetSplitterModel.scala | 94 ++-- .../oc/client/renderer/block/PrintModel.scala | 55 +- .../oc/client/renderer/block/RobotModel.scala | 39 +- .../client/renderer/block/ScreenModel.scala | 116 ++-- .../renderer/block/ServerRackModel.scala | 94 ++-- .../renderer/block/SmartBlockModelBase.scala | 135 ++--- .../renderer/entity/DroneRenderer.scala | 44 +- .../renderer/entity/ModelQuadcopter.scala | 229 +++----- .../renderer/font/DynamicFontRenderer.scala | 41 +- .../client/renderer/font/FontParserHex.java | 2 +- .../renderer/font/StaticFontRenderer.scala | 30 +- .../renderer/font/TextureFontRenderer.scala | 61 ++- .../client/renderer/gui/BufferRenderer.scala | 14 +- .../renderer/item/HoverBootRenderer.scala | 150 +++-- .../renderer/item/UpgradeRenderer.scala | 77 +-- .../client/renderer/markdown/Document.scala | 70 ++- .../markdown/segment/BasicTextSegment.scala | 2 +- .../markdown/segment/CodeSegment.scala | 9 +- .../markdown/segment/LinkSegment.scala | 3 +- .../markdown/segment/RenderSegment.scala | 52 +- .../renderer/markdown/segment/Segment.scala | 3 +- .../markdown/segment/TextSegment.scala | 19 +- .../segment/render/BlockImageProvider.scala | 9 +- .../segment/render/ItemImageProvider.scala | 10 +- .../render/ItemStackImageRenderer.scala | 19 +- .../segment/render/OreDictImageProvider.scala | 16 +- .../segment/render/TextureImageRenderer.scala | 55 +- .../renderer/tileentity/AdapterRenderer.scala | 98 ++-- .../tileentity/AssemblerRenderer.scala | 58 +- .../renderer/tileentity/CaseRenderer.scala | 58 +- .../renderer/tileentity/ChargerRenderer.scala | 76 +-- .../tileentity/DisassemblerRenderer.scala | 79 +-- .../tileentity/DiskDriveRenderer.scala | 79 +-- .../tileentity/GeolyzerRenderer.scala | 41 +- .../tileentity/HologramRenderer.scala | 97 ++-- .../tileentity/HologramRendererFallback.scala | 24 +- .../tileentity/MicrocontrollerRenderer.scala | 60 +- .../tileentity/NetSplitterRenderer.scala | 98 ++-- .../tileentity/PowerDistributorRenderer.scala | 77 +-- .../renderer/tileentity/PrinterRenderer.scala | 39 +- .../renderer/tileentity/RackRenderer.scala | 46 +- .../renderer/tileentity/RaidRenderer.scala | 64 ++- .../renderer/tileentity/RelayRenderer.scala | 63 ++- .../renderer/tileentity/RobotRenderer.scala | 353 ++++++------ .../renderer/tileentity/ScreenRenderer.scala | 127 +++-- .../tileentity/TransposerRenderer.scala | 91 +-- .../scala/li/cil/oc/common/Achievement.scala | 14 +- .../li/cil/oc/common/ComponentTracker.scala | 11 +- .../scala/li/cil/oc/common/EventHandler.scala | 219 +++++--- .../scala/li/cil/oc/common/GuiHandler.scala | 65 +-- src/main/scala/li/cil/oc/common/IMC.scala | 93 ++-- src/main/scala/li/cil/oc/common/Loot.scala | 70 +-- .../li/cil/oc/common/PacketBuilder.scala | 60 +- .../li/cil/oc/common/PacketHandler.scala | 99 ++-- src/main/scala/li/cil/oc/common/Proxy.scala | 147 ++--- .../scala/li/cil/oc/common/SaveHandler.scala | 62 ++- .../oc/common/ToolDurabilityProviders.scala | 2 +- .../cil/oc/common/asm/ClassTransformer.scala | 415 -------------- .../li/cil/oc/common/asm/Injectable.java | 50 -- .../li/cil/oc/common/block/Adapter.scala | 42 +- .../li/cil/oc/common/block/Assembler.scala | 18 +- .../scala/li/cil/oc/common/block/Cable.scala | 160 ++---- .../li/cil/oc/common/block/Capacitor.scala | 28 +- .../oc/common/block/CarpetedCapacitor.scala | 4 +- .../scala/li/cil/oc/common/block/Case.scala | 50 +- .../cil/oc/common/block/ChameliumBlock.scala | 43 +- .../li/cil/oc/common/block/Charger.scala | 36 +- .../li/cil/oc/common/block/Disassembler.scala | 17 +- .../li/cil/oc/common/block/DiskDrive.scala | 55 +- .../li/cil/oc/common/block/FakeEndstone.scala | 9 +- .../li/cil/oc/common/block/Geolyzer.scala | 3 +- .../li/cil/oc/common/block/Hologram.scala | 39 +- .../scala/li/cil/oc/common/block/Item.scala | 81 +-- .../li/cil/oc/common/block/Keyboard.scala | 127 +++-- .../cil/oc/common/block/Microcontroller.scala | 62 +-- .../li/cil/oc/common/block/MotionSensor.scala | 3 +- .../li/cil/oc/common/block/NetSplitter.scala | 47 +- .../cil/oc/common/block/PowerConverter.scala | 3 +- .../oc/common/block/PowerDistributor.scala | 3 +- .../scala/li/cil/oc/common/block/Print.scala | 156 +++--- .../li/cil/oc/common/block/Printer.scala | 18 +- .../scala/li/cil/oc/common/block/Rack.scala | 113 +--- .../scala/li/cil/oc/common/block/Raid.scala | 56 +- .../li/cil/oc/common/block/Redstone.scala | 15 +- .../cil/oc/common/block/RedstoneAware.scala | 32 +- .../scala/li/cil/oc/common/block/Relay.scala | 3 +- .../cil/oc/common/block/RobotAfterimage.scala | 85 ++- .../li/cil/oc/common/block/RobotProxy.scala | 154 +++--- .../scala/li/cil/oc/common/block/Screen.scala | 126 ++--- .../li/cil/oc/common/block/SimpleBlock.scala | 192 ++++--- .../li/cil/oc/common/block/Transposer.scala | 12 +- .../li/cil/oc/common/block/Waypoint.scala | 57 +- .../block/property/PropertyRotatable.scala | 10 +- .../block/property/PropertyRunning.scala | 4 +- .../common/block/property/PropertyTile.scala | 19 - .../block/property/UnlistedInteger.scala | 13 - .../oc/common/block/traits/CustomDrops.scala | 32 +- .../li/cil/oc/common/block/traits/GUI.scala | 14 +- .../common/block/traits/PowerAcceptor.scala | 16 +- .../oc/common/block/traits/StateAware.scala | 8 +- .../capabilities/CapabilityColored.scala | 34 +- .../capabilities/CapabilityEnvironment.scala | 36 +- .../CapabilitySidedComponent.scala | 24 +- .../CapabilitySidedEnvironment.scala | 34 +- .../cil/oc/common/command/SimpleCommand.scala | 23 - .../oc/common/component/GpuTextBuffer.scala | 34 +- .../oc/common/component/TerminalServer.scala | 71 ++- .../cil/oc/common/component/TextBuffer.scala | 193 ++++--- .../component/traits/VideoRamDevice.scala | 6 +- .../component/traits/VideoRamRasterizer.scala | 7 +- .../li/cil/oc/common/container/Adapter.scala | 4 +- .../cil/oc/common/container/Assembler.scala | 30 +- .../li/cil/oc/common/container/Case.scala | 25 +- .../li/cil/oc/common/container/Charger.scala | 4 +- .../oc/common/container/ComponentSlot.scala | 42 +- .../li/cil/oc/common/container/Database.scala | 40 +- .../oc/common/container/Disassembler.scala | 10 +- .../cil/oc/common/container/DiskDrive.scala | 4 +- .../li/cil/oc/common/container/Drone.scala | 20 +- .../container/DynamicComponentSlot.scala | 16 +- .../li/cil/oc/common/container/Player.scala | 214 ++++---- .../li/cil/oc/common/container/Printer.scala | 18 +- .../li/cil/oc/common/container/Rack.scala | 23 +- .../li/cil/oc/common/container/Raid.scala | 4 +- .../li/cil/oc/common/container/Relay.scala | 28 +- .../li/cil/oc/common/container/Robot.scala | 76 ++- .../li/cil/oc/common/container/Server.scala | 32 +- .../container/StaticComponentSlot.scala | 16 +- .../li/cil/oc/common/container/Tablet.scala | 10 +- .../scala/li/cil/oc/common/entity/Drone.scala | 413 +++++++------- .../oc/common/event/AngelUpgradeHandler.scala | 2 +- .../oc/common/event/BlockChangeHandler.scala | 44 +- .../event/ChunkloaderUpgradeHandler.scala | 145 +++-- .../event/ExperienceUpgradeHandler.scala | 17 +- .../event/FileSystemAccessHandler.scala | 10 +- .../oc/common/event/HoverBootsHandler.scala | 38 +- .../oc/common/event/NanomachinesHandler.scala | 67 +-- .../common/event/NetworkActivityHandler.scala | 8 +- .../event/RackMountableRenderHandler.scala | 56 +- .../oc/common/event/RobotCommonHandler.scala | 31 +- .../event/WirelessNetworkCardHandler.scala | 2 +- .../scala/li/cil/oc/common/init/Blocks.scala | 95 ++-- .../scala/li/cil/oc/common/init/Items.scala | 265 +++++---- .../common/inventory/ComponentInventory.scala | 30 +- .../common/inventory/DatabaseInventory.scala | 6 +- .../DiskDriveMountableInventory.scala | 6 +- .../cil/oc/common/inventory/Inventory.scala | 36 +- .../oc/common/inventory/InventoryProxy.scala | 50 +- .../common/inventory/ItemStackInventory.scala | 16 +- .../oc/common/inventory/ServerInventory.scala | 10 +- .../oc/common/inventory/SimpleInventory.scala | 45 +- .../scala/li/cil/oc/common/item/APU.scala | 4 +- .../scala/li/cil/oc/common/item/Acid.scala | 39 +- .../li/cil/oc/common/item/Analyzer.scala | 72 ++- .../li/cil/oc/common/item/Chamelium.scala | 31 +- .../li/cil/oc/common/item/ComponentBus.scala | 4 +- .../li/cil/oc/common/item/CustomModel.scala | 12 +- .../li/cil/oc/common/item/DebugCard.scala | 34 +- .../li/cil/oc/common/item/Debugger.scala | 18 +- .../li/cil/oc/common/item/Delegator.scala | 171 +++--- .../oc/common/item/DiskDriveMountable.scala | 12 +- .../scala/li/cil/oc/common/item/Drone.scala | 30 +- .../scala/li/cil/oc/common/item/EEPROM.scala | 16 +- .../li/cil/oc/common/item/FloppyDisk.scala | 27 +- .../li/cil/oc/common/item/HoverBoots.scala | 78 +-- .../li/cil/oc/common/item/InkCartridge.scala | 10 +- .../li/cil/oc/common/item/LinkedCard.scala | 22 +- .../scala/li/cil/oc/common/item/Manual.scala | 32 +- .../li/cil/oc/common/item/Nanomachines.scala | 44 +- .../scala/li/cil/oc/common/item/Present.scala | 22 +- .../scala/li/cil/oc/common/item/Server.scala | 35 +- .../scala/li/cil/oc/common/item/Tablet.scala | 234 ++++---- .../li/cil/oc/common/item/Terminal.scala | 49 +- .../li/cil/oc/common/item/TexturePicker.scala | 20 +- .../cil/oc/common/item/UpgradeBattery.scala | 4 +- .../cil/oc/common/item/UpgradeDatabase.scala | 22 +- .../oc/common/item/UpgradeExperience.scala | 12 +- .../li/cil/oc/common/item/UpgradeMF.scala | 32 +- .../li/cil/oc/common/item/UpgradeTank.scala | 18 +- .../scala/li/cil/oc/common/item/Wrench.scala | 57 +- .../oc/common/item/data/DebugCardData.scala | 20 +- .../cil/oc/common/item/data/DriveData.scala | 24 +- .../cil/oc/common/item/data/DroneData.scala | 12 +- .../oc/common/item/data/HoverBootsData.scala | 10 +- .../li/cil/oc/common/item/data/ItemData.scala | 17 +- .../item/data/MicrocontrollerData.scala | 20 +- .../oc/common/item/data/NanomachineData.scala | 22 +- .../item/data/NavigationUpgradeData.scala | 33 +- .../li/cil/oc/common/item/data/NodeData.scala | 36 +- .../cil/oc/common/item/data/PrintData.scala | 52 +- .../li/cil/oc/common/item/data/RaidData.scala | 22 +- .../cil/oc/common/item/data/RobotData.scala | 40 +- .../cil/oc/common/item/data/TabletData.scala | 34 +- .../cil/oc/common/item/traits/CPULike.scala | 29 +- .../oc/common/item/traits/Chargeable.scala | 22 +- .../cil/oc/common/item/traits/Delegate.scala | 72 ++- .../common/item/traits/FileSystemLike.scala | 44 +- .../cil/oc/common/item/traits/ItemTier.scala | 14 +- .../oc/common/item/traits/SimpleItem.scala | 60 +- .../oc/common/launch/CoreModContainer.scala | 19 - .../oc/common/launch/TransformerLoader.scala | 23 - .../common/nanomachines/ControllerImpl.scala | 85 +-- .../oc/common/nanomachines/Nanomachines.scala | 22 +- .../common/nanomachines/NeuralNetwork.scala | 49 +- .../provider/DisintegrationProvider.scala | 58 +- .../provider/HungryProvider.scala | 16 +- .../provider/MagnetProvider.scala | 32 +- .../provider/ParticleProvider.scala | 56 +- .../provider/PotionProvider.scala | 39 +- .../nanomachines/provider/ScalaProvider.scala | 6 +- .../cil/oc/common/recipe/ColorizeRecipe.scala | 90 --- .../recipe/ContainerItemAwareRecipe.scala | 14 - .../oc/common/recipe/DecolorizeRecipe.scala | 48 -- .../recipe/ExtendedFuzzyShapelessRecipe.scala | 27 - .../cil/oc/common/recipe/ExtendedRecipe.scala | 268 --------- .../recipe/ExtendedShapedOreRecipe.scala | 11 - .../recipe/ExtendedShapelessOreRecipe.scala | 10 - .../common/recipe/LootDiskCyclingRecipe.scala | 53 -- .../li/cil/oc/common/recipe/Recipes.scala | 516 ------------------ .../common/template/AssemblerTemplates.scala | 18 +- .../template/DisassemblerTemplates.scala | 4 +- .../oc/common/template/DroneTemplate.scala | 6 +- .../template/MicrocontrollerTemplate.scala | 4 +- .../template/NavigationUpgradeTemplate.scala | 2 +- .../oc/common/template/RobotTemplate.scala | 4 +- .../oc/common/template/ServerTemplate.scala | 2 +- .../oc/common/template/TabletTemplate.scala | 8 +- .../li/cil/oc/common/template/Template.scala | 14 +- .../common/template/TemplateBlacklist.scala | 10 +- .../li/cil/oc/common/tileentity/Adapter.scala | 83 +-- .../cil/oc/common/tileentity/Assembler.scala | 85 +-- .../li/cil/oc/common/tileentity/Cable.scala | 13 +- .../cil/oc/common/tileentity/Capacitor.scala | 18 +- .../common/tileentity/CarpetedCapacitor.scala | 35 +- .../li/cil/oc/common/tileentity/Case.scala | 47 +- .../li/cil/oc/common/tileentity/Charger.scala | 108 ++-- .../oc/common/tileentity/Disassembler.scala | 87 +-- .../cil/oc/common/tileentity/DiskDrive.scala | 45 +- .../cil/oc/common/tileentity/Geolyzer.scala | 17 +- .../cil/oc/common/tileentity/Hologram.scala | 117 ++-- .../cil/oc/common/tileentity/Keyboard.scala | 41 +- .../common/tileentity/Microcontroller.scala | 99 ++-- .../oc/common/tileentity/MotionSensor.scala | 17 +- .../oc/common/tileentity/NetSplitter.scala | 64 +-- .../oc/common/tileentity/PowerConverter.scala | 15 +- .../common/tileentity/PowerDistributor.scala | 33 +- .../li/cil/oc/common/tileentity/Print.scala | 144 ++--- .../li/cil/oc/common/tileentity/Printer.scala | 103 ++-- .../li/cil/oc/common/tileentity/Rack.scala | 116 ++-- .../li/cil/oc/common/tileentity/Raid.scala | 85 +-- .../cil/oc/common/tileentity/Redstone.scala | 19 +- .../li/cil/oc/common/tileentity/Relay.scala | 62 ++- .../li/cil/oc/common/tileentity/Robot.scala | 395 +++++++------- .../cil/oc/common/tileentity/RobotProxy.scala | 147 ++--- .../li/cil/oc/common/tileentity/Screen.scala | 109 ++-- .../cil/oc/common/tileentity/Transposer.scala | 17 +- .../cil/oc/common/tileentity/Waypoint.scala | 54 +- .../traits/BundledRedstoneAware.scala | 76 +-- .../oc/common/tileentity/traits/Colored.scala | 42 +- .../traits/ComponentInventory.scala | 63 +-- .../common/tileentity/traits/Computer.scala | 76 +-- .../tileentity/traits/Environment.scala | 42 +- .../cil/oc/common/tileentity/traits/Hub.scala | 60 +- .../common/tileentity/traits/Inventory.scala | 34 +- .../tileentity/traits/NotAnalyzable.scala | 6 +- .../common/tileentity/traits/OpenSides.scala | 43 +- .../tileentity/traits/PlayerInputAware.scala | 6 +- .../tileentity/traits/PowerAcceptor.scala | 1 - .../tileentity/traits/PowerBalancer.scala | 6 +- .../tileentity/traits/PowerInformation.scala | 20 +- .../tileentity/traits/RedstoneAware.scala | 78 +-- .../common/tileentity/traits/Rotatable.scala | 112 ++-- .../tileentity/traits/RotatableTile.scala | 57 +- .../tileentity/traits/RotationAware.scala | 6 +- .../common/tileentity/traits/TextBuffer.scala | 32 +- .../common/tileentity/traits/Tickable.scala | 6 +- .../common/tileentity/traits/TileEntity.scala | 77 ++- .../traits/power/AppliedEnergistics2.scala | 56 +- .../tileentity/traits/power/Common.scala | 26 +- .../tileentity/traits/power/Mekanism.scala | 29 - .../scala/li/cil/oc/integration/Mods.scala | 28 +- .../li/cil/oc/integration/appeng/AEUtil.scala | 57 +- .../appeng/ConverterCellInventory.java | 12 +- .../appeng/DriverBlockInterface.scala | 6 +- .../integration/appeng/DriverController.scala | 6 +- .../integration/appeng/DriverExportBus.scala | 11 +- .../integration/appeng/DriverImportBus.scala | 10 +- .../appeng/DriverPartInterface.scala | 24 +- .../integration/appeng/EventHandlerAE2.scala | 10 +- .../oc/integration/appeng/MachineSource.scala | 4 +- .../cil/oc/integration/appeng/ModAppEng.scala | 3 +- .../integration/appeng/NetworkControl.scala | 79 +-- .../computercraft/CallableHelper.java | 2 +- .../ComputerCraftFileSystem.scala | 4 +- .../ComputerCraftWritableFileSystem.scala | 5 +- .../computercraft/ConverterLuaObject.java | 15 +- .../DriverComputerCraftMedia.scala | 14 +- .../computercraft/DriverPeripheral.java | 67 ++- .../computercraft/PeripheralProvider.scala | 12 +- .../computercraft/RelayPeripheral.scala | 13 +- .../enderstorage/DriverFrequencyOwner.java | 15 +- .../integration/jei/CallbackDocHandler.scala | 56 +- .../jei/DrawableAnimatedIcon.scala | 20 +- .../jei/LootDiskCyclingRecipeHandler.scala | 33 -- .../integration/jei/ManualUsageHandler.scala | 74 +-- .../li/cil/oc/integration/jei/ModJEI.scala | 11 +- .../jei/ModPluginOpenComputers.scala | 89 +-- .../oc/integration/jei/RelayGuiHandler.scala | 14 +- .../mekanism/ConverterGasStack.scala | 16 +- .../mekanism/EventHandlerMekanism.scala | 18 - .../oc/integration/mekanism/ModMekanism.scala | 2 - .../minecraft/ConverterFluidStack.scala | 10 +- .../minecraft/ConverterFluidTankInfo.scala | 8 +- .../ConverterFluidTankProperties.scala | 8 +- .../minecraft/ConverterItemStack.scala | 66 +-- .../integration/minecraft/ConverterNBT.scala | 34 +- .../minecraft/ConverterWorld.scala | 22 +- .../minecraft/ConverterWorldProvider.scala | 23 - .../integration/minecraft/DriverBeacon.scala | 31 +- .../minecraft/DriverBrewingStand.scala | 16 +- .../minecraft/DriverCommandBlock.scala | 32 +- .../minecraft/DriverComparator.scala | 14 +- .../minecraft/DriverFluidHandler.java | 20 +- .../minecraft/DriverFluidTank.java | 9 +- .../integration/minecraft/DriverFurnace.scala | 26 +- .../minecraft/DriverInventory.java | 78 +-- .../minecraft/DriverMobSpawner.scala | 18 +- .../minecraft/DriverNoteBlock.scala | 60 +- .../minecraft/DriverRecordPlayer.scala | 30 +- .../minecraft/EventHandlerVanilla.scala | 64 +-- .../integration/minecraft/ModMinecraft.scala | 15 +- .../integration/minecraft/RecipeHandler.scala | 77 --- .../minecraftforge/DriverEnergyStorage.scala | 10 +- .../EventHandlerMinecraftForge.scala | 41 +- .../integration/opencomputers/DriverAPU.scala | 2 +- .../integration/opencomputers/DriverCPU.scala | 16 +- .../opencomputers/DriverDataCard.scala | 2 +- .../opencomputers/DriverDebugCard.scala | 2 +- .../DriverDiskDriveMountable.scala | 9 +- .../opencomputers/DriverEEPROM.scala | 2 +- .../opencomputers/DriverFileSystem.scala | 32 +- .../opencomputers/DriverGeolyzer.scala | 2 +- .../opencomputers/DriverGraphicsCard.scala | 2 +- .../opencomputers/DriverInternetCard.scala | 2 +- .../opencomputers/DriverLinkedCard.scala | 2 +- .../opencomputers/DriverLootDisk.scala | 13 +- .../opencomputers/DriverMotionSensor.scala | 2 +- .../opencomputers/DriverNetworkCard.scala | 2 +- .../opencomputers/DriverRedstoneCard.scala | 2 +- .../opencomputers/DriverServer.scala | 9 +- .../opencomputers/DriverTablet.scala | 30 +- .../opencomputers/DriverTransposer.scala | 2 +- .../opencomputers/DriverUpgradeAngel.scala | 2 +- .../opencomputers/DriverUpgradeBattery.scala | 2 +- .../DriverUpgradeChunkloader.scala | 2 +- .../opencomputers/DriverUpgradeCrafting.scala | 2 +- .../opencomputers/DriverUpgradeDatabase.scala | 6 +- .../DriverUpgradeGenerator.scala | 2 +- .../DriverUpgradeInventoryController.scala | 2 +- .../opencomputers/DriverUpgradeLeash.scala | 2 +- .../opencomputers/DriverUpgradeMF.scala | 23 +- .../DriverUpgradeNavigation.scala | 2 +- .../opencomputers/DriverUpgradePiston.scala | 2 +- .../opencomputers/DriverUpgradeSign.scala | 2 +- .../DriverUpgradeSolarGenerator.scala | 2 +- .../DriverUpgradeStickyPiston.scala | 2 +- .../opencomputers/DriverUpgradeTank.scala | 2 +- .../DriverUpgradeTankController.scala | 2 +- .../DriverUpgradeTractorBeam.scala | 2 +- .../opencomputers/DriverUpgradeTrading.scala | 2 +- .../DriverWirelessNetworkCard.scala | 2 +- .../EnvironmentProviderBlocks.scala | 4 +- .../InventoryProviderDatabase.scala | 8 +- .../InventoryProviderServer.scala | 6 +- .../oc/integration/opencomputers/Item.scala | 33 +- .../opencomputers/ModOpenComputers.scala | 14 +- .../projectred/EventHandlerProjectRed.scala | 7 +- .../projectred/ModProjectRed.scala | 6 +- .../SerialInterfaceProviderAdapter.scala | 45 +- .../oc/integration/util/BundledRedstone.scala | 10 +- .../util/DamageSourceWithRandomCause.scala | 16 +- .../cil/oc/integration/util/ItemSearch.scala | 6 +- .../li/cil/oc/integration/util/Wrench.scala | 11 +- .../integration/waila/BlockDataProvider.scala | 137 +++-- .../cil/oc/integration/waila/ModWaila.scala | 6 +- .../li/cil/oc/server/ComponentTracker.scala | 2 +- .../scala/li/cil/oc/server/GuiHandler.scala | 3 +- .../li/cil/oc/server/PacketHandler.scala | 129 ++--- .../scala/li/cil/oc/server/PacketSender.scala | 83 +-- src/main/scala/li/cil/oc/server/Proxy.scala | 14 - .../cil/oc/server/agent/AgentContainer.scala | 43 -- .../oc/server/agent/FakeNetworkManager.scala | 10 +- .../li/cil/oc/server/agent/Inventory.scala | 101 ++-- .../scala/li/cil/oc/server/agent/Player.scala | 430 ++++++++------- .../PlayerInteractionManagerHelper.scala | 21 +- .../oc/server/command/CommandHandler.scala | 15 - .../command/DebugNanomachinesCommand.scala | 37 -- .../command/DebugWhitelistCommand.scala | 54 -- .../command/LogNanomachinesCommand.scala | 39 -- .../NonDisassemblyAgreementCommand.scala | 49 -- .../command/SendDebugMessageCommand.scala | 27 - .../server/command/SpawnComputerCommand.scala | 101 ---- .../command/WirelessRenderingCommand.scala | 29 - .../li/cil/oc/server/command/package.scala | 31 -- .../li/cil/oc/server/component/Agent.scala | 162 +++--- .../li/cil/oc/server/component/DataCard.scala | 10 +- .../cil/oc/server/component/DebugCard.scala | 479 ++++++++-------- .../server/component/DiskDriveMountable.scala | 66 +-- .../li/cil/oc/server/component/Drive.scala | 23 +- .../li/cil/oc/server/component/Drone.scala | 16 +- .../li/cil/oc/server/component/EEPROM.scala | 20 +- .../cil/oc/server/component/FileSystem.scala | 50 +- .../li/cil/oc/server/component/Geolyzer.scala | 44 +- .../oc/server/component/GraphicsCard.scala | 60 +- .../oc/server/component/InternetCard.scala | 4 +- .../li/cil/oc/server/component/Keyboard.scala | 16 +- .../cil/oc/server/component/LinkedCard.scala | 14 +- .../oc/server/component/MotionSensor.scala | 59 +- .../cil/oc/server/component/NetworkCard.scala | 10 +- .../oc/server/component/RedstoneBundled.scala | 8 +- .../server/component/RedstoneSignaller.scala | 14 +- .../oc/server/component/RedstoneVanilla.scala | 13 +- .../server/component/RedstoneWireless.scala | 21 +- .../li/cil/oc/server/component/Robot.scala | 30 +- .../li/cil/oc/server/component/Server.scala | 81 +-- .../li/cil/oc/server/component/Tablet.scala | 6 +- .../li/cil/oc/server/component/Trade.scala | 110 ++-- .../component/UpgradeBarcodeReader.scala | 28 +- .../server/component/UpgradeChunkloader.scala | 46 +- .../oc/server/component/UpgradeCrafting.scala | 43 +- .../oc/server/component/UpgradeDatabase.scala | 26 +- .../server/component/UpgradeExperience.scala | 30 +- .../server/component/UpgradeGenerator.scala | 79 +-- .../UpgradeInventoryController.scala | 10 +- .../oc/server/component/UpgradeLeash.scala | 44 +- .../cil/oc/server/component/UpgradeMF.scala | 58 +- .../server/component/UpgradeNavigation.scala | 32 +- .../oc/server/component/UpgradePiston.scala | 34 +- .../cil/oc/server/component/UpgradeSign.scala | 52 +- .../component/UpgradeSolarGenerator.scala | 14 +- .../cil/oc/server/component/UpgradeTank.scala | 37 +- .../server/component/UpgradeTractorBeam.scala | 24 +- .../oc/server/component/UpgradeTrading.scala | 16 +- .../component/WirelessNetworkCard.scala | 18 +- .../component/traits/InventoryAnalytics.scala | 11 +- .../component/traits/InventoryAware.scala | 8 +- .../component/traits/InventoryControl.scala | 18 +- .../traits/InventoryWorldControl.scala | 38 +- .../traits/InventoryWorldControlMk2.scala | 10 +- .../traits/ItemInventoryControl.scala | 2 +- .../component/traits/SideRestricted.scala | 4 +- .../server/component/traits/TankControl.scala | 15 +- .../traits/TankInventoryControl.scala | 31 +- .../component/traits/TankWorldControl.scala | 18 +- .../component/traits/WakeMessageAware.scala | 12 +- .../server/component/traits/WorldAware.scala | 64 ++- .../traits/WorldInventoryAnalytics.scala | 11 +- .../component/traits/WorldTankAnalytics.scala | 13 +- .../server/driver/CompoundBlockDriver.scala | 21 +- .../driver/CompoundBlockEnvironment.scala | 20 +- .../li/cil/oc/server/driver/Registry.scala | 14 +- .../scala/li/cil/oc/server/fs/Buffered.scala | 12 +- .../scala/li/cil/oc/server/fs/Capacity.scala | 12 +- .../fs/CompositeReadOnlyFileSystem.scala | 10 +- .../fs/FileOutputStreamFileSystem.scala | 6 +- .../li/cil/oc/server/fs/FileSystem.scala | 26 +- .../oc/server/fs/InputStreamFileSystem.scala | 28 +- .../oc/server/fs/OutputStreamFileSystem.scala | 30 +- .../li/cil/oc/server/fs/ReadOnlyWrapper.scala | 6 +- .../cil/oc/server/fs/VirtualFileSystem.scala | 62 +-- .../oc/server/machine/ArchitectureAPI.scala | 6 +- .../cil/oc/server/machine/ArgumentsImpl.scala | 12 +- .../li/cil/oc/server/machine/Machine.scala | 139 ++--- .../machine/luac/NativeLuaArchitecture.scala | 18 +- .../server/machine/luac/PersistenceAPI.scala | 14 +- .../oc/server/machine/luac/UserdataAPI.scala | 8 +- .../machine/luaj/LuaJLuaArchitecture.scala | 6 +- .../li/cil/oc/server/network/Component.scala | 16 +- .../li/cil/oc/server/network/Connector.scala | 12 +- .../li/cil/oc/server/network/Network.scala | 94 ++-- .../scala/li/cil/oc/server/network/Node.scala | 10 +- .../li/cil/oc/server/network/Waypoints.scala | 34 +- .../oc/server/network/WirelessNetwork.scala | 46 +- src/main/scala/li/cil/oc/util/Audio.scala | 111 ++-- .../scala/li/cil/oc/util/BlockPosition.scala | 24 +- src/main/scala/li/cil/oc/util/Color.scala | 76 +-- .../scala/li/cil/oc/util/ExtendedAABB.scala | 24 +- .../li/cil/oc/util/ExtendedArguments.scala | 41 +- .../scala/li/cil/oc/util/ExtendedBlock.scala | 19 +- .../li/cil/oc/util/ExtendedEnumFacing.scala | 12 +- .../li/cil/oc/util/ExtendedInventory.scala | 6 +- .../scala/li/cil/oc/util/ExtendedNBT.scala | 204 +++---- .../scala/li/cil/oc/util/ExtendedWorld.scala | 75 ++- .../scala/li/cil/oc/util/FluidUtils.scala | 143 ++--- .../scala/li/cil/oc/util/InventoryUtils.scala | 96 ++-- .../scala/li/cil/oc/util/ItemColorizer.scala | 27 +- .../li/cil/oc/util/ItemStackWrapper.scala | 4 +- src/main/scala/li/cil/oc/util/ItemUtils.scala | 68 +-- .../scala/li/cil/oc/util/NbtDataStream.scala | 22 +- .../li/cil/oc/util/OldScaledResolution.java | 37 -- .../scala/li/cil/oc/util/PackedColor.scala | 12 +- .../scala/li/cil/oc/util/PlayerUtils.scala | 24 +- src/main/scala/li/cil/oc/util/Rarity.scala | 5 +- .../scala/li/cil/oc/util/RenderState.scala | 55 +- .../scala/li/cil/oc/util/RotationHelper.scala | 46 +- .../scala/li/cil/oc/util/TextBuffer.scala | 46 +- src/main/scala/li/cil/oc/util/Tooltip.scala | 19 +- .../scala/li/cil/oc/util/UpdateCheck.scala | 5 +- .../li/cil/oc/util/UpgradeExperience.scala | 8 +- 645 files changed, 12708 insertions(+), 14668 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/sounds/preload.cfg delete mode 100644 src/main/scala/li/cil/oc/client/CommandHandler.scala delete mode 100644 src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala delete mode 100644 src/main/scala/li/cil/oc/common/asm/Injectable.java delete mode 100644 src/main/scala/li/cil/oc/common/block/property/PropertyTile.scala delete mode 100644 src/main/scala/li/cil/oc/common/block/property/UnlistedInteger.scala delete mode 100644 src/main/scala/li/cil/oc/common/command/SimpleCommand.scala delete mode 100644 src/main/scala/li/cil/oc/common/launch/CoreModContainer.scala delete mode 100644 src/main/scala/li/cil/oc/common/launch/TransformerLoader.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedFuzzyShapelessRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedShapedOreRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessOreRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala delete mode 100644 src/main/scala/li/cil/oc/common/recipe/Recipes.scala delete mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala delete mode 100644 src/main/scala/li/cil/oc/integration/jei/LootDiskCyclingRecipeHandler.scala delete mode 100644 src/main/scala/li/cil/oc/integration/mekanism/EventHandlerMekanism.scala delete mode 100644 src/main/scala/li/cil/oc/integration/minecraft/ConverterWorldProvider.scala delete mode 100644 src/main/scala/li/cil/oc/integration/minecraft/RecipeHandler.scala delete mode 100644 src/main/scala/li/cil/oc/server/Proxy.scala delete mode 100644 src/main/scala/li/cil/oc/server/agent/AgentContainer.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/CommandHandler.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/DebugNanomachinesCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/DebugWhitelistCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/LogNanomachinesCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/NonDisassemblyAgreementCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/SpawnComputerCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/WirelessRenderingCommand.scala delete mode 100644 src/main/scala/li/cil/oc/server/command/package.scala delete mode 100644 src/main/scala/li/cil/oc/util/OldScaledResolution.java diff --git a/src/main/java/li/cil/oc/api/CreativeTab.java b/src/main/java/li/cil/oc/api/CreativeTab.java index 2454256fe3..219309b60f 100644 --- a/src/main/java/li/cil/oc/api/CreativeTab.java +++ b/src/main/java/li/cil/oc/api/CreativeTab.java @@ -1,6 +1,6 @@ package li.cil.oc.api; -import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.ItemGroup; /** * Allows access to the creative tab used by OpenComputers. @@ -13,7 +13,7 @@ public final class CreativeTab { * not try to access this anyway when OpenComputers isn't * present (don't ship the API in your mod), so don't rely on this! */ - public static CreativeTabs instance = CreativeTabs.REDSTONE; + public static ItemGroup instance = ItemGroup.TAB_REDSTONE; private CreativeTab() { } diff --git a/src/main/java/li/cil/oc/api/Driver.java b/src/main/java/li/cil/oc/api/Driver.java index 4d8fe319cf..90ceb83bd9 100644 --- a/src/main/java/li/cil/oc/api/Driver.java +++ b/src/main/java/li/cil/oc/api/Driver.java @@ -6,9 +6,9 @@ import li.cil.oc.api.driver.DriverItem; import li.cil.oc.api.driver.DriverBlock; import li.cil.oc.api.network.EnvironmentHost; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandler; @@ -124,7 +124,7 @@ public static void add(final InventoryProvider provider) { * @param pos the position of the block. * @return a driver for the block, or null if there is none. */ - public static DriverBlock driverFor(World world, BlockPos pos, EnumFacing side) { + public static DriverBlock driverFor(World world, BlockPos pos, Direction side) { if (API.driver != null) return API.driver.driverFor(world, pos, side); return null; @@ -214,7 +214,7 @@ public static Set> environmentsFor(ItemStack stack) { * @param player the player holding the item. May be null. * @return the IItemHandler implementation interfacing the stack, or null. */ - public static IItemHandler itemHandlerFor(ItemStack stack, EntityPlayer player) { + public static IItemHandler itemHandlerFor(ItemStack stack, PlayerEntity player) { if (API.driver != null) return API.driver.itemHandlerFor(stack, player); return null; diff --git a/src/main/java/li/cil/oc/api/IMC.java b/src/main/java/li/cil/oc/api/IMC.java index 6149544096..11f286e162 100644 --- a/src/main/java/li/cil/oc/api/IMC.java +++ b/src/main/java/li/cil/oc/api/IMC.java @@ -1,10 +1,10 @@ package li.cil.oc.api; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagString; -import net.minecraftforge.fml.common.event.FMLInterModComms; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.StringNBT; +import net.minecraftforge.fml.InterModComms; import org.apache.commons.lang3.tuple.Pair; /** @@ -53,7 +53,7 @@ public final class IMC { * @param callback the callback to register as a filtering method. */ public static void registerAssemblerFilter(final String callback) { - FMLInterModComms.sendMessage(MOD_ID, REGISTER_ASSEMBLER_FILTER, callback); + InterModComms.sendTo(MOD_ID, REGISTER_ASSEMBLER_FILTER, () -> callback); } /** @@ -114,59 +114,59 @@ public static void registerAssemblerFilter(final String callback) { * for the third component slot. Up to nine. */ public static void registerAssemblerTemplate(final String name, final String select, final String validate, final String assemble, final Class host, final int[] containerTiers, final int[] upgradeTiers, final Iterable> componentSlots) { - final NBTTagCompound nbt = new NBTTagCompound(); + final CompoundNBT nbt = new CompoundNBT(); if (name != null) { - nbt.setString("name", name); + nbt.putString("name", name); } - nbt.setString("select", select); - nbt.setString("validate", validate); - nbt.setString("assemble", assemble); + nbt.putString("select", select); + nbt.putString("validate", validate); + nbt.putString("assemble", assemble); if (host != null) { - nbt.setString("hostClass", host.getName()); + nbt.putString("hostClass", host.getName()); } - final NBTTagList containersNbt = new NBTTagList(); + final ListNBT containersNbt = new ListNBT(); if (containerTiers != null) { for (int tier : containerTiers) { - final NBTTagCompound slotNbt = new NBTTagCompound(); - slotNbt.setInteger("tier", tier); - containersNbt.appendTag(slotNbt); + final CompoundNBT slotNbt = new CompoundNBT(); + slotNbt.putInt("tier", tier); + containersNbt.add(slotNbt); } } - if (containersNbt.tagCount() > 0) { - nbt.setTag("containerSlots", containersNbt); + if (containersNbt.size() > 0) { + nbt.put("containerSlots", containersNbt); } - final NBTTagList upgradesNbt = new NBTTagList(); + final ListNBT upgradesNbt = new ListNBT(); if (upgradeTiers != null) { for (int tier : upgradeTiers) { - final NBTTagCompound slotNbt = new NBTTagCompound(); - slotNbt.setInteger("tier", tier); - upgradesNbt.appendTag(slotNbt); + final CompoundNBT slotNbt = new CompoundNBT(); + slotNbt.putInt("tier", tier); + upgradesNbt.add(slotNbt); } } - if (upgradesNbt.tagCount() > 0) { - nbt.setTag("upgradeSlots", upgradesNbt); + if (upgradesNbt.size() > 0) { + nbt.put("upgradeSlots", upgradesNbt); } - final NBTTagList componentsNbt = new NBTTagList(); + final ListNBT componentsNbt = new ListNBT(); if (componentSlots != null) { for (Pair slot : componentSlots) { if (slot == null) { - componentsNbt.appendTag(new NBTTagCompound()); + componentsNbt.add(new CompoundNBT()); } else { - final NBTTagCompound slotNbt = new NBTTagCompound(); - slotNbt.setString("type", slot.getLeft()); - slotNbt.setInteger("tier", slot.getRight()); - componentsNbt.appendTag(slotNbt); + final CompoundNBT slotNbt = new CompoundNBT(); + slotNbt.putString("type", slot.getLeft()); + slotNbt.putInt("tier", slot.getRight()); + componentsNbt.add(slotNbt); } } } - if (componentsNbt.tagCount() > 0) { - nbt.setTag("componentSlots", componentsNbt); + if (componentsNbt.size() > 0) { + nbt.put("componentSlots", componentsNbt); } - FMLInterModComms.sendMessage(MOD_ID, REGISTER_ASSEMBLER_TEMPLATE, nbt); + InterModComms.sendTo(MOD_ID, REGISTER_ASSEMBLER_TEMPLATE, () -> nbt); } /** @@ -203,14 +203,14 @@ public static void registerAssemblerTemplate(final String name, final String sel * ingredients from an item. */ public static void registerDisassemblerTemplate(final String name, final String select, final String disassemble) { - final NBTTagCompound nbt = new NBTTagCompound(); + final CompoundNBT nbt = new CompoundNBT(); if (name != null) { - nbt.setString("name", name); + nbt.putString("name", name); } - nbt.setString("select", select); - nbt.setString("disassemble", disassemble); + nbt.putString("select", select); + nbt.putString("disassemble", disassemble); - FMLInterModComms.sendMessage(MOD_ID, REGISTER_DISASSEMBLER_TEMPLATE, nbt); + InterModComms.sendTo(MOD_ID, REGISTER_DISASSEMBLER_TEMPLATE, () -> nbt); } /** @@ -234,7 +234,7 @@ public static void registerDisassemblerTemplate(final String name, final String * @param callback the callback to register as a durability provider. */ public static void registerToolDurabilityProvider(final String callback) { - FMLInterModComms.sendMessage(MOD_ID, REGISTER_TOOL_DURABILITY_PROVIDER, callback); + InterModComms.sendTo(MOD_ID, REGISTER_TOOL_DURABILITY_PROVIDER, () -> callback); } /** @@ -249,7 +249,7 @@ public static void registerToolDurabilityProvider(final String callback) { *

* Signature of callbacks must be: *

-     * boolean callback(EntityPlayer player, BlockPos pos, boolean changeDurability)
+     * boolean callback(PlayerEntity player, BlockPos pos, boolean changeDurability)
      * 
*

* Callbacks must be declared as packagePath.className.methodName. @@ -258,7 +258,7 @@ public static void registerToolDurabilityProvider(final String callback) { * @param callback the callback to register as a wrench tool handler. */ public static void registerWrenchTool(final String callback) { - FMLInterModComms.sendMessage(MOD_ID, REGISTER_WRENCH_TOOL, callback); + InterModComms.sendTo(MOD_ID, REGISTER_WRENCH_TOOL, () -> callback); } /** @@ -281,7 +281,7 @@ public static void registerWrenchTool(final String callback) { * @param callback the callback to register as a wrench tool tester. */ public static void registerWrenchToolCheck(final String callback) { - FMLInterModComms.sendMessage(MOD_ID, REGISTER_WRENCH_TOOL_CHECK, callback); + InterModComms.sendTo(MOD_ID, REGISTER_WRENCH_TOOL_CHECK, () -> callback); } /** @@ -307,11 +307,11 @@ public static void registerWrenchToolCheck(final String callback) { * @param charge the callback to register for charging items. */ public static void registerItemCharge(final String name, final String canCharge, final String charge) { - final NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("name", name); - nbt.setString("canCharge", canCharge); - nbt.setString("charge", charge); - FMLInterModComms.sendMessage(MOD_ID, REGISTER_ITEM_CHARGE, nbt); + final CompoundNBT nbt = new CompoundNBT(); + nbt.putString("name", name); + nbt.putString("canCharge", canCharge); + nbt.putString("charge", charge); + InterModComms.sendTo(MOD_ID, REGISTER_ITEM_CHARGE, () -> nbt); } /** @@ -335,7 +335,7 @@ public static void registerItemCharge(final String name, final String canCharge, * @param callback the callback to register as an ink provider. */ public static void registerInkProvider(final String callback) { - FMLInterModComms.sendMessage(MOD_ID, REGISTER_INK_PROVIDER, callback); + InterModComms.sendTo(MOD_ID, REGISTER_INK_PROVIDER, () -> callback); } /** @@ -348,7 +348,7 @@ public static void registerInkProvider(final String callback) { * @param peripheral the class of the peripheral to blacklist. */ public static void blacklistPeripheral(final Class peripheral) { - FMLInterModComms.sendMessage(MOD_ID, BLACKLIST_PERIPHERAL, peripheral.getName()); + InterModComms.sendTo(MOD_ID, BLACKLIST_PERIPHERAL, () -> peripheral.getName()); } /** @@ -367,13 +367,13 @@ public static void blacklistPeripheral(final Class peripheral) { * @param stack the item stack representing the blacklisted component. */ public static void blacklistHost(final String name, final Class host, final ItemStack stack) { - final NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("name", name); - nbt.setString("host", host.getName()); - final NBTTagCompound stackNbt = new NBTTagCompound(); - stack.writeToNBT(stackNbt); - nbt.setTag("item", stackNbt); - FMLInterModComms.sendMessage(MOD_ID, BLACKLIST_HOST, nbt); + final CompoundNBT nbt = new CompoundNBT(); + nbt.putString("name", name); + nbt.putString("host", host.getName()); + final CompoundNBT stackNbt = new CompoundNBT(); + stack.save(stackNbt); + nbt.put("item", stackNbt); + InterModComms.sendTo(MOD_ID, BLACKLIST_HOST, () -> nbt); } /** @@ -401,17 +401,17 @@ public static void blacklistHost(final String name, final Class host, final Item * @param architectures the names of the architectures this entry applies to. */ public static void registerProgramDiskLabel(final String programName, final String diskLabel, final String... architectures) { - final NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("program", programName); - nbt.setString("label", diskLabel); + final CompoundNBT nbt = new CompoundNBT(); + nbt.putString("program", programName); + nbt.putString("label", diskLabel); if (architectures != null && architectures.length > 0) { - final NBTTagList architecturesNbt = new NBTTagList(); + final ListNBT architecturesNbt = new ListNBT(); for (final String architecture : architectures) { - architecturesNbt.appendTag(new NBTTagString(architecture)); + architecturesNbt.add(StringNBT.valueOf(architecture)); } - nbt.setTag("architectures", architecturesNbt); + nbt.put("architectures", architecturesNbt); } - FMLInterModComms.sendMessage(MOD_ID, REGISTER_PROGRAM_DISK_LABEL, nbt); + InterModComms.sendTo(MOD_ID, REGISTER_PROGRAM_DISK_LABEL, () -> nbt); } // ----------------------------------------------------------------------- // diff --git a/src/main/java/li/cil/oc/api/Items.java b/src/main/java/li/cil/oc/api/Items.java index ec37582994..673969aef6 100644 --- a/src/main/java/li/cil/oc/api/Items.java +++ b/src/main/java/li/cil/oc/api/Items.java @@ -1,7 +1,7 @@ package li.cil.oc.api; import li.cil.oc.api.detail.ItemInfo; -import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.DyeColor; import net.minecraft.item.ItemStack; import java.util.concurrent.Callable; @@ -72,7 +72,7 @@ public static ItemInfo get(ItemStack stack) { * @return an item stack representing the registered loot disk, to allow * adding a recipe for your loot disk, for example. */ - public static ItemStack registerFloppy(String name, EnumDyeColor color, Callable factory, boolean doRecipeCycling) { + public static ItemStack registerFloppy(String name, DyeColor color, Callable factory, boolean doRecipeCycling) { if (API.items != null) return API.items.registerFloppy(name, color, factory, doRecipeCycling); return ItemStack.EMPTY; diff --git a/src/main/java/li/cil/oc/api/Manual.java b/src/main/java/li/cil/oc/api/Manual.java index 5096202c78..90b6af2d70 100644 --- a/src/main/java/li/cil/oc/api/Manual.java +++ b/src/main/java/li/cil/oc/api/Manual.java @@ -5,7 +5,7 @@ import li.cil.oc.api.manual.ImageRenderer; import li.cil.oc.api.manual.PathProvider; import li.cil.oc.api.manual.TabIconRenderer; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -153,7 +153,7 @@ public static Iterable contentFor(String path) { * * @param player the player to open the manual for. */ - public static void openFor(EntityPlayer player) { + public static void openFor(PlayerEntity player) { if (API.manual != null) API.manual.openFor(player); } diff --git a/src/main/java/li/cil/oc/api/Nanomachines.java b/src/main/java/li/cil/oc/api/Nanomachines.java index afa239b8d4..39a18df84d 100644 --- a/src/main/java/li/cil/oc/api/Nanomachines.java +++ b/src/main/java/li/cil/oc/api/Nanomachines.java @@ -2,7 +2,7 @@ import li.cil.oc.api.nanomachines.BehaviorProvider; import li.cil.oc.api.nanomachines.Controller; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import java.util.Collections; @@ -44,7 +44,7 @@ public static Iterable getProviders() { * @param player the player to check for. * @return true if the player has a controller, false otherwise. */ - public static boolean hasController(EntityPlayer player) { + public static boolean hasController(PlayerEntity player) { if (API.nanomachines != null) return API.nanomachines.hasController(player); return false; @@ -60,7 +60,7 @@ public static boolean hasController(EntityPlayer player) { * @param player the player to get the controller for. * @return the controller for the specified player. */ - public static Controller getController(EntityPlayer player) { + public static Controller getController(PlayerEntity player) { if (API.nanomachines != null) return API.nanomachines.getController(player); return null; @@ -75,7 +75,7 @@ public static Controller getController(EntityPlayer player) { * * @param player the player to install a nanomachine controller for. */ - public static Controller installController(EntityPlayer player) { + public static Controller installController(PlayerEntity player) { if (API.nanomachines != null) return API.nanomachines.installController(player); return null; @@ -88,7 +88,7 @@ public static Controller installController(EntityPlayer player) { * * @param player the player to uninstall a nanomachine controller from. */ - public static void uninstallController(EntityPlayer player) { + public static void uninstallController(PlayerEntity player) { if (API.nanomachines != null) API.nanomachines.uninstallController(player); } diff --git a/src/main/java/li/cil/oc/api/Network.java b/src/main/java/li/cil/oc/api/Network.java index 5db80f8e5c..f306f68560 100644 --- a/src/main/java/li/cil/oc/api/Network.java +++ b/src/main/java/li/cil/oc/api/Network.java @@ -6,10 +6,12 @@ import li.cil.oc.api.network.Packet; import li.cil.oc.api.network.Visibility; import li.cil.oc.api.network.WirelessEndpoint; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.RegistryKey; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.World; /** * This class provides factories for networks and nodes. @@ -31,7 +33,7 @@ */ public final class Network { /** - * Convenience overload for {@link #joinOrCreateNetwork(IBlockAccess, BlockPos)}. + * Convenience overload for {@link #joinOrCreateNetwork(IBlockReader, BlockPos)}. *

* If the tile entity implements {@link Environment} its one node will be * connected to any existing adjacent tile entity nodes. If none exist a @@ -56,7 +58,7 @@ public static void joinOrCreateNetwork(final TileEntity tileEntity) { * @param world the world containing the location to connect. * @param pos the position at which to update the network. */ - public static void joinOrCreateNetwork(final IBlockAccess world, final BlockPos pos) { + public static void joinOrCreateNetwork(final IBlockReader world, final BlockPos pos) { if (API.network != null) API.network.joinOrCreateNetwork(world, pos); } @@ -138,7 +140,7 @@ public static void leaveWirelessNetwork(final WirelessEndpoint endpoint) { * @param endpoint the endpoint to remove from the wireless network. * @param dimension the dimension with the wireless network to remove the endpoint from. */ - public static void leaveWirelessNetwork(final WirelessEndpoint endpoint, final int dimension) { + public static void leaveWirelessNetwork(final WirelessEndpoint endpoint, final RegistryKey dimension) { if (API.network != null) API.network.leaveWirelessNetwork(endpoint, dimension); } @@ -233,7 +235,7 @@ public static Packet newPacket(final String source, final String destination, fi * @param nbt the tag to load the packet from. * @return the loaded packet. */ - public static Packet newPacket(final NBTTagCompound nbt) { + public static Packet newPacket(final CompoundNBT nbt) { if (API.network != null) return API.network.newPacket(nbt); return null; diff --git a/src/main/java/li/cil/oc/api/Persistable.java b/src/main/java/li/cil/oc/api/Persistable.java index 8ad2b2a459..b7bb1ccf93 100644 --- a/src/main/java/li/cil/oc/api/Persistable.java +++ b/src/main/java/li/cil/oc/api/Persistable.java @@ -1,6 +1,6 @@ package li.cil.oc.api; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * An object that can be persisted to an NBT tag and restored back from it. @@ -11,15 +11,15 @@ public interface Persistable { * * @param nbt the tag to read the state from. */ - void load(NBTTagCompound nbt); + void loadData(CompoundNBT nbt); /** * Saves the current state of the object into the specified NBT tag. *

* This should write the state in such a way that it can be restored when - * {@link #load} is called with that tag. + * {@link #loadData} is called with that tag. * * @param nbt the tag to save the state to. */ - void save(NBTTagCompound nbt); + void saveData(CompoundNBT nbt); } \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/component/RackMountable.java b/src/main/java/li/cil/oc/api/component/RackMountable.java index efe4057441..9c4aecce41 100644 --- a/src/main/java/li/cil/oc/api/component/RackMountable.java +++ b/src/main/java/li/cil/oc/api/component/RackMountable.java @@ -4,11 +4,11 @@ import li.cil.oc.api.network.ComponentHost; import li.cil.oc.api.network.ManagedEnvironment; import li.cil.oc.api.util.StateAware; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Hand; /** * Use this interface on environments provided by drivers for items that can @@ -41,7 +41,7 @@ public interface RackMountable extends ManagedEnvironment, StateAware { * * @return the data to synchronize to the clients. */ - NBTTagCompound getData(); + CompoundNBT getData(); /** * The number of connectables exposed by the environment. @@ -70,5 +70,5 @@ public interface RackMountable extends ManagedEnvironment, StateAware { * @param hitY the relative y coordinate of the activation on the mountable. * @return whether the activation was handled (e.g. GUI opened). */ - boolean onActivate(EntityPlayer player, EnumHand hand, ItemStack heldItem, float hitX, float hitY); + boolean onActivate(PlayerEntity player, Hand hand, ItemStack heldItem, float hitX, float hitY); } diff --git a/src/main/java/li/cil/oc/api/component/package-info.java b/src/main/java/li/cil/oc/api/component/package-info.java index b8dc74b7bc..e4fee4451b 100644 --- a/src/main/java/li/cil/oc/api/component/package-info.java +++ b/src/main/java/li/cil/oc/api/component/package-info.java @@ -4,10 +4,6 @@ * This will allow OpenComputers to provide some more advanced integration * with your components. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|component", - apiVersion = API.VERSION) package li.cil.oc.api.component; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/detail/DriverAPI.java b/src/main/java/li/cil/oc/api/detail/DriverAPI.java index 138f60becb..c0bcd02681 100644 --- a/src/main/java/li/cil/oc/api/detail/DriverAPI.java +++ b/src/main/java/li/cil/oc/api/detail/DriverAPI.java @@ -6,9 +6,9 @@ import li.cil.oc.api.driver.DriverItem; import li.cil.oc.api.driver.DriverBlock; import li.cil.oc.api.network.EnvironmentHost; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandler; @@ -95,7 +95,7 @@ public interface DriverAPI { * @return a driver for the block, or null if there is none. */ @Nullable - DriverBlock driverFor(World world, BlockPos pos, EnumFacing side); + DriverBlock driverFor(World world, BlockPos pos, Direction side); /** * Looks up a driver for the specified item stack. @@ -167,7 +167,7 @@ public interface DriverAPI { * @param player the player holding the item. May be null. * @return the IItemHandler implementation interfacing the stack, or null. */ - IItemHandler itemHandlerFor(ItemStack stack, EntityPlayer player); + IItemHandler itemHandlerFor(ItemStack stack, PlayerEntity player); /** * Get a list of all registered item drivers. diff --git a/src/main/java/li/cil/oc/api/detail/ItemAPI.java b/src/main/java/li/cil/oc/api/detail/ItemAPI.java index 579500cf5b..4efea8904d 100644 --- a/src/main/java/li/cil/oc/api/detail/ItemAPI.java +++ b/src/main/java/li/cil/oc/api/detail/ItemAPI.java @@ -1,7 +1,7 @@ package li.cil.oc.api.detail; import li.cil.oc.api.FileSystem; -import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.DyeColor; import net.minecraft.item.ItemStack; import javax.annotation.Nullable; @@ -59,7 +59,7 @@ public interface ItemAPI { * @return an item stack representing the registered loot disk, to allow * adding a recipe for your loot disk, for example. */ - ItemStack registerFloppy(String name, EnumDyeColor color, Callable factory, boolean doRecipeCycling); + ItemStack registerFloppy(String name, DyeColor color, Callable factory, boolean doRecipeCycling); /** * Register a single custom EEPROM. diff --git a/src/main/java/li/cil/oc/api/detail/ManualAPI.java b/src/main/java/li/cil/oc/api/detail/ManualAPI.java index 605955472d..e8bc944494 100644 --- a/src/main/java/li/cil/oc/api/detail/ManualAPI.java +++ b/src/main/java/li/cil/oc/api/detail/ManualAPI.java @@ -5,7 +5,7 @@ import li.cil.oc.api.manual.ImageRenderer; import li.cil.oc.api.manual.PathProvider; import li.cil.oc.api.manual.TabIconRenderer; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -119,7 +119,7 @@ public interface ManualAPI { * * @param player the player to open the manual for. */ - void openFor(EntityPlayer player); + void openFor(PlayerEntity player); /** * Reset the history of the manual. diff --git a/src/main/java/li/cil/oc/api/detail/NanomachinesAPI.java b/src/main/java/li/cil/oc/api/detail/NanomachinesAPI.java index 4a85f5fcf7..3459c3e86c 100644 --- a/src/main/java/li/cil/oc/api/detail/NanomachinesAPI.java +++ b/src/main/java/li/cil/oc/api/detail/NanomachinesAPI.java @@ -2,7 +2,7 @@ import li.cil.oc.api.nanomachines.BehaviorProvider; import li.cil.oc.api.nanomachines.Controller; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; public interface NanomachinesAPI { /** @@ -29,7 +29,7 @@ public interface NanomachinesAPI { * @param player the player to check for. * @return true if the player has a controller, false otherwise. */ - boolean hasController(EntityPlayer player); + boolean hasController(PlayerEntity player); /** * Get the nanomachine controller of the specified player. @@ -41,7 +41,7 @@ public interface NanomachinesAPI { * @param player the player to get the controller for. * @return the controller for the specified player. */ - Controller getController(EntityPlayer player); + Controller getController(PlayerEntity player); /** * Install a controller for the specified player if it doesn't already @@ -53,7 +53,7 @@ public interface NanomachinesAPI { * @param player the player to install a nanomachine controller for. * @return the controller for the specified player. */ - Controller installController(EntityPlayer player); + Controller installController(PlayerEntity player); /** * Uninstall a controller from the specified player if it has one. @@ -62,5 +62,5 @@ public interface NanomachinesAPI { * * @param player the player to uninstall a nanomachine controller from. */ - void uninstallController(EntityPlayer player); + void uninstallController(PlayerEntity player); } diff --git a/src/main/java/li/cil/oc/api/detail/NetworkAPI.java b/src/main/java/li/cil/oc/api/detail/NetworkAPI.java index 967f4f1fd1..b48d129989 100644 --- a/src/main/java/li/cil/oc/api/detail/NetworkAPI.java +++ b/src/main/java/li/cil/oc/api/detail/NetworkAPI.java @@ -5,14 +5,16 @@ import li.cil.oc.api.network.Packet; import li.cil.oc.api.network.Visibility; import li.cil.oc.api.network.WirelessEndpoint; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.RegistryKey; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.World; public interface NetworkAPI { /** - * Convenience overload for {@link #joinOrCreateNetwork(IBlockAccess, BlockPos)}. + * Convenience overload for {@link #joinOrCreateNetwork(IBlockReader, BlockPos)}. *

* If the tile entity implements {@link Environment} its one node will be * connected to any existing adjacent tile entity nodes. If none exist a @@ -34,7 +36,7 @@ public interface NetworkAPI { * @param world the world containing the location to connect. * @param pos the position at which to update the network. */ - void joinOrCreateNetwork(IBlockAccess world, BlockPos pos); + void joinOrCreateNetwork(IBlockReader world, BlockPos pos); /** * Creates a new network with the specified node as its initial node. @@ -101,7 +103,7 @@ public interface NetworkAPI { * @param endpoint the endpoint to remove from the wireless network. * @param dimension the dimension with the wireless network to remove the endpoint from. */ - void leaveWirelessNetwork(WirelessEndpoint endpoint, int dimension); + void leaveWirelessNetwork(WirelessEndpoint endpoint, RegistryKey dimension); /** * Sends a packet via the wireless network. @@ -182,5 +184,5 @@ public interface NetworkAPI { * @param nbt the tag to load the packet from. * @return the loaded packet. */ - Packet newPacket(NBTTagCompound nbt); + Packet newPacket(CompoundNBT nbt); } \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/DriverBlock.java b/src/main/java/li/cil/oc/api/driver/DriverBlock.java index 9dd7891c2c..69fd372c06 100644 --- a/src/main/java/li/cil/oc/api/driver/DriverBlock.java +++ b/src/main/java/li/cil/oc/api/driver/DriverBlock.java @@ -2,7 +2,7 @@ import li.cil.oc.api.network.ManagedEnvironment; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.world.World; /** @@ -44,7 +44,7 @@ public interface DriverBlock { * @param side the side of the block to check. * @return true if the block is supported; false otherwise. */ - boolean worksWith(World world, BlockPos pos, EnumFacing side); + boolean worksWith(World world, BlockPos pos, Direction side); /** * Create a new managed environment interfacing the specified block. @@ -67,5 +67,5 @@ public interface DriverBlock { * @param side the side of the block to check. * @return the environment for the block at that location. */ - ManagedEnvironment createEnvironment(World world, BlockPos pos, EnumFacing side); + ManagedEnvironment createEnvironment(World world, BlockPos pos, Direction side); } \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/DriverItem.java b/src/main/java/li/cil/oc/api/driver/DriverItem.java index 965d658182..98b42d2823 100644 --- a/src/main/java/li/cil/oc/api/driver/DriverItem.java +++ b/src/main/java/li/cil/oc/api/driver/DriverItem.java @@ -2,7 +2,7 @@ import li.cil.oc.api.network.ManagedEnvironment; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * Interface for item component drivers. @@ -110,5 +110,5 @@ public interface DriverItem { * @return the tag to use for saving and loading, or null to use * the default tag oc:data. */ - NBTTagCompound dataTag(ItemStack stack); + CompoundNBT dataTag(ItemStack stack); } \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java index 4308b454a0..e231318a2d 100644 --- a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java +++ b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java @@ -1,6 +1,6 @@ package li.cil.oc.api.driver; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; @@ -13,7 +13,7 @@ * the inventory controller upgrade, for example. *

* Implementations returned by {@link #getInventory} should save changes - * back to the item stack when {@link IInventory#markDirty()} is called. + * back to the item stack when {@link IInventory#setChanged()} is called. * Return null if the specified stack is not supported. */ public interface InventoryProvider { @@ -24,7 +24,7 @@ public interface InventoryProvider { * @param player the player holding the item, may be null. * @return true if the stack is supported, false otherwise. */ - boolean worksWith(ItemStack stack, EntityPlayer player); + boolean worksWith(ItemStack stack, PlayerEntity player); /** * Get an inventory implementation that allows interfacing with the @@ -38,5 +38,5 @@ public interface InventoryProvider { * @param player the player holding the item, may be null. * @return the inventory representing the contents, or null. */ - IInventory getInventory(ItemStack stack, EntityPlayer player); + IInventory getInventory(ItemStack stack, PlayerEntity player); } diff --git a/src/main/java/li/cil/oc/api/driver/MethodWhitelist.java b/src/main/java/li/cil/oc/api/driver/MethodWhitelist.java index 838f5785ab..25cdfb9e5e 100644 --- a/src/main/java/li/cil/oc/api/driver/MethodWhitelist.java +++ b/src/main/java/li/cil/oc/api/driver/MethodWhitelist.java @@ -17,7 +17,7 @@ * suppress inventory functionality if your TileEntity implements IInventory. *

* To do so, implement this interface in the environment that you - * return from your driver's {@link DriverBlock#createEnvironment(net.minecraft.world.World, BlockPos, net.minecraft.util.EnumFacing)} + * return from your driver's {@link DriverBlock#createEnvironment(net.minecraft.world.World, BlockPos, net.minecraft.util.Direction)} * method, and provide the names of the allowed methods from {@link #whitelistedMethods()}. *

* Important: if multiple drivers apply to a single block that each diff --git a/src/main/java/li/cil/oc/api/driver/NamedBlock.java b/src/main/java/li/cil/oc/api/driver/NamedBlock.java index 91796fd1c2..4a869bd10f 100644 --- a/src/main/java/li/cil/oc/api/driver/NamedBlock.java +++ b/src/main/java/li/cil/oc/api/driver/NamedBlock.java @@ -9,7 +9,7 @@ *

* This was previously to be implemented on the driver itself, but that has been * deprecated. Implement it in the environment returned from the block driver's - * {@link DriverBlock#createEnvironment(net.minecraft.world.World, BlockPos, net.minecraft.util.EnumFacing)} + * {@link DriverBlock#createEnvironment(net.minecraft.world.World, BlockPos, net.minecraft.util.Direction)} * method instead. */ public interface NamedBlock { diff --git a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java index 87bc9f1ad0..e3d39ed84c 100644 --- a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java +++ b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java @@ -1,10 +1,11 @@ package li.cil.oc.api.driver.item; +import com.mojang.blaze3d.matrix.MatrixStack; import li.cil.oc.api.event.RobotRenderEvent; import li.cil.oc.api.internal.Robot; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import java.util.Set; @@ -72,7 +73,7 @@ public interface UpgradeRenderer { * @param robot the robot the upgrade is rendered on. * @param pt partial tick time, e.g. for animations. */ - void render(ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot, float pt); + void render(MatrixStack matrix, ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot, float pt); /** * Mount point names for {@link #computePreferredMountPoint}. diff --git a/src/main/java/li/cil/oc/api/driver/item/package-info.java b/src/main/java/li/cil/oc/api/driver/item/package-info.java index 61ba9e400f..ecb06d065a 100644 --- a/src/main/java/li/cil/oc/api/driver/item/package-info.java +++ b/src/main/java/li/cil/oc/api/driver/item/package-info.java @@ -4,10 +4,6 @@ * These interfaces allow specializing item drivers to provide static data, * that is without creating an actual environment. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|driver|item", - apiVersion = API.VERSION) package li.cil.oc.api.driver.item; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/package-info.java b/src/main/java/li/cil/oc/api/driver/package-info.java index 76cca59f09..1e8d4c5d80 100644 --- a/src/main/java/li/cil/oc/api/driver/package-info.java +++ b/src/main/java/li/cil/oc/api/driver/package-info.java @@ -4,10 +4,6 @@ * Drivers are used to add items and third party blocks to the internal network, * which is mostly used to make components wrapping them available to computers. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|driver", - apiVersion = API.VERSION) package li.cil.oc.api.driver; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/event/FileSystemAccessEvent.java b/src/main/java/li/cil/oc/api/event/FileSystemAccessEvent.java index 4a649f7ea2..3e2f9bfb93 100644 --- a/src/main/java/li/cil/oc/api/event/FileSystemAccessEvent.java +++ b/src/main/java/li/cil/oc/api/event/FileSystemAccessEvent.java @@ -1,11 +1,11 @@ package li.cil.oc.api.event; import li.cil.oc.api.network.Node; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; /** * Events for handling file system access and representing it on the client. @@ -33,7 +33,7 @@ public class FileSystemAccessEvent extends Event { protected TileEntity tileEntity; - protected NBTTagCompound data; + protected CompoundNBT data; /** * Constructor for tile entity hosted file systems. @@ -42,12 +42,12 @@ public class FileSystemAccessEvent extends Event { * @param tileEntity the tile entity hosting the file system. * @param data the additional data. */ - protected FileSystemAccessEvent(String sound, TileEntity tileEntity, NBTTagCompound data) { + protected FileSystemAccessEvent(String sound, TileEntity tileEntity, CompoundNBT data) { this.sound = sound; - this.world = tileEntity.getWorld(); - this.x = tileEntity.getPos().getX() + 0.5; - this.y = tileEntity.getPos().getY() + 0.5; - this.z = tileEntity.getPos().getZ() + 0.5; + this.world = tileEntity.getLevel(); + this.x = tileEntity.getBlockPos().getX() + 0.5; + this.y = tileEntity.getBlockPos().getY() + 0.5; + this.z = tileEntity.getBlockPos().getZ() + 0.5; this.tileEntity = tileEntity; this.data = data; } @@ -62,7 +62,7 @@ protected FileSystemAccessEvent(String sound, TileEntity tileEntity, NBTTagCompo * @param z the z coordinate of the file system's container. * @param data the additional data. */ - protected FileSystemAccessEvent(String sound, World world, double x, double y, double z, NBTTagCompound data) { + protected FileSystemAccessEvent(String sound, World world, double x, double y, double z, CompoundNBT data) { this.sound = sound; this.world = world; this.x = x; @@ -113,7 +113,7 @@ public double getZ() { * Important: this can be null, which is usually the * case when the container is an entity or item. */ - public TileEntity getTileEntity() { + public TileEntity getBlockEntity() { return tileEntity; } @@ -121,7 +121,7 @@ public TileEntity getTileEntity() { * Addition custom data, this is used to transmit the number of the server * in a server rack the file system lives in, for example. */ - public NBTTagCompound getData() { + public CompoundNBT getData() { return data; } @@ -129,12 +129,12 @@ public static final class Server extends FileSystemAccessEvent { private Node node; public Server(String sound, TileEntity tileEntity, Node node) { - super(sound, tileEntity, new NBTTagCompound()); + super(sound, tileEntity, new CompoundNBT()); this.node = node; } public Server(String sound, World world, double x, double y, double z, Node node) { - super(sound, world, x, y, z, new NBTTagCompound()); + super(sound, world, x, y, z, new CompoundNBT()); this.node = node; } @@ -154,7 +154,7 @@ public static final class Client extends FileSystemAccessEvent { * @param tileEntity the tile entity hosting the file system. * @param data the additional data. */ - public Client(String sound, TileEntity tileEntity, NBTTagCompound data) { + public Client(String sound, TileEntity tileEntity, CompoundNBT data) { super(sound, tileEntity, data); } @@ -168,7 +168,7 @@ public Client(String sound, TileEntity tileEntity, NBTTagCompound data) { * @param z the z coordinate of the file system's container. * @param data the additional data. */ - public Client(String sound, World world, double x, double y, double z, NBTTagCompound data) { + public Client(String sound, World world, double x, double y, double z, CompoundNBT data) { super(sound, world, x, y, z, data); } } diff --git a/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java b/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java index 1dc5865567..62437d6dc1 100644 --- a/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java +++ b/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java @@ -2,8 +2,8 @@ import li.cil.oc.api.network.EnvironmentHost; import net.minecraft.util.math.BlockPos; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/li/cil/oc/api/event/NetworkActivityEvent.java b/src/main/java/li/cil/oc/api/event/NetworkActivityEvent.java index 918f31fe6d..2e2ae9f762 100644 --- a/src/main/java/li/cil/oc/api/event/NetworkActivityEvent.java +++ b/src/main/java/li/cil/oc/api/event/NetworkActivityEvent.java @@ -1,10 +1,10 @@ package li.cil.oc.api.event; import li.cil.oc.api.network.Node; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.eventbus.api.Event; /** * Events for handling network activity and representing it on the client. @@ -29,7 +29,7 @@ public class NetworkActivityEvent extends Event { protected TileEntity tileEntity; - protected NBTTagCompound data; + protected CompoundNBT data; /** * Constructor for tile entity hosted network cards. @@ -37,11 +37,11 @@ public class NetworkActivityEvent extends Event { * @param tileEntity the tile entity hosting the network card. * @param data the additional data. */ - protected NetworkActivityEvent(TileEntity tileEntity, NBTTagCompound data) { - this.world = tileEntity.getWorld(); - this.x = tileEntity.getPos().getX() + 0.5; - this.y = tileEntity.getPos().getY() + 0.5; - this.z = tileEntity.getPos().getZ() + 0.5; + protected NetworkActivityEvent(TileEntity tileEntity, CompoundNBT data) { + this.world = tileEntity.getLevel(); + this.x = tileEntity.getBlockPos().getX() + 0.5; + this.y = tileEntity.getBlockPos().getY() + 0.5; + this.z = tileEntity.getBlockPos().getZ() + 0.5; this.tileEntity = tileEntity; this.data = data; } @@ -55,7 +55,7 @@ protected NetworkActivityEvent(TileEntity tileEntity, NBTTagCompound data) { * @param z the z coordinate of the network card's container. * @param data the additional data. */ - protected NetworkActivityEvent(World world, double x, double y, double z, NBTTagCompound data) { + protected NetworkActivityEvent(World world, double x, double y, double z, CompoundNBT data) { this.world = world; this.x = x; this.y = y; @@ -98,7 +98,7 @@ public double getZ() { * Important: this can be null, which is usually the * case when the container is an entity or item. */ - public TileEntity getTileEntity() { + public TileEntity getBlockEntity() { return tileEntity; } @@ -106,7 +106,7 @@ public TileEntity getTileEntity() { * Addition custom data, this is used to transmit the number of the server * in a server rack the network card lives in, for example. */ - public NBTTagCompound getData() { + public CompoundNBT getData() { return data; } @@ -114,12 +114,12 @@ public static final class Server extends NetworkActivityEvent { private Node node; public Server(TileEntity tileEntity, Node node) { - super(tileEntity, new NBTTagCompound()); + super(tileEntity, new CompoundNBT()); this.node = node; } public Server(World world, double x, double y, double z, Node node) { - super(world, x, y, z, new NBTTagCompound()); + super(world, x, y, z, new CompoundNBT()); this.node = node; } @@ -138,7 +138,7 @@ public static final class Client extends NetworkActivityEvent { * @param tileEntity the tile entity hosting the network card. * @param data the additional data. */ - public Client(TileEntity tileEntity, NBTTagCompound data) { + public Client(TileEntity tileEntity, CompoundNBT data) { super(tileEntity, data); } @@ -151,7 +151,7 @@ public Client(TileEntity tileEntity, NBTTagCompound data) { * @param z the z coordinate of the network card's container. * @param data the additional data. */ - public Client(World world, double x, double y, double z, NBTTagCompound data) { + public Client(World world, double x, double y, double z, CompoundNBT data) { super(world, x, y, z, data); } } diff --git a/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java b/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java index fc77c14fc1..a880c0ed26 100644 --- a/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java +++ b/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java @@ -1,18 +1,22 @@ package li.cil.oc.api.event; +import com.mojang.blaze3d.matrix.MatrixStack; import li.cil.oc.api.component.RackMountable; import li.cil.oc.api.internal.Rack; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; +import net.minecraft.inventory.container.PlayerContainer; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Direction; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraft.util.math.vector.Matrix4f; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; import org.lwjgl.opengl.GL11; /** @@ -39,9 +43,9 @@ public abstract class RackMountableRenderEvent extends Event { * * @see RackMountable#getData() */ - public final NBTTagCompound data; + public final CompoundNBT data; - public RackMountableRenderEvent(Rack rack, int mountable, NBTTagCompound data) { + public RackMountableRenderEvent(Rack rack, int mountable, CompoundNBT data) { this.rack = rack; this.mountable = mountable; this.data = data; @@ -62,14 +66,14 @@ public static class Block extends RackMountableRenderEvent { /** * The front-facing side, i.e. where the mountable is visible on the rack. */ - public final EnumFacing side; + public final Direction side; /** * Texture to use for the front of the mountable. */ private TextureAtlasSprite frontTextureOverride; - public Block(final Rack rack, final int mountable, final NBTTagCompound data, final EnumFacing side) { + public Block(final Rack rack, final int mountable, final CompoundNBT data, final Direction side) { super(rack, mountable, data); this.side = side; } @@ -94,7 +98,7 @@ public void setFrontTextureOverride(final TextureAtlasSprite texture) { /** * Fired when the dynamic rack model is rendered. *

- * Code here runs inside a TileEntitySpecialRenderer, so go nuts. This is + * Code here runs inside a TileEntityRenderer, so go nuts. This is * primarily meant to allow rendering custom overlays, such as LEDs. The GL state * will have been adjusted such that rendering a one by one quad starting at the * origin will fill the full front face of the rack (i.e. rotation and translation @@ -107,6 +111,21 @@ public void setFrontTextureOverride(final TextureAtlasSprite texture) { * texture in the vertical area occupied by the mountable. */ public static class TileEntity extends RackMountableRenderEvent { + /** + * The transformation used by the rendering engine. + */ + public final MatrixStack stack; + + /** + * An accessor to the renderer's buffer context. + */ + public final IRenderTypeBuffer typeBuffer; + + /** + * Packed block light and overlay texture coordinates. + */ + public final int light, overlay; + /** * The vertical low and high texture coordinates for the mountable's slot. *

@@ -114,8 +133,12 @@ public static class TileEntity extends RackMountableRenderEvent { */ public final float v0, v1; - public TileEntity(final Rack rack, final int mountable, final NBTTagCompound data, final float v0, final float v1) { + public TileEntity(final Rack rack, final int mountable, final CompoundNBT data, final MatrixStack stack, final IRenderTypeBuffer typeBuffer, final int light, final int overlay, final float v0, final float v1) { super(rack, mountable, data); + this.stack = stack; + this.typeBuffer = typeBuffer; + this.light = light; + this.overlay = overlay; this.v0 = v0; this.v1 = v1; } @@ -138,15 +161,16 @@ public void renderOverlay(final ResourceLocation texture) { * @param u1 the upper end of the vertical area to render at. */ public void renderOverlay(final ResourceLocation texture, final float u0, final float u1) { - Minecraft.getMinecraft().getTextureManager().bindTexture(texture); + Minecraft.getInstance().getTextureManager().bind(texture); final Tessellator t = Tessellator.getInstance(); - final BufferBuilder r = t.getBuffer(); + final BufferBuilder r = t.getBuilder(); + final Matrix4f matrix = stack.last().pose(); r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.pos(u0, v1, 0).tex(u0, v1).endVertex(); - r.pos(u1, v1, 0).tex(u1, v1).endVertex(); - r.pos(u1, v0, 0).tex(u1, v0).endVertex(); - r.pos(u0, v0, 0).tex(u0, v0).endVertex(); - t.draw(); + r.vertex(matrix, u0, v1, 0).uv(u0, v1).endVertex(); + r.vertex(matrix, u1, v1, 0).uv(u1, v1).endVertex(); + r.vertex(matrix, u1, v0, 0).uv(u1, v0).endVertex(); + r.vertex(matrix, u0, v0, 0).uv(u0, v0).endVertex(); + t.end(); } /** @@ -167,16 +191,18 @@ public void renderOverlayFromAtlas(final ResourceLocation texture) { * @param u1 the upper end of the vertical area to render at. */ public void renderOverlayFromAtlas(final ResourceLocation texture, final float u0, final float u1) { - Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - final TextureAtlasSprite icon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(texture.toString()); + final AtlasTexture atlas = Minecraft.getInstance().getModelManager().getAtlas(PlayerContainer.BLOCK_ATLAS); + atlas.bind(); + final TextureAtlasSprite icon = atlas.getSprite(texture); final Tessellator t = Tessellator.getInstance(); - final BufferBuilder r = t.getBuffer(); + final BufferBuilder r = t.getBuilder(); + final Matrix4f matrix = stack.last().pose(); r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.pos(u0, v1, 0).tex(icon.getInterpolatedU(u0 * 16), icon.getInterpolatedV(v1 * 16)).endVertex(); - r.pos(u1, v1, 0).tex(icon.getInterpolatedU(u1 * 16), icon.getInterpolatedV(v1 * 16)).endVertex(); - r.pos(u1, v0, 0).tex(icon.getInterpolatedU(u1 * 16), icon.getInterpolatedV(v0 * 16)).endVertex(); - r.pos(u0, v0, 0).tex(icon.getInterpolatedU(u0 * 16), icon.getInterpolatedV(v0 * 16)).endVertex(); - t.draw(); + r.vertex(matrix, u0, v1, 0).uv(icon.getU(u0 * 16), icon.getV(v1 * 16)).endVertex(); + r.vertex(matrix, u1, v1, 0).uv(icon.getU(u1 * 16), icon.getV(v1 * 16)).endVertex(); + r.vertex(matrix, u1, v0, 0).uv(icon.getU(u1 * 16), icon.getV(v0 * 16)).endVertex(); + r.vertex(matrix, u0, v0, 0).uv(icon.getU(u0 * 16), icon.getV(v0 * 16)).endVertex(); + t.end(); } } } diff --git a/src/main/java/li/cil/oc/api/event/RobotAnalyzeEvent.java b/src/main/java/li/cil/oc/api/event/RobotAnalyzeEvent.java index b1497ab402..49309e9ded 100644 --- a/src/main/java/li/cil/oc/api/event/RobotAnalyzeEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotAnalyzeEvent.java @@ -1,7 +1,7 @@ package li.cil.oc.api.event; import li.cil.oc.api.internal.Agent; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; /** * Fired when an analyzer is used on a robot. @@ -12,9 +12,9 @@ public class RobotAnalyzeEvent extends RobotEvent { /** * The player that used the analyzer. */ - public final EntityPlayer player; + public final PlayerEntity player; - public RobotAnalyzeEvent(Agent agent, EntityPlayer player) { + public RobotAnalyzeEvent(Agent agent, PlayerEntity player) { super(agent); this.player = player; } diff --git a/src/main/java/li/cil/oc/api/event/RobotAttackEntityEvent.java b/src/main/java/li/cil/oc/api/event/RobotAttackEntityEvent.java index 55a7501888..a2ecac8141 100644 --- a/src/main/java/li/cil/oc/api/event/RobotAttackEntityEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotAttackEntityEvent.java @@ -2,7 +2,7 @@ import li.cil.oc.api.internal.Agent; import net.minecraft.entity.Entity; -import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraftforge.eventbus.api.Cancelable; public class RobotAttackEntityEvent extends RobotEvent { /** diff --git a/src/main/java/li/cil/oc/api/event/RobotBreakBlockEvent.java b/src/main/java/li/cil/oc/api/event/RobotBreakBlockEvent.java index 5e11343aa5..2281d7d899 100644 --- a/src/main/java/li/cil/oc/api/event/RobotBreakBlockEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotBreakBlockEvent.java @@ -3,7 +3,7 @@ import li.cil.oc.api.internal.Agent; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraftforge.eventbus.api.Cancelable; public abstract class RobotBreakBlockEvent extends RobotEvent { protected RobotBreakBlockEvent(Agent agent) { diff --git a/src/main/java/li/cil/oc/api/event/RobotEvent.java b/src/main/java/li/cil/oc/api/event/RobotEvent.java index a8672a04b5..0fb168ac28 100644 --- a/src/main/java/li/cil/oc/api/event/RobotEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotEvent.java @@ -1,7 +1,7 @@ package li.cil.oc.api.event; import li.cil.oc.api.internal.Agent; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.eventbus.api.Event; /** * Base class for events generated by robots. diff --git a/src/main/java/li/cil/oc/api/event/RobotMoveEvent.java b/src/main/java/li/cil/oc/api/event/RobotMoveEvent.java index d1c8f7096c..d721e0e264 100644 --- a/src/main/java/li/cil/oc/api/event/RobotMoveEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotMoveEvent.java @@ -1,16 +1,16 @@ package li.cil.oc.api.event; import li.cil.oc.api.internal.Agent; -import net.minecraft.util.EnumFacing; -import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraft.util.Direction; +import net.minecraftforge.eventbus.api.Cancelable; public abstract class RobotMoveEvent extends RobotEvent { /** * The direction in which the robot will be moving. */ - public final EnumFacing direction; + public final Direction direction; - protected RobotMoveEvent(Agent agent, EnumFacing direction) { + protected RobotMoveEvent(Agent agent, Direction direction) { super(agent); this.direction = direction; } @@ -22,7 +22,7 @@ protected RobotMoveEvent(Agent agent, EnumFacing direction) { */ @Cancelable public static class Pre extends RobotMoveEvent { - public Pre(Agent agent, EnumFacing direction) { + public Pre(Agent agent, Direction direction) { super(agent, direction); } } @@ -31,7 +31,7 @@ public Pre(Agent agent, EnumFacing direction) { * Fired after a robot moved. */ public static class Post extends RobotMoveEvent { - public Post(Agent agent, EnumFacing direction) { + public Post(Agent agent, Direction direction) { super(agent, direction); } } diff --git a/src/main/java/li/cil/oc/api/event/RobotPlaceBlockEvent.java b/src/main/java/li/cil/oc/api/event/RobotPlaceBlockEvent.java index 36cb5e60f7..07df5919fd 100644 --- a/src/main/java/li/cil/oc/api/event/RobotPlaceBlockEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotPlaceBlockEvent.java @@ -4,7 +4,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraftforge.eventbus.api.Cancelable; public abstract class RobotPlaceBlockEvent extends RobotEvent { /** diff --git a/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java b/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java index 4b20d0364b..fdb405b98f 100644 --- a/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java @@ -4,9 +4,9 @@ import li.cil.oc.api.internal.Agent; import li.cil.oc.api.internal.Robot; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import org.lwjgl.util.vector.Vector3f; -import org.lwjgl.util.vector.Vector4f; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraft.util.math.vector.Vector3f; +import net.minecraft.util.math.vector.Vector4f; import java.util.Set; diff --git a/src/main/java/li/cil/oc/api/event/SignChangeEvent.java b/src/main/java/li/cil/oc/api/event/SignChangeEvent.java index 64321c25f1..88a09b002f 100644 --- a/src/main/java/li/cil/oc/api/event/SignChangeEvent.java +++ b/src/main/java/li/cil/oc/api/event/SignChangeEvent.java @@ -1,30 +1,30 @@ package li.cil.oc.api.event; -import net.minecraft.tileentity.TileEntitySign; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraft.tileentity.SignTileEntity; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; /** * A bit more specific sign change event that holds information about new text of the sign. Used in the sign upgrade. */ public abstract class SignChangeEvent extends Event { - public final TileEntitySign sign; + public final SignTileEntity sign; public final String[] lines; - private SignChangeEvent(TileEntitySign sign, String[] lines) { + private SignChangeEvent(SignTileEntity sign, String[] lines) { this.sign = sign; this.lines = lines; } @Cancelable public static class Pre extends SignChangeEvent { - public Pre(TileEntitySign sign, String[] lines) { + public Pre(SignTileEntity sign, String[] lines) { super(sign, lines); } } public static class Post extends SignChangeEvent { - public Post(TileEntitySign sign, String[] lines) { + public Post(SignTileEntity sign, String[] lines) { super(sign, lines); } } diff --git a/src/main/java/li/cil/oc/api/event/package-info.java b/src/main/java/li/cil/oc/api/event/package-info.java index 6173e66fad..f1758d349d 100644 --- a/src/main/java/li/cil/oc/api/event/package-info.java +++ b/src/main/java/li/cil/oc/api/event/package-info.java @@ -2,10 +2,6 @@ * Events dispatched by OpenComputers to allow other mods to hook into some * of its functionality. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|event", - apiVersion = API.VERSION) package li.cil.oc.api.event; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/fs/package-info.java b/src/main/java/li/cil/oc/api/fs/package-info.java index 6c466110b1..46d9d5a620 100644 --- a/src/main/java/li/cil/oc/api/fs/package-info.java +++ b/src/main/java/li/cil/oc/api/fs/package-info.java @@ -13,10 +13,6 @@ * that can be added as component nodes to the network, so they can be used * from computers). */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|filesystem", - apiVersion = API.VERSION) package li.cil.oc.api.fs; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/internal/Agent.java b/src/main/java/li/cil/oc/api/internal/Agent.java index 6f78c196b6..53a71356f7 100644 --- a/src/main/java/li/cil/oc/api/internal/Agent.java +++ b/src/main/java/li/cil/oc/api/internal/Agent.java @@ -1,7 +1,7 @@ package li.cil.oc.api.internal; import li.cil.oc.api.machine.MachineHost; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.IInventory; import java.util.UUID; @@ -67,7 +67,7 @@ public interface Agent extends MachineHost, Rotatable { * * @return the fake player for the agent. */ - EntityPlayer player(); + PlayerEntity player(); /** * Get the name of this agent. diff --git a/src/main/java/li/cil/oc/api/internal/Drone.java b/src/main/java/li/cil/oc/api/internal/Drone.java index 45a9aba3d6..95fcfe7ffe 100644 --- a/src/main/java/li/cil/oc/api/internal/Drone.java +++ b/src/main/java/li/cil/oc/api/internal/Drone.java @@ -1,7 +1,7 @@ package li.cil.oc.api.internal; import li.cil.oc.api.network.EnvironmentHost; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; /** * This interface is implemented as a marker by drones. @@ -20,7 +20,7 @@ public interface Drone extends Agent, EnvironmentHost, Rotatable, Tiered { /** * Get the current target coordinates of the drone. */ - Vec3d getTarget(); + Vector3d getTarget(); /** * Set the new target coordinates of the drone. @@ -29,7 +29,7 @@ public interface Drone extends Agent, EnvironmentHost, Rotatable, Tiered { * to avoid jitter on the client and floating point inaccuracies to * accumulate. */ - void setTarget(Vec3d value); + void setTarget(Vector3d value); /** * Get the drones velocity vector. @@ -38,5 +38,5 @@ public interface Drone extends Agent, EnvironmentHost, Rotatable, Tiered { * so you can cast this to {@link net.minecraft.entity.Entity} and use that * instead, if you'd like. */ - Vec3d getVelocity(); + Vector3d getVelocity(); } diff --git a/src/main/java/li/cil/oc/api/internal/Keyboard.java b/src/main/java/li/cil/oc/api/internal/Keyboard.java index 7fdbf325a7..9ac4c7b6ed 100644 --- a/src/main/java/li/cil/oc/api/internal/Keyboard.java +++ b/src/main/java/li/cil/oc/api/internal/Keyboard.java @@ -2,7 +2,7 @@ import li.cil.oc.api.Persistable; import li.cil.oc.api.network.Environment; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; /** * This interface is implemented by the keyboard component, to allow more @@ -42,6 +42,6 @@ interface UsabilityChecker { * @param player the player to check for. * @return whether the keyboard is usable by the player. */ - boolean isUsableByPlayer(Keyboard keyboard, EntityPlayer player); + boolean isUsableByPlayer(Keyboard keyboard, PlayerEntity player); } } diff --git a/src/main/java/li/cil/oc/api/internal/Rack.java b/src/main/java/li/cil/oc/api/internal/Rack.java index 46aa88d3bc..9a11421a1b 100644 --- a/src/main/java/li/cil/oc/api/internal/Rack.java +++ b/src/main/java/li/cil/oc/api/internal/Rack.java @@ -4,7 +4,7 @@ import li.cil.oc.api.network.EnvironmentHost; import li.cil.oc.api.network.SidedEnvironment; import net.minecraft.inventory.IInventory; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * This interface is implemented by the rack tile entity. @@ -48,7 +48,7 @@ public interface Rack extends SidedEnvironment, EnvironmentHost, Rotatable, IInv * @param slot the slot of the mountable to get the data for. * @return the data of the mountable in that slot, or null. */ - NBTTagCompound getMountableData(int slot); + CompoundNBT getMountableData(int slot); /** * Mark the mountable in the specified slot as changed. diff --git a/src/main/java/li/cil/oc/api/internal/Robot.java b/src/main/java/li/cil/oc/api/internal/Robot.java index ca5efa25fa..a94387a37c 100644 --- a/src/main/java/li/cil/oc/api/internal/Robot.java +++ b/src/main/java/li/cil/oc/api/internal/Robot.java @@ -3,8 +3,8 @@ import li.cil.oc.api.network.EnvironmentHost; import li.cil.oc.api.network.Environment; import net.minecraft.inventory.ISidedInventory; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; /** * This interface allows interaction with robots. @@ -74,7 +74,7 @@ public interface Robot extends Agent, Environment, EnvironmentHost, Tiered, ISid * to know whether to resume animations or not, based on whether the robot * is currently powered on or not. */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) boolean shouldAnimate(); } diff --git a/src/main/java/li/cil/oc/api/internal/Rotatable.java b/src/main/java/li/cil/oc/api/internal/Rotatable.java index 2695770f15..af1d6ffedc 100644 --- a/src/main/java/li/cil/oc/api/internal/Rotatable.java +++ b/src/main/java/li/cil/oc/api/internal/Rotatable.java @@ -1,7 +1,7 @@ package li.cil.oc.api.internal; import li.cil.oc.api.driver.DriverItem; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; /** * This interface is implemented by the computer case and robot tile entities @@ -32,7 +32,7 @@ public interface Rotatable { * * @return the current facing. */ - EnumFacing facing(); + Direction facing(); /** * Converts a facing relative to the block's local coordinate @@ -45,7 +45,7 @@ public interface Rotatable { * @param value the value to translate. * @return the translated orientation. */ - EnumFacing toGlobal(EnumFacing value); + Direction toGlobal(Direction value); /** * Converts a global orientation to a facing relative to the @@ -58,5 +58,5 @@ public interface Rotatable { * @param value the value to translate. * @return the translated orientation. */ - EnumFacing toLocal(EnumFacing value); + Direction toLocal(Direction value); } diff --git a/src/main/java/li/cil/oc/api/internal/Tablet.java b/src/main/java/li/cil/oc/api/internal/Tablet.java index e042685ac7..1b478170fb 100644 --- a/src/main/java/li/cil/oc/api/internal/Tablet.java +++ b/src/main/java/li/cil/oc/api/internal/Tablet.java @@ -2,7 +2,7 @@ import li.cil.oc.api.network.EnvironmentHost; import li.cil.oc.api.machine.MachineHost; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; /** * This interface is implemented as a marker by tablets. @@ -34,5 +34,5 @@ public interface Tablet extends EnvironmentHost, MachineHost, Rotatable { * * @return the player last holding the tablet. */ - EntityPlayer player(); + PlayerEntity player(); } diff --git a/src/main/java/li/cil/oc/api/internal/TextBuffer.java b/src/main/java/li/cil/oc/api/internal/TextBuffer.java index 2c7d3bcc15..7bde1f80f0 100644 --- a/src/main/java/li/cil/oc/api/internal/TextBuffer.java +++ b/src/main/java/li/cil/oc/api/internal/TextBuffer.java @@ -1,10 +1,11 @@ package li.cil.oc.api.internal; +import com.mojang.blaze3d.matrix.MatrixStack; import li.cil.oc.api.Persistable; import li.cil.oc.api.network.ManagedEnvironment; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; /** * This interface implements functionality for displaying and manipulating @@ -446,8 +447,8 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @return true if the displayed content changed since the last * call to this method. */ - @SideOnly(Side.CLIENT) - boolean renderText(); + @OnlyIn(Dist.CLIENT) + boolean renderText(MatrixStack stack); /** * The natural width of the rendered text. @@ -458,7 +459,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * * @return the total width of the rendered buffer, in pixels. */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) int renderWidth(); /** @@ -470,7 +471,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * * @return the total height of the rendered buffer, in pixels. */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) int renderHeight(); /** @@ -481,7 +482,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * * @param enabled whether the text buffer should be rendered. */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) void setRenderingEnabled(boolean enabled); /** @@ -489,7 +490,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * * @see #setRenderingEnabled(boolean) */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) boolean isRenderingEnabled(); // ----------------------------------------------------------------------- // @@ -505,7 +506,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param code the key code of the pressed key. * @param player the player that pressed the key. Pass null on the client side. */ - void keyDown(char character, int code, EntityPlayer player); + void keyDown(char character, int code, PlayerEntity player); /** * Signals a key up event for the buffer. @@ -518,7 +519,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param code the key code of the released key. * @param player the player that released the key. Pass null on the client side. */ - void keyUp(char character, int code, EntityPlayer player); + void keyUp(char character, int code, PlayerEntity player); /** * Signals a clipboard paste event for the buffer. @@ -530,7 +531,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param value the text that was pasted. * @param player the player that pasted the text. Pass null on the client side. */ - void clipboard(String value, EntityPlayer player); + void clipboard(String value, PlayerEntity player); /** * Signals a mouse button down event for the buffer. @@ -543,7 +544,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that was pressed. * @param player the player that pressed the mouse button. Pass null on the client side. */ - void mouseDown(double x, double y, int button, EntityPlayer player); + void mouseDown(double x, double y, int button, PlayerEntity player); /** * Signals a mouse drag event for the buffer. @@ -556,7 +557,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that is pressed. * @param player the player that moved the mouse. Pass null on the client side. */ - void mouseDrag(double x, double y, int button, EntityPlayer player); + void mouseDrag(double x, double y, int button, PlayerEntity player); /** * Signals a mouse button release event for the buffer. @@ -569,7 +570,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param button the button of the mouse that was released. * @param player the player that released the mouse button. Pass null on the client side. */ - void mouseUp(double x, double y, int button, EntityPlayer player); + void mouseUp(double x, double y, int button, PlayerEntity player); /** * Signals a mouse wheel scroll event for the buffer. @@ -582,7 +583,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { * @param delta indicates the direction of the mouse scroll. * @param player the player that scrolled the mouse wheel. Pass null on the client side. */ - void mouseScroll(double x, double y, int delta, EntityPlayer player); + void mouseScroll(double x, double y, int delta, PlayerEntity player); // ----------------------------------------------------------------------- // diff --git a/src/main/java/li/cil/oc/api/internal/Wrench.java b/src/main/java/li/cil/oc/api/internal/Wrench.java index f1584824af..7609aa8e3e 100644 --- a/src/main/java/li/cil/oc/api/internal/Wrench.java +++ b/src/main/java/li/cil/oc/api/internal/Wrench.java @@ -1,6 +1,6 @@ package li.cil.oc.api.internal; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -25,5 +25,5 @@ public interface Wrench { * @param simulate whether to simulate the usage. * @return whether the wrench can be used on the block. */ - boolean useWrenchOnBlock(EntityPlayer player, World world, BlockPos pos, boolean simulate); + boolean useWrenchOnBlock(PlayerEntity player, World world, BlockPos pos, boolean simulate); } diff --git a/src/main/java/li/cil/oc/api/internal/package-info.java b/src/main/java/li/cil/oc/api/internal/package-info.java index 0cea951a40..f07112644f 100644 --- a/src/main/java/li/cil/oc/api/internal/package-info.java +++ b/src/main/java/li/cil/oc/api/internal/package-info.java @@ -13,10 +13,6 @@ * used inside the specified environment (where the environment class may * be assignable to one of the interfaces in this package). */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|internal", - apiVersion = API.VERSION) package li.cil.oc.api.internal; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/machine/Architecture.java b/src/main/java/li/cil/oc/api/machine/Architecture.java index 0bf190b44d..05d431da0c 100644 --- a/src/main/java/li/cil/oc/api/machine/Architecture.java +++ b/src/main/java/li/cil/oc/api/machine/Architecture.java @@ -1,7 +1,7 @@ package li.cil.oc.api.machine; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; import java.lang.annotation.*; @@ -134,13 +134,13 @@ public interface Architecture { /** * Restores the state of this architecture as previously saved in - * {@link #save(NBTTagCompound)}. The architecture should be in the same + * {@link #saveData(CompoundNBT)}. The architecture should be in the same * state it was when it was saved after this, so it can be resumed from * whatever state the owning machine was in when it was saved. * * @param nbt the tag compound to save to. */ - void load(NBTTagCompound nbt); + void loadData(CompoundNBT nbt); /** * Saves the architecture for later restoration, e.g. across games or chunk @@ -151,7 +151,7 @@ public interface Architecture { * * @param nbt the tag compound to save to. */ - void save(NBTTagCompound nbt); + void saveData(CompoundNBT nbt); /** * Architectures can be annotated with this to provide a nice display name. diff --git a/src/main/java/li/cil/oc/api/machine/Context.java b/src/main/java/li/cil/oc/api/machine/Context.java index b92aa75096..66dd616cb9 100644 --- a/src/main/java/li/cil/oc/api/machine/Context.java +++ b/src/main/java/li/cil/oc/api/machine/Context.java @@ -36,7 +36,7 @@ public interface Context { * Use this to check whether you should signal something to the computer, * for example. Note that for signals triggered via network messages there * is a computer.checked_signal message, that expects an - * EntityPlayer as the first argument and performs this check + * PlayerEntity as the first argument and performs this check * before pushing the signal. * * @param player the name of the player to check for. @@ -171,7 +171,7 @@ public interface Context { *

  • Strings.
  • *
  • Byte arrays (which appear as strings on the Lua side, e.g.).
  • *
  • Maps if and only if both keys and values are strings.
  • - *
  • NBTTagCompounds.
  • + *
  • CompoundNBTs.
  • * * If an unsupported type is specified the method will enqueue nothing * instead, resulting in a nil on the Lua side, e.g., and log a diff --git a/src/main/java/li/cil/oc/api/machine/package-info.java b/src/main/java/li/cil/oc/api/machine/package-info.java index 09b038befd..1f5c666e0f 100644 --- a/src/main/java/li/cil/oc/api/machine/package-info.java +++ b/src/main/java/li/cil/oc/api/machine/package-info.java @@ -16,10 +16,6 @@ * implemented, but merely to allow accessing some mod internals in a regulated * fashion, such as {@link li.cil.oc.api.internal.Robot}. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|machine", - apiVersion = API.VERSION) package li.cil.oc.api.machine; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/manual/ImageRenderer.java b/src/main/java/li/cil/oc/api/manual/ImageRenderer.java index 2dcad21f77..0e1a445481 100644 --- a/src/main/java/li/cil/oc/api/manual/ImageRenderer.java +++ b/src/main/java/li/cil/oc/api/manual/ImageRenderer.java @@ -1,5 +1,7 @@ package li.cil.oc.api.manual; +import com.mojang.blaze3d.matrix.MatrixStack; + /** * This allows implementing custom image renderers. *

    @@ -15,7 +17,7 @@ public interface ImageRenderer { * The width of the area this renderer uses. *

    * This is used to offset the OpenGL state properly before calling - * {@link #render(int, int)}, to correctly align the image horizontally. + * {@link #render(MatrixStack, int, int)}, to correctly align the image horizontally. * * @return the width of the rendered image. */ @@ -25,7 +27,7 @@ public interface ImageRenderer { * The height of the area this renderer uses. *

    * This is used to offset the OpenGL state properly before calling - * {@link #render(int, int)}, as well as to know where to resume rendering + * {@link #render(MatrixStack, int, int)}, as well as to know where to resume rendering * other content below the image. * * @return the height of the rendered image. @@ -40,8 +42,9 @@ public interface ImageRenderer { * (getWidth,getHeight,*), i.e. translation and scaling are taken care * of for you. * + * @param stack the render transformation for this image * @param mouseX the X position of the mouse relative to the element. * @param mouseY the Y position of the mouse relative to the element. */ - void render(int mouseX, int mouseY); + void render(MatrixStack stack, int mouseX, int mouseY); } diff --git a/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java b/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java index ee7af3a746..ad07891d9c 100644 --- a/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java @@ -1,7 +1,7 @@ package li.cil.oc.api.manual; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; /** * Allows defining a renderer for a manual tab. @@ -21,6 +21,6 @@ public interface TabIconRenderer { * This should render something in a 16x16 area. The OpenGL state has been * adjusted so that drawing starts at (0,0,0), and should go to (16,16,0). */ - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) void render(); } diff --git a/src/main/java/li/cil/oc/api/manual/package-info.java b/src/main/java/li/cil/oc/api/manual/package-info.java index eba0a4c2e9..cb61332b7c 100644 --- a/src/main/java/li/cil/oc/api/manual/package-info.java +++ b/src/main/java/li/cil/oc/api/manual/package-info.java @@ -5,10 +5,6 @@ * other mod that may choose to add its documentation to it, such as addon * mods. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|manual", - apiVersion = API.VERSION) package li.cil.oc.api.manual; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/nanomachines/BehaviorProvider.java b/src/main/java/li/cil/oc/api/nanomachines/BehaviorProvider.java index 4e0ecf9525..69bbadc9ed 100644 --- a/src/main/java/li/cil/oc/api/nanomachines/BehaviorProvider.java +++ b/src/main/java/li/cil/oc/api/nanomachines/BehaviorProvider.java @@ -1,7 +1,7 @@ package li.cil.oc.api.nanomachines; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.CompoundNBT; /** * Implemented by providers for behaviors. @@ -26,12 +26,12 @@ public interface BehaviorProvider { * Note that this is only called on the server side when reconfiguring * nanomachines. If you have a behavior that actually acts client-only, * you still need to return it here, as it will be synchronized to the - * client using {@link #writeToNBT} and {@link #readFromNBT}. + * client using {@link #save} and {@link #load}. * * @param player the player the behaviors should be created for. * @return list of new behaviors, may be null. */ - Iterable createBehaviors(EntityPlayer player); + Iterable createBehaviors(PlayerEntity player); /** * Write a behavior to NBT. @@ -45,7 +45,7 @@ public interface BehaviorProvider { * @param behavior the behavior to serialize. * @return the serialized representation of the specified behavior. */ - NBTTagCompound writeToNBT(Behavior behavior); + CompoundNBT save(Behavior behavior); /** * Restore a behavior from NBT. @@ -62,5 +62,5 @@ public interface BehaviorProvider { * @param nbt the tag to restore the behavior from. * @return the restored behavior, or null if unhandled. */ - Behavior readFromNBT(EntityPlayer player, NBTTagCompound nbt); + Behavior load(PlayerEntity player, CompoundNBT nbt); } diff --git a/src/main/java/li/cil/oc/api/network/Analyzable.java b/src/main/java/li/cil/oc/api/network/Analyzable.java index 204d577209..3fab595c6a 100644 --- a/src/main/java/li/cil/oc/api/network/Analyzable.java +++ b/src/main/java/li/cil/oc/api/network/Analyzable.java @@ -1,7 +1,7 @@ package li.cil.oc.api.network; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Direction; /** * Allows defining a callback for when a block is right-clicked with an @@ -31,5 +31,5 @@ public interface Analyzable { * @return the nodes to display information for, usually an environment's * main node (i.e. this.node()). */ - Node[] onAnalyze(EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ); + Node[] onAnalyze(PlayerEntity player, Direction side, float hitX, float hitY, float hitZ); } diff --git a/src/main/java/li/cil/oc/api/network/Environment.java b/src/main/java/li/cil/oc/api/network/Environment.java index 906543874d..b0b63eccd1 100644 --- a/src/main/java/li/cil/oc/api/network/Environment.java +++ b/src/main/java/li/cil/oc/api/network/Environment.java @@ -18,20 +18,20 @@ * When a tile entity implements this interface a good way of connecting and * disconnecting is the following pattern: *

    - *     void updateEntity() {
    - *         super.updateEntity()
    + *     void tick() {
    + *         super.tick()
      *         if (node != null && node.network == null) {
      *             api.Network.joinOrCreateNetwork(this);
      *         }
      *     }
      *
    - *     void onChunkUnload() {
    - *         super.onChunkUnload()
    + *     void onChunkUnloaded() {
    + *         super.onChunkUnloaded()
      *         if (node != null) node.remove()
      *     }
      *
    - *     void invalidate() {
    - *         super.invalidate()
    + *     void setRemoved() {
    + *         super.setRemoved()
      *         if (node != null) node.remove()
      *     }
      * 
    diff --git a/src/main/java/li/cil/oc/api/network/Network.java b/src/main/java/li/cil/oc/api/network/Network.java index e1d6fa8931..577eafe17e 100644 --- a/src/main/java/li/cil/oc/api/network/Network.java +++ b/src/main/java/li/cil/oc/api/network/Network.java @@ -75,8 +75,8 @@ public interface Network { * Removes a node from the network. *

    * This should be called by nodes when they are destroyed (e.g. in - * {@link net.minecraft.tileentity.TileEntity#invalidate()}) or unloaded - * (e.g. in {@link net.minecraft.tileentity.TileEntity#onChunkUnload()}). + * {@link net.minecraft.tileentity.TileEntity#setRemoved()}) or unloaded + * (e.g. in {@link net.minecraft.tileentity.TileEntity#onChunkUnloaded()}). * Removing the node can lead to one or more new networks if it was the a * bridge node, i.e. the only node connecting the resulting networks. * diff --git a/src/main/java/li/cil/oc/api/network/Packet.java b/src/main/java/li/cil/oc/api/network/Packet.java index 233e720187..572cf606e6 100644 --- a/src/main/java/li/cil/oc/api/network/Packet.java +++ b/src/main/java/li/cil/oc/api/network/Packet.java @@ -1,6 +1,6 @@ package li.cil.oc.api.network; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * These packets represent messages sent using a network card or wireless @@ -65,5 +65,5 @@ public interface Packet { * Restore a packet saved like this using the factory method in the * {@link li.cil.oc.api.Network} class. */ - void save(NBTTagCompound nbt); + void saveData(CompoundNBT nbt); } diff --git a/src/main/java/li/cil/oc/api/network/SidedComponent.java b/src/main/java/li/cil/oc/api/network/SidedComponent.java index bc5eea0dd0..06c1ed13af 100644 --- a/src/main/java/li/cil/oc/api/network/SidedComponent.java +++ b/src/main/java/li/cil/oc/api/network/SidedComponent.java @@ -1,6 +1,6 @@ package li.cil.oc.api.network; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; /** * This is an extended version of {@link li.cil.oc.api.network.SimpleComponent} @@ -22,5 +22,5 @@ public interface SidedComponent { * @param side the side to check for. * @return whether the component may be connected to from the specified side. */ - boolean canConnectNode(EnumFacing side); + boolean canConnectNode(Direction side); } diff --git a/src/main/java/li/cil/oc/api/network/SidedEnvironment.java b/src/main/java/li/cil/oc/api/network/SidedEnvironment.java index 1257100430..509e56ed54 100644 --- a/src/main/java/li/cil/oc/api/network/SidedEnvironment.java +++ b/src/main/java/li/cil/oc/api/network/SidedEnvironment.java @@ -1,9 +1,9 @@ package li.cil.oc.api.network; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; /** * This interface is like {@link net.minecraft.inventory.ISidedInventory} is to @@ -32,7 +32,7 @@ public interface SidedEnvironment { * @return the node for the specified side. * @see li.cil.oc.api.network.Environment#node */ - Node sidedNode(EnumFacing side); + Node sidedNode(Direction side); /** * Whether the environment provides a node to connect to on the specified @@ -50,6 +50,6 @@ public interface SidedEnvironment { * @param side the side to check for. * @return whether the environment provides a node for the specified side. */ - @SideOnly(Side.CLIENT) - boolean canConnect(EnumFacing side); + @OnlyIn(Dist.CLIENT) + boolean canConnect(Direction side); } diff --git a/src/main/java/li/cil/oc/api/network/SimpleComponent.java b/src/main/java/li/cil/oc/api/network/SimpleComponent.java index 7c5d10de3f..e59e58654d 100644 --- a/src/main/java/li/cil/oc/api/network/SimpleComponent.java +++ b/src/main/java/li/cil/oc/api/network/SimpleComponent.java @@ -13,10 +13,6 @@ * tile entity. Use this only for simple cases, where you want to expose a * couple of methods to the programs running computers. *

    - * This is an interface instead of an annotation, to allow stripping via the - * ever so handy {@link net.minecraftforge.fml.common.Optional} annotation, - * meaning there will be no strong dependency on OpenComputers. - *

    * Classes implementing this interface will be expanded with the methods * required for them to function as native block components (say, like the * screen or keyboard). This means functions in the Environment @@ -86,6 +82,7 @@ * } * */ +@Deprecated public interface SimpleComponent { /** * The name the component should be made available as. diff --git a/src/main/java/li/cil/oc/api/network/package-info.java b/src/main/java/li/cil/oc/api/network/package-info.java index 7fa496cdd0..6dbef6b188 100644 --- a/src/main/java/li/cil/oc/api/network/package-info.java +++ b/src/main/java/li/cil/oc/api/network/package-info.java @@ -4,10 +4,6 @@ * This mainly involves the (purely server-side!) network that is spanned over * all of OpenComputers' components, including blocks and items alike. */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|network", - apiVersion = API.VERSION) package li.cil.oc.api.network; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/package-info.java b/src/main/java/li/cil/oc/api/package-info.java index 502aa3781b..f9dae02d67 100644 --- a/src/main/java/li/cil/oc/api/package-info.java +++ b/src/main/java/li/cil/oc/api/package-info.java @@ -15,7 +15,7 @@ * Note that for tile entities you implement yourself, you will not have to * provide a driver, as long as you implement the necessary interface: * {@link li.cil.oc.api.network.Environment} and call {@link li.cil.oc.api.Network#joinOrCreateNetwork(net.minecraft.tileentity.TileEntity)} - * in the first updateEntity() call. For items that should be installed + * in the first tick() call. For items that should be installed * in a computer you will always have to provide a driver. *

    *
    The {@link li.cil.oc.api.FileSystem} API
    @@ -34,8 +34,4 @@ * * */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|core", - apiVersion = API.VERSION) package li.cil.oc.api; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java b/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java index ea1ad4bb9c..19429a0360 100644 --- a/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java +++ b/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java @@ -2,7 +2,7 @@ import li.cil.oc.api.nanomachines.Behavior; import li.cil.oc.api.nanomachines.DisableReason; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; /** * Base class for behaviors, mostly useful to have less cluttered classes when @@ -14,7 +14,7 @@ public abstract class AbstractBehavior implements Behavior { /** * The player this behavior was created for. */ - public final EntityPlayer player; + public final PlayerEntity player; /** * Pass along the player the behavior was created for here to have it stored @@ -22,7 +22,7 @@ public abstract class AbstractBehavior implements Behavior { * * @param player the player the behavior was created for. */ - protected AbstractBehavior(EntityPlayer player) { + protected AbstractBehavior(PlayerEntity player) { this.player = player; } diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractManagedEnvironment.java b/src/main/java/li/cil/oc/api/prefab/AbstractManagedEnvironment.java index cb6c39145e..010668196b 100644 --- a/src/main/java/li/cil/oc/api/prefab/AbstractManagedEnvironment.java +++ b/src/main/java/li/cil/oc/api/prefab/AbstractManagedEnvironment.java @@ -3,7 +3,7 @@ import li.cil.oc.api.network.ManagedEnvironment; import li.cil.oc.api.network.Message; import li.cil.oc.api.network.Node; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * Simple base implementation of the ManagedEnvironment interface, so @@ -46,14 +46,14 @@ public void onMessage(final Message message) { } @Override - public void load(final NBTTagCompound nbt) { + public void loadData(final CompoundNBT nbt) { if (node() != null) { - node().load(nbt.getCompoundTag(NODE_TAG)); + node().loadData(nbt.getCompound(NODE_TAG)); } } @Override - public void save(final NBTTagCompound nbt) { + public void saveData(final CompoundNBT nbt) { if (node() != null) { // Force joining a network when saving and we're not in one yet, so that // the address is embedded in the saved data that gets sent to the client, @@ -62,15 +62,15 @@ public void save(final NBTTagCompound nbt) { if (node().address() == null) { li.cil.oc.api.Network.joinNewNetwork(node()); - final NBTTagCompound nodeTag = new NBTTagCompound(); - node().save(nodeTag); - nbt.setTag(NODE_TAG, nodeTag); + final CompoundNBT nodeTag = new CompoundNBT(); + node().saveData(nodeTag); + nbt.put(NODE_TAG, nodeTag); node().remove(); } else { - final NBTTagCompound nodeTag = new NBTTagCompound(); - node().save(nodeTag); - nbt.setTag(NODE_TAG, nodeTag); + final CompoundNBT nodeTag = new CompoundNBT(); + node().saveData(nodeTag); + nbt.put(NODE_TAG, nodeTag); } } } diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java b/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java index 9f03938353..58ca1b8597 100644 --- a/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java +++ b/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java @@ -2,8 +2,8 @@ import li.cil.oc.api.nanomachines.Behavior; import li.cil.oc.api.nanomachines.BehaviorProvider; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.CompoundNBT; /** * Example base implementation of nanomachine behavior provider. @@ -38,7 +38,7 @@ protected AbstractProvider(String id) { * @param behavior the behavior to persist. * @param nbt the NBT tag to persist it to. */ - protected void writeBehaviorToNBT(Behavior behavior, NBTTagCompound nbt) { + protected void writeBehaviorToNBT(Behavior behavior, CompoundNBT nbt) { } /** @@ -51,20 +51,20 @@ protected void writeBehaviorToNBT(Behavior behavior, NBTTagCompound nbt) { * @param nbt the NBT tag to load restore the behavior from. * @return the restored behavior. */ - protected abstract Behavior readBehaviorFromNBT(EntityPlayer player, NBTTagCompound nbt); + protected abstract Behavior readBehaviorFromNBT(PlayerEntity player, CompoundNBT nbt); // ----------------------------------------------------------------------- // @Override - public NBTTagCompound writeToNBT(Behavior behavior) { - NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("provider", id); + public CompoundNBT save(Behavior behavior) { + CompoundNBT nbt = new CompoundNBT(); + nbt.putString("provider", id); writeBehaviorToNBT(behavior, nbt); return nbt; } @Override - public Behavior readFromNBT(EntityPlayer player, NBTTagCompound nbt) { + public Behavior load(PlayerEntity player, CompoundNBT nbt) { if (id.equals(nbt.getString("provider"))) { return readBehaviorFromNBT(player, nbt); } else { diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractValue.java b/src/main/java/li/cil/oc/api/prefab/AbstractValue.java index 7d686b5df6..d0c8261808 100644 --- a/src/main/java/li/cil/oc/api/prefab/AbstractValue.java +++ b/src/main/java/li/cil/oc/api/prefab/AbstractValue.java @@ -3,7 +3,7 @@ import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Context; import li.cil.oc.api.machine.Value; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * Basic implementation for the Value interface. @@ -28,10 +28,10 @@ public void dispose(Context context) { } @Override - public void load(NBTTagCompound nbt) { + public void loadData(CompoundNBT nbt) { } @Override - public void save(NBTTagCompound nbt) { + public void saveData(CompoundNBT nbt) { } } diff --git a/src/main/java/li/cil/oc/api/prefab/DriverItem.java b/src/main/java/li/cil/oc/api/prefab/DriverItem.java index e44e4b7797..c111035ec9 100644 --- a/src/main/java/li/cil/oc/api/prefab/DriverItem.java +++ b/src/main/java/li/cil/oc/api/prefab/DriverItem.java @@ -2,7 +2,7 @@ import li.cil.oc.api.network.EnvironmentHost; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.CompoundNBT; /** * If you wish to create item components such as the network card or hard drives @@ -30,7 +30,7 @@ protected DriverItem(final ItemStack... items) { public boolean worksWith(final ItemStack stack) { if (!stack.isEmpty()) { for (ItemStack item : items) { - if (!item.isEmpty() && item.isItemEqual(stack)) { + if (!item.isEmpty() && item.sameItem(stack)) { return true; } } @@ -44,17 +44,14 @@ public int tier(final ItemStack stack) { } @Override - public NBTTagCompound dataTag(final ItemStack stack) { - if (!stack.hasTagCompound()) { - stack.setTagCompound(new NBTTagCompound()); - } - final NBTTagCompound nbt = stack.getTagCompound(); + public CompoundNBT dataTag(final ItemStack stack) { + final CompoundNBT nbt = stack.getOrCreateTag(); // This is the suggested key under which to store item component data. // You are free to change this as you please. - if (!nbt.hasKey("oc:data")) { - nbt.setTag("oc:data", new NBTTagCompound()); + if (!nbt.contains("oc:data")) { + nbt.put("oc:data", new CompoundNBT()); } - return nbt.getCompoundTag("oc:data"); + return nbt.getCompound("oc:data"); } // Convenience methods provided for HostAware drivers. diff --git a/src/main/java/li/cil/oc/api/prefab/DriverSidedBlock.java b/src/main/java/li/cil/oc/api/prefab/DriverSidedBlock.java index 40ca7f1931..079d372630 100644 --- a/src/main/java/li/cil/oc/api/prefab/DriverSidedBlock.java +++ b/src/main/java/li/cil/oc/api/prefab/DriverSidedBlock.java @@ -1,14 +1,10 @@ package li.cil.oc.api.prefab; import li.cil.oc.api.driver.DriverBlock; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; +import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.world.World; -import net.minecraftforge.oredict.OreDictionary; /** * If you wish to create a block component for a third-party block, i.e. a block @@ -22,36 +18,24 @@ * You still have to provide the implementation for creating its environment, if * any. *

    - * To limit sidedness, I recommend overriding {@link #worksWith(World, BlockPos, EnumFacing)} + * To limit sidedness, I recommend overriding {@link #worksWith(World, BlockPos, Direction)} * and calling super.worksWith in addition to the side check. * * @see li.cil.oc.api.network.ManagedEnvironment */ @SuppressWarnings("UnusedDeclaration") public abstract class DriverSidedBlock implements DriverBlock { - protected final ItemStack[] blocks; + protected final BlockState[] blocks; - protected DriverSidedBlock(final ItemStack... blocks) { + protected DriverSidedBlock(final BlockState... blocks) { this.blocks = blocks.clone(); } @Override - public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) { - final IBlockState state = world.getBlockState(pos); - final Block block = state.getBlock(); - return worksWith(block, block.getMetaFromState(state)); - } - - protected boolean worksWith(final Block referenceBlock, final int referenceMetadata) { - for (ItemStack stack : blocks) { - if (!stack.isEmpty() && stack.getItem() instanceof ItemBlock) { - final ItemBlock item = (ItemBlock) stack.getItem(); - final Block supportedBlock = item.getBlock(); - final int supportedMetadata = item.getMetadata(stack.getItemDamage()); - if (referenceBlock == supportedBlock && (referenceMetadata == supportedMetadata || stack.getItemDamage() == OreDictionary.WILDCARD_VALUE)) { - return true; - } - } + public boolean worksWith(final World world, final BlockPos pos, final Direction side) { + final BlockState state = world.getBlockState(pos); + for (BlockState block : blocks) { + if (block == state) return true; } return false; } diff --git a/src/main/java/li/cil/oc/api/prefab/DriverSidedTileEntity.java b/src/main/java/li/cil/oc/api/prefab/DriverSidedTileEntity.java index e17b3b0d86..9c40c7183a 100644 --- a/src/main/java/li/cil/oc/api/prefab/DriverSidedTileEntity.java +++ b/src/main/java/li/cil/oc/api/prefab/DriverSidedTileEntity.java @@ -3,25 +3,25 @@ import li.cil.oc.api.driver.DriverBlock; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.world.World; /** - * To limit sidedness, I recommend overriding {@link #worksWith(World, BlockPos, EnumFacing)} + * To limit sidedness, I recommend overriding {@link #worksWith(World, BlockPos, Direction)} * and calling super.worksWith in addition to the side check. */ public abstract class DriverSidedTileEntity implements DriverBlock { public abstract Class getTileEntityClass(); @Override - public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) { + public boolean worksWith(final World world, final BlockPos pos, final Direction side) { final Class filter = getTileEntityClass(); if (filter == null) { // This can happen if filter classes are deduced by reflection and // the class in question is not present. return false; } - final TileEntity tileEntity = world.getTileEntity(pos); + final TileEntity tileEntity = world.getBlockEntity(pos); return tileEntity != null && filter.isAssignableFrom(tileEntity.getClass()); } } diff --git a/src/main/java/li/cil/oc/api/prefab/ItemStackArrayValue.java b/src/main/java/li/cil/oc/api/prefab/ItemStackArrayValue.java index 883cc40b3c..e916a3d38f 100644 --- a/src/main/java/li/cil/oc/api/prefab/ItemStackArrayValue.java +++ b/src/main/java/li/cil/oc/api/prefab/ItemStackArrayValue.java @@ -4,9 +4,8 @@ import li.cil.oc.api.machine.Callback; import li.cil.oc.api.machine.Context; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; import java.util.HashMap; import java.util.TreeMap; @@ -16,8 +15,8 @@ public class ItemStackArrayValue extends AbstractValue { private ItemStack[] array = null; private int iteratorIndex; - private static final byte TAGLIST_ID = (new NBTTagList()).getId(); - private static final byte COMPOUND_ID = (new NBTTagCompound()).getId(); + private static final byte TAGLIST_ID = (new ListNBT()).getId(); + private static final byte COMPOUND_ID = (new CompoundNBT()).getId(); private static final String ARRAY_KEY = "Array"; private static final String INDEX_KEY = "Index"; @@ -70,43 +69,42 @@ public Object apply(Context context, Arguments arguments) { } @Override - public void load(NBTTagCompound nbt) { - if (nbt.hasKey(ARRAY_KEY, TAGLIST_ID)){ - NBTTagList tagList = nbt.getTagList(ARRAY_KEY,COMPOUND_ID); - this.array = new ItemStack[tagList.tagCount()]; - for (int i = 0; i < tagList.tagCount(); ++i){ - NBTTagCompound el = tagList.getCompoundTagAt(i); - if (el.hasNoTags()) + public void loadData(CompoundNBT nbt) { + if (nbt.contains(ARRAY_KEY, TAGLIST_ID)){ + ListNBT tagList = nbt.getList(ARRAY_KEY,COMPOUND_ID); + this.array = new ItemStack[tagList.size()]; + for (int i = 0; i < tagList.size(); ++i){ + CompoundNBT el = tagList.getCompound(i); + if (el.isEmpty()) this.array[i] = ItemStack.EMPTY; else - this.array[i] = new ItemStack(el); + this.array[i] = ItemStack.of(el); } } else { this.array = null; } - this.iteratorIndex = nbt.getInteger(INDEX_KEY); + this.iteratorIndex = nbt.getInt(INDEX_KEY); } @Override - public void save(NBTTagCompound nbt) { + public void saveData(CompoundNBT nbt) { - NBTTagCompound nullnbt = new NBTTagCompound(); + CompoundNBT nullnbt = new CompoundNBT(); if (this.array != null) { - NBTTagList nbttaglist = new NBTTagList(); + ListNBT nbttaglist = new ListNBT(); for (ItemStack stack : this.array) { if (stack != null) { - NBTBase nbttagcompound = stack.serializeNBT(); - nbttaglist.appendTag(nbttagcompound); + nbttaglist.add(stack.save(new CompoundNBT())); } else { - nbttaglist.appendTag(nullnbt); + nbttaglist.add(nullnbt); } } - nbt.setTag(ARRAY_KEY, nbttaglist); + nbt.put(ARRAY_KEY, nbttaglist); } - nbt.setInteger(INDEX_KEY, iteratorIndex); + nbt.putInt(INDEX_KEY, iteratorIndex); } @Callback(doc="function():nil -- Reset the iterator index so that the next call will return the first element.") diff --git a/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java b/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java index 5c42ff2142..d20fcd4dfc 100644 --- a/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java @@ -2,12 +2,12 @@ import li.cil.oc.api.manual.TabIconRenderer; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; +import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.opengl.GL13; /** * Simple implementation of a tab icon renderer using an item stack as its graphic. @@ -20,13 +20,13 @@ public ItemStackTabIconRenderer(ItemStack stack) { this.stack = stack; } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) @Override public void render() { - GlStateManager.enableRescaleNormal(); - RenderHelper.enableGUIStandardItemLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - Minecraft.getMinecraft().getRenderItem().renderItemAndEffectIntoGUI(stack, 0, 0); - RenderHelper.disableStandardItemLighting(); + RenderSystem.enableRescaleNormal(); + RenderHelper.turnOff(); + RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240, 240); + Minecraft.getInstance().getItemRenderer().renderAndDecorateItem(stack, 0, 0); + RenderHelper.turnOff(); } } diff --git a/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java b/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java index 44d5efe680..7d24042b24 100644 --- a/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java +++ b/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java @@ -43,7 +43,7 @@ public Iterable getContent(String path) { final ResourceLocation location = new ResourceLocation(resourceDomain, basePath + (path.startsWith("/") ? path.substring(1) : path)); InputStream is = null; try { - is = Minecraft.getMinecraft().getResourceManager().getResource(location).getInputStream(); + is = Minecraft.getInstance().getResourceManager().getResource(location).getInputStream(); final BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charsets.UTF_8)); final ArrayList lines = new ArrayList(); String line; diff --git a/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java b/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java index 3b7f45eff4..202f236f89 100644 --- a/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java @@ -2,13 +2,13 @@ import li.cil.oc.api.manual.TabIconRenderer; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import org.lwjgl.opengl.GL11; /** @@ -23,17 +23,16 @@ public TextureTabIconRenderer(ResourceLocation location) { } @Override - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) public void render() { - Minecraft.getMinecraft().getTextureManager().bindTexture(location); - GlStateManager.bindTexture(Minecraft.getMinecraft().getTextureManager().getTexture(location).getGlTextureId()); + Minecraft.getInstance().getTextureManager().bind(location); final Tessellator t = Tessellator.getInstance(); - final BufferBuilder r = t.getBuffer(); + final BufferBuilder r = t.getBuilder(); r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.pos(0, 16, 0).tex(0, 1).endVertex(); - r.pos(16, 16, 0).tex(1, 1).endVertex(); - r.pos(16, 0, 0).tex(1, 0).endVertex(); - r.pos(0, 0, 0).tex(0, 0).endVertex(); - t.draw(); + r.vertex(0, 16, 0).uv(0, 1).endVertex(); + r.vertex(16, 16, 0).uv(1, 1).endVertex(); + r.vertex(16, 0, 0).uv(1, 0).endVertex(); + r.vertex(0, 0, 0).uv(0, 0).endVertex(); + t.end(); } } diff --git a/src/main/java/li/cil/oc/api/prefab/TileEntityEnvironment.java b/src/main/java/li/cil/oc/api/prefab/TileEntityEnvironment.java index 010fd82423..ac1a15ad31 100644 --- a/src/main/java/li/cil/oc/api/prefab/TileEntityEnvironment.java +++ b/src/main/java/li/cil/oc/api/prefab/TileEntityEnvironment.java @@ -5,8 +5,10 @@ import li.cil.oc.api.network.Message; import li.cil.oc.api.network.Node; import li.cil.oc.api.network.Visibility; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; /** * TileEntities can implement the {@link li.cil.oc.api.network.Environment} @@ -58,6 +60,10 @@ public abstract class TileEntityEnvironment extends TileEntity implements Enviro protected Node node; // ----------------------------------------------------------------------- // + + public TileEntityEnvironment(TileEntityType type) { + super(type); + } @Override public Node node() { @@ -67,7 +73,7 @@ public Node node() { @Override public void onConnect(final Node node) { // This is called when the call to Network.joinOrCreateNetwork(this) in - // updateEntity was successful, in which case `node == this`. + // tick was successful, in which case `node == this`. // This is also called for any other node that gets connected to the // network our node is in, in which case `node` is the added node. // If our node is added to an existing network, this is called for each @@ -77,8 +83,8 @@ public void onConnect(final Node node) { @Override public void onDisconnect(final Node node) { // This is called when this node is removed from its network when the - // tile entity is removed from the world (see onChunkUnload() and - // invalidate()), in which case `node == this`. + // tile entity is removed from the world (see onChunkUnloaded() and + // setRemoved()), in which case `node == this`. // This is also called for each other node that gets removed from the // network our node is in, in which case `node` is the removed node. // If a net-split occurs this is called for each node that is no longer @@ -100,16 +106,16 @@ public void onLoad() { } @Override - public void onChunkUnload() { - super.onChunkUnload(); + public void onChunkUnloaded() { + super.onChunkUnloaded(); // Make sure to remove the node from its network when its environment, // meaning this tile entity, gets unloaded. if (node != null) node.remove(); } @Override - public void invalidate() { - super.invalidate(); + public void setRemoved() { + super.setRemoved(); // Make sure to remove the node from its network when its environment, // meaning this tile entity, gets unloaded. if (node != null) node.remove(); @@ -118,8 +124,8 @@ public void invalidate() { // ----------------------------------------------------------------------- // @Override - public void readFromNBT(final NBTTagCompound nbt) { - super.readFromNBT(nbt); + public void load(final BlockState state, final CompoundNBT nbt) { + super.load(state, nbt); // The host check may be superfluous for you. It's just there to allow // some special cases, where getNode() returns some node managed by // some other instance (for example when you have multiple internal @@ -129,18 +135,18 @@ public void readFromNBT(final NBTTagCompound nbt) { // to continue working without interruption across loads. If the // node is a power connector this is also required to restore the // internal energy buffer of the node. - node.load(nbt.getCompoundTag(TAG_NODE)); + node.loadData(nbt.getCompound(TAG_NODE)); } } @Override - public NBTTagCompound writeToNBT(final NBTTagCompound nbt) { - super.writeToNBT(nbt); - // See readFromNBT() regarding host check. + public CompoundNBT save(final CompoundNBT nbt) { + super.save(nbt); + // See load() regarding host check. if (node != null && node.host() == this) { - final NBTTagCompound nodeNbt = new NBTTagCompound(); - node.save(nodeNbt); - nbt.setTag(TAG_NODE, nodeNbt); + final CompoundNBT nodeNbt = new CompoundNBT(); + node.saveData(nodeNbt); + nbt.put(TAG_NODE, nodeNbt); } return nbt; } diff --git a/src/main/java/li/cil/oc/api/prefab/TileEntitySidedEnvironment.java b/src/main/java/li/cil/oc/api/prefab/TileEntitySidedEnvironment.java index c3d878d9ed..953685a04f 100644 --- a/src/main/java/li/cil/oc/api/prefab/TileEntitySidedEnvironment.java +++ b/src/main/java/li/cil/oc/api/prefab/TileEntitySidedEnvironment.java @@ -3,10 +3,12 @@ import li.cil.oc.api.Network; import li.cil.oc.api.network.Node; import li.cil.oc.api.network.SidedEnvironment; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.ITickable; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.Direction; /** * TileEntities can implement the {@link li.cil.oc.api.network.SidedEnvironment} @@ -19,11 +21,11 @@ * network as an index structure to find other nodes connected to them. */ @SuppressWarnings("UnusedDeclaration") -public abstract class TileEntitySidedEnvironment extends TileEntity implements SidedEnvironment, ITickable { +public abstract class TileEntitySidedEnvironment extends TileEntity implements SidedEnvironment, ITickableTileEntity { // See constructor. protected Node[] nodes = new Node[6]; - // See updateEntity(). + // See tick(). protected boolean addedToNetwork = false; /** @@ -60,7 +62,8 @@ public abstract class TileEntitySidedEnvironment extends TileEntity implements S * .create(), ...); * */ - protected TileEntitySidedEnvironment(final Node... nodes) { + protected TileEntitySidedEnvironment(TileEntityType type, final Node... nodes) { + super(type); System.arraycopy(nodes, 0, this.nodes, 0, Math.min(nodes.length, this.nodes.length)); } @@ -72,17 +75,17 @@ protected TileEntitySidedEnvironment(final Node... nodes) { // exists for a side won't work on the client. @Override - public Node sidedNode(final EnumFacing side) { + public Node sidedNode(final Direction side) { return nodes[side.ordinal()]; } // ----------------------------------------------------------------------- // @Override - public void update() { + public void tick() { // On the first update, try to add our node to nearby networks. We do - // this in the update logic, not in validate() because we need to access - // neighboring tile entities, which isn't possible in validate(). + // this in the update logic, not in clearRemoved() because we need to access + // neighboring tile entities, which isn't possible in clearRemoved(). // We could alternatively check node != null && node.network() == null, // but this has somewhat better performance, and makes it clearer. if (!addedToNetwork) { @@ -94,8 +97,8 @@ public void update() { } @Override - public void onChunkUnload() { - super.onChunkUnload(); + public void onChunkUnloaded() { + super.onChunkUnloaded(); // Make sure to remove the node from its network when its environment, // meaning this tile entity, gets unloaded. for (Node node : nodes) { @@ -104,8 +107,8 @@ public void onChunkUnload() { } @Override - public void invalidate() { - super.invalidate(); + public void setRemoved() { + super.setRemoved(); // Make sure to remove the node from its network when its environment, // meaning this tile entity, gets unloaded. for (Node node : nodes) { @@ -116,8 +119,8 @@ public void invalidate() { // ----------------------------------------------------------------------- // @Override - public void readFromNBT(final NBTTagCompound nbt) { - super.readFromNBT(nbt); + public void load(final BlockState state, final CompoundNBT nbt) { + super.load(state, nbt); int index = 0; for (Node node : nodes) { // The host check may be superfluous for you. It's just there to allow @@ -129,22 +132,22 @@ public void readFromNBT(final NBTTagCompound nbt) { // to continue working without interruption across loads. If the // node is a power connector this is also required to restore the // internal energy buffer of the node. - node.load(nbt.getCompoundTag("oc:node" + index)); + node.loadData(nbt.getCompound("oc:node" + index)); } ++index; } } @Override - public NBTTagCompound writeToNBT(NBTTagCompound nbt) { - super.writeToNBT(nbt); + public CompoundNBT save(CompoundNBT nbt) { + super.save(nbt); int index = 0; for (Node node : nodes) { - // See readFromNBT() regarding host check. + // See load() regarding host check. if (node != null && node.host() == this) { - final NBTTagCompound nodeNbt = new NBTTagCompound(); - node.save(nodeNbt); - nbt.setTag("oc:node" + index, nodeNbt); + final CompoundNBT nodeNbt = new CompoundNBT(); + node.saveData(nodeNbt); + nbt.put("oc:node" + index, nodeNbt); } ++index; } diff --git a/src/main/java/li/cil/oc/api/prefab/package-info.java b/src/main/java/li/cil/oc/api/prefab/package-info.java index a28cc87fba..8fb8970f61 100644 --- a/src/main/java/li/cil/oc/api/prefab/package-info.java +++ b/src/main/java/li/cil/oc/api/prefab/package-info.java @@ -7,10 +7,6 @@ * while leaving them in the same package with the same name and then ship them * with your mod! */ -@net.minecraftforge.fml.common.API( - owner = API.ID_OWNER, - provides = "opencomputersapi|prefab", - apiVersion = API.VERSION) package li.cil.oc.api.prefab; import li.cil.oc.api.API; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java b/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java index 1bfca877f6..0d1ab88e82 100644 --- a/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java +++ b/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java @@ -3,8 +3,8 @@ import li.cil.oc.api.Network; import li.cil.oc.util.SideTracker; import net.minecraft.tileentity.TileEntity; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.event.TickEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java b/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java index a73aa62056..2d5631b399 100644 --- a/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java +++ b/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java @@ -2,17 +2,19 @@ import li.cil.oc.api.network.Environment; import li.cil.oc.api.network.SimpleComponent; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; /** * This interface defines the names to which existing or placeholders for * existing methods will be moved. This allows transparent injection of our - * functionality, i.e. existing validate() etc. methods will be called as + * functionality, i.e. existing clearRemoved() etc. methods will be called as * if we didn't inject our code. *

    * Yes, the names are not "conventional", but that is by design, to avoid * naming collisions. */ +@Deprecated public interface SimpleComponentImpl extends Environment, SimpleComponent { public static final String PostFix = "_OpenComputers"; @@ -20,9 +22,9 @@ public interface SimpleComponentImpl extends Environment, SimpleComponent { void invalidate_OpenComputers(); - void onChunkUnload_OpenComputers(); + void onChunkUnloaded_OpenComputers(); - void readFromNBT_OpenComputers(NBTTagCompound nbt); + void readFromNBT_OpenComputers(BlockState state, CompoundNBT nbt); - NBTTagCompound writeToNBT_OpenComputers(NBTTagCompound nbt); + CompoundNBT writeToNBT_OpenComputers(CompoundNBT nbt); } diff --git a/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java b/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java index c866b4bc48..6697b85523 100644 --- a/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java +++ b/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java @@ -2,14 +2,20 @@ import li.cil.oc.api.network.Message; import li.cil.oc.api.network.Node; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; // This is a template implementation of methods injected into classes that are // marked for component functionality. These methods will be copied into tile // entities marked as simple components as necessary by the class transformer. @SuppressWarnings("unused") public abstract class SimpleEnvironment extends TileEntity implements SimpleComponentImpl { + public SimpleEnvironment(TileEntityType type) { + super(type); + } + @Override public Node node() { return StaticSimpleEnvironment.node(this); @@ -33,28 +39,28 @@ public void onMessage(Message message) { // instead of plain overwriting them. @Override - public void validate() { + public void clearRemoved() { StaticSimpleEnvironment.validate(this); } @Override - public void invalidate() { + public void setRemoved() { StaticSimpleEnvironment.invalidate(this); } @Override - public void onChunkUnload() { - StaticSimpleEnvironment.onChunkUnload(this); + public void onChunkUnloaded() { + StaticSimpleEnvironment.onChunkUnloaded(this); } @Override - public void readFromNBT(NBTTagCompound nbt) { - StaticSimpleEnvironment.readFromNBT(this, nbt); + public void load(BlockState state, CompoundNBT nbt) { + StaticSimpleEnvironment.load(this, state, nbt); } @Override - public NBTTagCompound writeToNBT(NBTTagCompound nbt) { - return StaticSimpleEnvironment.writeToNBT(this, nbt); + public CompoundNBT save(CompoundNBT nbt) { + return StaticSimpleEnvironment.save(this, nbt); } // The following methods are only injected if their real versions do not @@ -64,22 +70,22 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { // them through an interface, and need no runtime reflection. public void validate_OpenComputers() { - super.validate(); + super.clearRemoved(); } public void invalidate_OpenComputers() { - super.invalidate(); + super.setRemoved(); } - public void onChunkUnload_OpenComputers() { - super.onChunkUnload(); + public void onChunkUnloaded_OpenComputers() { + super.onChunkUnloaded(); } - public void readFromNBT_OpenComputers(NBTTagCompound nbt) { - super.readFromNBT(nbt); + public void readFromNBT_OpenComputers(BlockState state, CompoundNBT nbt) { + super.load(state, nbt); } - public NBTTagCompound writeToNBT_OpenComputers(NBTTagCompound nbt) { - return super.writeToNBT(nbt); + public CompoundNBT writeToNBT_OpenComputers(CompoundNBT nbt) { + return super.save(nbt); } } diff --git a/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java b/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java index 82d2b81d82..3d5eb86933 100644 --- a/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java +++ b/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java @@ -7,7 +7,8 @@ import li.cil.oc.api.network.Visibility; import li.cil.oc.common.asm.SimpleComponentTickHandler; import li.cil.oc.util.SideTracker; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; import java.util.HashMap; @@ -61,8 +62,8 @@ public static void invalidate(final SimpleComponentImpl self) { } } - public static void onChunkUnload(final SimpleComponentImpl self) { - self.onChunkUnload_OpenComputers(); + public static void onChunkUnloaded(final SimpleComponentImpl self) { + self.onChunkUnloaded_OpenComputers(); final Node node = node(self); if (node != null) { node.remove(); @@ -70,21 +71,21 @@ public static void onChunkUnload(final SimpleComponentImpl self) { } } - public static void readFromNBT(final SimpleComponentImpl self, NBTTagCompound nbt) { - self.readFromNBT_OpenComputers(nbt); + public static void load(final SimpleComponentImpl self, BlockState state, CompoundNBT nbt) { + self.readFromNBT_OpenComputers(state, nbt); final Node node = node(self); if (node != null) { - node.load(nbt.getCompoundTag("oc:node")); + node.loadData(nbt.getCompound("oc:node")); } } - public static NBTTagCompound writeToNBT(final SimpleComponentImpl self, NBTTagCompound nbt) { + public static CompoundNBT save(final SimpleComponentImpl self, CompoundNBT nbt) { nbt = self.writeToNBT_OpenComputers(nbt); final Node node = node(self); if (node != null) { - final NBTTagCompound nodeNbt = new NBTTagCompound(); - node.save(nodeNbt); - nbt.setTag("oc:node", nodeNbt); + final CompoundNBT nodeNbt = new CompoundNBT(); + node.saveData(nodeNbt); + nbt.put("oc:node", nodeNbt); } return nbt; } diff --git a/src/main/java/li/cil/oc/util/SideTracker.java b/src/main/java/li/cil/oc/util/SideTracker.java index 1a0f302ee7..43bf69c0ab 100644 --- a/src/main/java/li/cil/oc/util/SideTracker.java +++ b/src/main/java/li/cil/oc/util/SideTracker.java @@ -1,6 +1,6 @@ package li.cil.oc.util; -import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.forgespi.Environment; import java.util.Collections; import java.util.Set; @@ -13,7 +13,7 @@ public static void addServerThread() { } public static boolean isServer() { - return FMLCommonHandler.instance().getEffectiveSide().isServer() || serverThreads.contains(Thread.currentThread()); + return Environment.get().getDist().isDedicatedServer() || serverThreads.contains(Thread.currentThread()); } public static boolean isClient() { diff --git a/src/main/resources/META-INF/oc_at.cfg b/src/main/resources/META-INF/oc_at.cfg index 1c841fa0fb..564c1daff7 100644 --- a/src/main/resources/META-INF/oc_at.cfg +++ b/src/main/resources/META-INF/oc_at.cfg @@ -1,11 +1,13 @@ # OpenComputers access transformer config -public net.minecraft.client.gui.inventory.GuiContainer field_146999_f #xSize -public net.minecraft.client.gui.inventory.GuiContainer field_147000_g #ySize -public net.minecraft.client.gui.inventory.GuiContainer field_147003_i #guiLeft -public net.minecraft.client.gui.inventory.GuiContainer field_147009_r #guiTop -public net.minecraft.block.BlockPistonBase func_176319_a(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;Z)Z # doMove -public net.minecraft.client.audio.SoundManager field_148620_e #sndSystem -public net.minecraft.client.audio.SoundManager$SoundSystemStarterThread -#public net.minecraft.enchantment.Enchantment field_180311_a # enchantmentsList -public net.minecraft.tileentity.MobSpawnerBaseLogic func_190895_g()Lnet/minecraft/util/ResourceLocation; # getEntityId -public net.minecraft.entity.Entity func_180432_n(Lnet/minecraft/entity/Entity;)V # copyDataFromOld +public net.minecraft.block.PistonBlock func_176319_a(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Z)Z # moveBlocks +public-f net.minecraft.entity.player.PlayerEntity field_71069_bz # inventoryMenu +public-f net.minecraft.entity.player.PlayerEntity field_71071_by # inventory +public net.minecraft.tileentity.AbstractFurnaceTileEntity field_214018_j # litTime +public net.minecraft.tileentity.AbstractFurnaceTileEntity field_214019_k # litDuration +public net.minecraft.tileentity.AbstractFurnaceTileEntity field_214020_l # cookingProgress +public net.minecraft.tileentity.AbstractFurnaceTileEntity field_214021_m # cookingTotalTime +public net.minecraft.tileentity.BeaconTileEntity field_146010_n # secondaryPower +public net.minecraft.tileentity.BeaconTileEntity field_146013_m # primaryPower +public net.minecraft.tileentity.BrewingStandTileEntity field_145946_k # brewTime +public net.minecraft.tileentity.SignTileEntity field_145915_a # messages +public net.minecraft.world.spawner.AbstractSpawner func_190895_g()Lnet/minecraft/util/ResourceLocation; # getEntityId diff --git a/src/main/resources/assets/opencomputers/sounds/preload.cfg b/src/main/resources/assets/opencomputers/sounds/preload.cfg deleted file mode 100644 index e7334d6c89..0000000000 --- a/src/main/resources/assets/opencomputers/sounds/preload.cfg +++ /dev/null @@ -1,16 +0,0 @@ -assets/opencomputers/sounds/computer_running.ogg -assets/opencomputers/sounds/floppy_access1.ogg -assets/opencomputers/sounds/floppy_access2.ogg -assets/opencomputers/sounds/floppy_access3.ogg -assets/opencomputers/sounds/floppy_access4.ogg -assets/opencomputers/sounds/floppy_access5.ogg -assets/opencomputers/sounds/floppy_access6.ogg -assets/opencomputers/sounds/floppy_eject.ogg -assets/opencomputers/sounds/floppy_insert.ogg -assets/opencomputers/sounds/hdd_access1.ogg -assets/opencomputers/sounds/hdd_access2.ogg -assets/opencomputers/sounds/hdd_access3.ogg -assets/opencomputers/sounds/hdd_access4.ogg -assets/opencomputers/sounds/hdd_access5.ogg -assets/opencomputers/sounds/hdd_access6.ogg -assets/opencomputers/sounds/hdd_access7.ogg \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/CreativeTab.scala b/src/main/scala/li/cil/oc/CreativeTab.scala index 1a54db307c..3137a73def 100644 --- a/src/main/scala/li/cil/oc/CreativeTab.scala +++ b/src/main/scala/li/cil/oc/CreativeTab.scala @@ -1,11 +1,9 @@ package li.cil.oc -import net.minecraft.creativetab.CreativeTabs +import net.minecraft.item.ItemGroup -object CreativeTab extends CreativeTabs(CreativeTabs.getNextID, OpenComputers.Name) { +object CreativeTab extends ItemGroup(-1, OpenComputers.Name) { private lazy val stack = api.Items.get(Constants.BlockName.CaseTier1).createItemStack(1) - override def getTabIconItem = stack - - override def getTranslatedTabLabel = getTabLabel + override def makeIcon = stack } diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index d66c0be78a..41ab2281a4 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -1,13 +1,8 @@ package li.cil.oc -import li.cil.oc.client.CommandHandler.SetClipboardCommand -import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.TextComponentString -import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.util.text._ import net.minecraft.util.text.event.ClickEvent import net.minecraft.util.text.event.HoverEvent -import net.minecraft.util.text.translation.I18n -import net.minecraftforge.fml.common.event.FMLFingerprintViolationEvent import scala.util.matching.Regex @@ -16,47 +11,57 @@ object Localization { private def resolveKey(key: String) = if (canLocalize(Settings.namespace + key)) Settings.namespace + key else key - def canLocalize(key: String): Boolean = I18n.canTranslate(key) + def canLocalize(key: String): Boolean = LanguageMap.getInstance.has(key) - def localizeLater(formatKey: String, values: AnyRef*) = new TextComponentTranslation(resolveKey(formatKey), values: _*) + def localizeLater(formatKey: String, values: AnyRef*) = new TranslationTextComponent(resolveKey(formatKey), values: _*) - def localizeLater(key: String) = new TextComponentTranslation(resolveKey(key)) + def localizeLater(key: String) = new TranslationTextComponent(resolveKey(key)) - def localizeImmediately(formatKey: String, values: AnyRef*): String = I18n.translateToLocalFormatted(resolveKey(formatKey), values: _*).split(nl).map(_.trim).mkString("\n") + def localizeImmediately(formatKey: String, values: AnyRef*): String = { + val k = resolveKey(formatKey) + var lm = LanguageMap.getInstance + if (!lm.has(k)) return k + String.format(lm.getOrDefault(k), values: _*).split(nl).map(_.trim).mkString("\n") + } - def localizeImmediately(key: String): String = I18n.translateToLocal(resolveKey(key)).split(nl).map(_.trim).mkString("\n") + def localizeImmediately(key: String): String = { + val k = resolveKey(key) + var lm = LanguageMap.getInstance + if (!lm.has(k)) return k + lm.getOrDefault(k).split(nl).map(_.trim).mkString("\n") + } object Analyzer { - def Address(value: String): TextComponentTranslation = { + def Address(value: String): IFormattableTextComponent = { val result = localizeLater("gui.Analyzer.Address", value) - result.getStyle.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, s"/${SetClipboardCommand.name} $value")) - result.getStyle.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, localizeLater("gui.Analyzer.CopyToClipboard"))) - result + result.setStyle(result.getStyle + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, value)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, localizeLater("gui.Analyzer.CopyToClipboard")))) } - def AddressCopied: TextComponentTranslation = localizeLater("gui.Analyzer.AddressCopied") + def AddressCopied: TranslationTextComponent = localizeLater("gui.Analyzer.AddressCopied") - def ChargerSpeed(value: Double): TextComponentTranslation = localizeLater("gui.Analyzer.ChargerSpeed", (value * 100).toInt + "%") + def ChargerSpeed(value: Double): TranslationTextComponent = localizeLater("gui.Analyzer.ChargerSpeed", (value * 100).toInt + "%") - def ComponentName(value: String): TextComponentTranslation = localizeLater("gui.Analyzer.ComponentName", value) + def ComponentName(value: String): TranslationTextComponent = localizeLater("gui.Analyzer.ComponentName", value) - def Components(count: Int, maxCount: Int): TextComponentTranslation = localizeLater("gui.Analyzer.Components", count + "/" + maxCount) + def Components(count: Int, maxCount: Int): TranslationTextComponent = localizeLater("gui.Analyzer.Components", count + "/" + maxCount) - def LastError(value: String): TextComponentTranslation = localizeLater("gui.Analyzer.LastError", localizeLater(value)) + def LastError(value: String): TranslationTextComponent = localizeLater("gui.Analyzer.LastError", localizeLater(value)) - def RobotOwner(owner: String): TextComponentTranslation = localizeLater("gui.Analyzer.RobotOwner", owner) + def RobotOwner(owner: String): TranslationTextComponent = localizeLater("gui.Analyzer.RobotOwner", owner) - def RobotName(name: String): TextComponentTranslation = localizeLater("gui.Analyzer.RobotName", name) + def RobotName(name: String): TranslationTextComponent = localizeLater("gui.Analyzer.RobotName", name) - def RobotXp(experience: Double, level: Int): TextComponentTranslation = localizeLater("gui.Analyzer.RobotXp", f"$experience%.2f", level.toString) + def RobotXp(experience: Double, level: Int): TranslationTextComponent = localizeLater("gui.Analyzer.RobotXp", f"$experience%.2f", level.toString) - def StoredEnergy(value: String): TextComponentTranslation = localizeLater("gui.Analyzer.StoredEnergy", value) + def StoredEnergy(value: String): TranslationTextComponent = localizeLater("gui.Analyzer.StoredEnergy", value) - def TotalEnergy(value: String): TextComponentTranslation = localizeLater("gui.Analyzer.TotalEnergy", value) + def TotalEnergy(value: String): TranslationTextComponent = localizeLater("gui.Analyzer.TotalEnergy", value) - def Users(list: Iterable[String]): TextComponentTranslation = localizeLater("gui.Analyzer.Users", list.mkString(", ")) + def Users(list: Iterable[String]): TranslationTextComponent = localizeLater("gui.Analyzer.Users", list.mkString(", ")) - def WirelessStrength(value: Double): TextComponentTranslation = localizeLater("gui.Analyzer.WirelessStrength", value.toInt.toString) + def WirelessStrength(value: Double): TranslationTextComponent = localizeLater("gui.Analyzer.WirelessStrength", value.toInt.toString) } object Assembler { @@ -64,13 +69,13 @@ object Localization { def CollectResult: String = localizeImmediately("gui.Assembler.Collect") - def InsertCPU: TextComponentTranslation = localizeLater("gui.Assembler.InsertCPU") + def InsertCPU: TranslationTextComponent = localizeLater("gui.Assembler.InsertCPU") - def InsertRAM: TextComponentTranslation = localizeLater("gui.Assembler.InsertRAM") + def InsertRAM: TranslationTextComponent = localizeLater("gui.Assembler.InsertRAM") def Complexity(complexity: Int, maxComplexity: Int): ITextComponent = { val message = localizeLater("gui.Assembler.Complexity", complexity.toString, maxComplexity.toString) - if (complexity > maxComplexity) new TextComponentString("§4").appendSibling(message) + if (complexity > maxComplexity) new StringTextComponent("§4").append(message) else message } @@ -78,29 +83,27 @@ object Localization { def Progress(progress: Double, timeRemaining: String): String = localizeImmediately("gui.Assembler.Progress", progress.toInt.toString, timeRemaining) - def Warning(name: String): ITextComponent = new TextComponentString("§7- ").appendSibling(localizeLater("gui.Assembler.Warning." + name)) + def Warning(name: String): ITextComponent = new StringTextComponent("§7- ").append(localizeLater("gui.Assembler.Warning." + name)) - def Warnings: TextComponentTranslation = localizeLater("gui.Assembler.Warnings") + def Warnings: TranslationTextComponent = localizeLater("gui.Assembler.Warnings") } object Chat { - def WarningLuaFallback: ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningLuaFallback")) - - def WarningProjectRed: ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningProjectRed")) + def WarningLuaFallback: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningLuaFallback")) - def WarningFingerprint(event: FMLFingerprintViolationEvent): ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningFingerprint", event.getExpectedFingerprint, event.getFingerprints.toArray.mkString(", "))) + def WarningProjectRed: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningProjectRed")) - def WarningRecipes: ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningRecipes")) + def WarningRecipes: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningRecipes")) - def WarningClassTransformer: ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningClassTransformer")) + def WarningClassTransformer: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningClassTransformer")) - def WarningSimpleComponent: ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningSimpleComponent")) + def WarningSimpleComponent: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningSimpleComponent")) - def WarningLink(url: String): ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.WarningLink", url)) + def WarningLink(url: String): ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningLink", url)) - def InfoNewVersion(version: String): ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.NewVersion", version)) + def InfoNewVersion(version: String): ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.NewVersion", version)) - def TextureName(name: String): ITextComponent = new TextComponentString("§aOpenComputers§f: ").appendSibling(localizeLater("gui.Chat.TextureName", name)) + def TextureName(name: String): ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.TextureName", name)) } object Computer { @@ -156,9 +159,9 @@ object Localization { } object Terminal { - def InvalidKey: TextComponentTranslation = localizeLater("gui.Terminal.InvalidKey") + def InvalidKey: TranslationTextComponent = localizeLater("gui.Terminal.InvalidKey") - def OutOfRange: TextComponentTranslation = localizeLater("gui.Terminal.OutOfRange") + def OutOfRange: TranslationTextComponent = localizeLater("gui.Terminal.OutOfRange") } object Tooltip { diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 51d86e2729..463db11e29 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -2,62 +2,72 @@ package li.cil.oc import li.cil.oc.common.IMC import li.cil.oc.common.Proxy -import li.cil.oc.server.command.CommandHandler -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventHandler -import net.minecraftforge.fml.common.SidedProxy -import net.minecraftforge.fml.common.event.FMLInterModComms.IMCEvent -import net.minecraftforge.fml.common.event._ -import net.minecraftforge.fml.common.network.FMLEventChannel import li.cil.oc.util.ThreadPoolFactory +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.world.World +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.event.RegisterCommandsEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.forgespi.Environment +import net.minecraftforge.fml.InterModComms +import net.minecraftforge.fml.ModContainer +import net.minecraftforge.fml.ModLoadingContext +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.event.lifecycle._ +import net.minecraftforge.fml.event.server._ +import net.minecraftforge.fml.network.simple.SimpleChannel import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger +import scala.collection.convert.WrapAsScala._ + @Mod(OpenComputers.ID) object OpenComputers { final val ID = "opencomputers" final val Name = "OpenComputers" - def log: Logger = logger.getOrElse(LogManager.getLogger(Name)) + val Version = ModLoadingContext.get.getActiveContainer.getModInfo.getVersion - var logger: Option[Logger] = None + final val log: Logger = LogManager.getLogger(Name) - @SidedProxy(clientSide = "li.cil.oc.client.Proxy", serverSide = "li.cil.oc.server.Proxy") - var proxy: Proxy = null + lazy val proxy: Proxy = { + val cls = Environment.get.getDist match { + case Dist.CLIENT => Class.forName("li.cil.oc.client.Proxy") + case _ => Class.forName("li.cil.oc.common.Proxy") + } + cls.getConstructor().newInstance().asInstanceOf[Proxy] + } - var channel: FMLEventChannel = _ + val modContainer: ModContainer = ModLoadingContext.get.getActiveContainer - @EventHandler - def preInit(e: FMLPreInitializationEvent) { - logger = Option(e.getModLog) - proxy.preInit(e) - OpenComputers.log.info("Done with pre init phase.") - } + var channel: SimpleChannel = null - @EventHandler - def init(e: FMLInitializationEvent): Unit = { - proxy.init(e) - OpenComputers.log.info("Done with init phase.") - } + MinecraftForge.EVENT_BUS.register(this) + + @Deprecated + def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = proxy.openGui(player, guiId, world, x, y, z) - @EventHandler - def postInit(e: FMLPostInitializationEvent): Unit = { - proxy.postInit(e) - OpenComputers.log.info("Done with post init phase.") + @SubscribeEvent + def commonInit(e: FMLCommonSetupEvent): Unit = { + proxy.preInit(e) + proxy.init(e) } - @EventHandler + @SubscribeEvent def serverStart(e: FMLServerStartingEvent): Unit = { - CommandHandler.register(e) ThreadPoolFactory.safePools.foreach(_.newThreadPool()) } - @EventHandler + @SubscribeEvent def serverStop(e: FMLServerStoppedEvent): Unit = { ThreadPoolFactory.safePools.foreach(_.waitForCompletion()) } - @EventHandler - def imc(e: IMCEvent): Unit = IMC.handleEvent(e) + @SubscribeEvent + def imc(e: InterModProcessEvent): Unit = InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) + + @SubscribeEvent + def loadComplete(e: FMLLoadCompleteEvent): Unit = proxy.postInit(e) } diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index d632a68283..893e656be3 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -4,6 +4,7 @@ import java.io._ import java.net.Inet4Address import java.net.InetAddress import java.nio.charset.StandardCharsets +import java.nio.file.Paths import java.security.SecureRandom import java.util.UUID @@ -14,11 +15,11 @@ import li.cil.oc.Settings.DebugCardAccess import li.cil.oc.common.Tier import li.cil.oc.server.component.DebugCard import li.cil.oc.server.component.DebugCard.AccessContext +import net.minecraftforge.fml.loading.FMLPaths import org.apache.commons.codec.binary.Hex -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion -import net.minecraftforge.fml.common.versioning.VersionRange import org.apache.commons.lang3.StringEscapeUtils +import org.apache.maven.artifact.versioning.DefaultArtifactVersion +import org.apache.maven.artifact.versioning.VersionRange import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -455,8 +456,7 @@ class Settings(val config: Config) { case "true" | "allow" | java.lang.Boolean.TRUE => DebugCardAccess.Allowed case "false" | "deny" | java.lang.Boolean.FALSE => DebugCardAccess.Forbidden case "whitelist" => - val wlFile = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator + - "debug_card_whitelist.txt") + val wlFile = FMLPaths.CONFIGDIR.get.resolve(Paths.get("opencomputers", "debug_card_whitelist.txt")).toFile() DebugCardAccess.Whitelist(wlFile) @@ -483,7 +483,7 @@ class Settings(val config: Config) { } object Settings { - val resourceDomain = "opencomputers" + val resourceDomain = OpenComputers.ID val namespace = "oc:" val savePath = "opencomputers/" val scriptPath: String = "/assets/" + resourceDomain + "/lua/" @@ -572,12 +572,12 @@ object Settings { // created by) against the current version to see if some hard changes // were made. If so, the new default values are copied over. private def patchConfig(config: Config, defaults: Config) = { - val mod = Loader.instance.activeModContainer + val modVersion = OpenComputers.modContainer.getModInfo.getVersion val prefix = "opencomputers." val configVersion = new DefaultArtifactVersion(if (config.hasPath(prefix + "version")) config.getString(prefix + "version") else "0.0.0") var patched = config - if (configVersion.compareTo(mod.getProcessedVersion) != 0) { - OpenComputers.log.info(s"Updating config from version '${configVersion.getVersionString}' to '${defaults.getString(prefix + "version")}'.") + if (!configVersion.equals(modVersion)) { + OpenComputers.log.info(s"Updating config from version '${configVersion}' to '${defaults.getString(prefix + "version")}'.") patched = patched.withValue(prefix + "version", defaults.getValue(prefix + "version")) for ((version, paths) <- configPatches if version.containsVersion(configVersion)) { for (path <- paths) { diff --git a/src/main/scala/li/cil/oc/client/ColorHandler.scala b/src/main/scala/li/cil/oc/client/ColorHandler.scala index 8430feda31..0ae6e82c10 100644 --- a/src/main/scala/li/cil/oc/client/ColorHandler.scala +++ b/src/main/scala/li/cil/oc/client/ColorHandler.scala @@ -8,15 +8,17 @@ import li.cil.oc.util.Color import li.cil.oc.util.ItemColorizer import li.cil.oc.util.ItemUtils import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.color.IBlockColor import net.minecraft.client.renderer.color.IItemColor -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.DyeColor import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.util.IItemProvider import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockDisplayReader +import net.minecraft.world.IBlockReader object ColorHandler { def init(): Unit = { @@ -26,7 +28,7 @@ object ColorHandler { }, api.Items.get(Constants.BlockName.Cable).block()) - register((state, world, pos, tintIndex) => if (pos == null) 0xFFFFFFFF else world.getTileEntity(pos) match { + register((state, world, pos, tintIndex) => if (pos == null) 0xFFFFFFFF else world.getBlockEntity(pos) match { case colored: Colored => colored.getColor case _ => state.getBlock match { case block: block.Case => Color.rgbValues(Color.byTier(block.tier)) @@ -38,7 +40,7 @@ object ColorHandler { api.Items.get(Constants.BlockName.CaseTier3).block(), api.Items.get(Constants.BlockName.CaseCreative).block()) - register((state, world, pos, tintIndex) => Color.rgbValues(Color.byOreName(Color.dyes(state.getBlock.getMetaFromState(state) max 0 min Color.dyes.length))), + register((state, world, pos, tintIndex) => Color.rgbValues(state.getValue(block.ChameliumBlock.Color)), api.Items.get(Constants.BlockName.ChameliumBlock).block()) register((state, world, pos, tintIndex) => tintIndex, @@ -53,23 +55,23 @@ object ColorHandler { api.Items.get(Constants.BlockName.ScreenTier3).block()) register((stack, tintIndex) => if (ItemColorizer.hasColor(stack)) ItemColorizer.getColor(stack) else tintIndex, - Item.getItemFromBlock(api.Items.get(Constants.BlockName.Cable).block())) + api.Items.get(Constants.BlockName.Cable).block()) register((stack, tintIndex) => Color.rgbValues(Color.byTier(ItemUtils.caseTier(stack))), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.CaseTier1).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.CaseTier2).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.CaseTier3).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.CaseCreative).block())) + api.Items.get(Constants.BlockName.CaseTier1).block(), + api.Items.get(Constants.BlockName.CaseTier2).block(), + api.Items.get(Constants.BlockName.CaseTier3).block(), + api.Items.get(Constants.BlockName.CaseCreative).block()) - register((stack, tintIndex) => Color.rgbValues(EnumDyeColor.byDyeDamage(stack.getItemDamage)), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.ChameliumBlock).block())) + register((stack, tintIndex) => Color.rgbValues(DyeColor.byId(stack.getDamageValue)), + api.Items.get(Constants.BlockName.ChameliumBlock).block()) register((stack, tintIndex) => tintIndex, - Item.getItemFromBlock(api.Items.get(Constants.BlockName.ScreenTier1).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.ScreenTier2).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.ScreenTier3).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.Print).block()), - Item.getItemFromBlock(api.Items.get(Constants.BlockName.Robot).block())) + api.Items.get(Constants.BlockName.ScreenTier1).block(), + api.Items.get(Constants.BlockName.ScreenTier2).block(), + api.Items.get(Constants.BlockName.ScreenTier3).block(), + api.Items.get(Constants.BlockName.Print).block(), + api.Items.get(Constants.BlockName.Robot).block()) register((stack, tintIndex) => if (tintIndex == 1) { @@ -78,15 +80,15 @@ object ColorHandler { api.Items.get(Constants.ItemName.HoverBoots).item()) } - def register(handler: (IBlockState, IBlockAccess, BlockPos, Int) => Int, blocks: Block*): Unit = { - Minecraft.getMinecraft.getBlockColors.registerBlockColorHandler(new IBlockColor { - override def colorMultiplier(state: IBlockState, world: IBlockAccess, pos: BlockPos, tintIndex: Int): Int = handler(state, world, pos, tintIndex) + def register(handler: (BlockState, IBlockReader, BlockPos, Int) => Int, blocks: Block*): Unit = { + Minecraft.getInstance.getBlockColors.register(new IBlockColor { + override def getColor(state: BlockState, world: IBlockDisplayReader, pos: BlockPos, tintIndex: Int): Int = handler(state, world, pos, tintIndex) }, blocks: _*) } - def register(handler: (ItemStack, Int) => Int, items: Item*): Unit = { - Minecraft.getMinecraft.getItemColors.registerItemColorHandler(new IItemColor { - override def colorMultiplier(stack: ItemStack, tintIndex: Int): Int = handler(stack, tintIndex) + def register(handler: (ItemStack, Int) => Int, items: IItemProvider*): Unit = { + Minecraft.getInstance.getItemColors.register(new IItemColor { + override def getColor(stack: ItemStack, tintIndex: Int): Int = handler(stack, tintIndex) }, items: _*) } } diff --git a/src/main/scala/li/cil/oc/client/CommandHandler.scala b/src/main/scala/li/cil/oc/client/CommandHandler.scala deleted file mode 100644 index 6ea1bfb835..0000000000 --- a/src/main/scala/li/cil/oc/client/CommandHandler.scala +++ /dev/null @@ -1,32 +0,0 @@ -package li.cil.oc.client - -import li.cil.oc.common.command.SimpleCommand -import net.minecraft.client.gui.GuiScreen -import net.minecraft.command.ICommandSender -import net.minecraft.server.MinecraftServer -import net.minecraftforge.client.ClientCommandHandler - -object CommandHandler { - def register(): Unit = { - ClientCommandHandler.instance.registerCommand(SetClipboardCommand) - } - - object SetClipboardCommand extends SimpleCommand("oc_setclipboard") { - override def getUsage(source: ICommandSender): String = name + " " - - override def execute(server: MinecraftServer, source: ICommandSender, command: Array[String]): Unit = { - if (source.getEntityWorld.isRemote && command != null && command.length > 0) { - GuiScreen.setClipboardString(command(0)) - } - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 0 - } - -} diff --git a/src/main/scala/li/cil/oc/client/ComponentTracker.scala b/src/main/scala/li/cil/oc/client/ComponentTracker.scala index 42a6950794..6cc54342dc 100644 --- a/src/main/scala/li/cil/oc/client/ComponentTracker.scala +++ b/src/main/scala/li/cil/oc/client/ComponentTracker.scala @@ -4,5 +4,5 @@ import li.cil.oc.common import net.minecraft.world.World object ComponentTracker extends common.ComponentTracker { - override protected def clear(world: World) = if (world.isRemote) super.clear(world) + override protected def clear(world: World) = if (world.isClientSide) super.clear(world) } diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index bf9736f932..5f03fc2eeb 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -11,66 +11,67 @@ import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventor import li.cil.oc.common.item import li.cil.oc.common.item.Delegator import li.cil.oc.common.tileentity +import li.cil.oc.common.tileentity.traits.TileEntity import li.cil.oc.common.{GuiHandler => CommonGuiHandler} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.tileentity.TileEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.world.World import net.minecraft.item.ItemStack +@Deprecated object GuiHandler extends CommonGuiHandler { - override def getClientGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int): AnyRef = { + override def getClientGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = { GuiType.Categories.get(id) match { case Some(GuiType.Category.Block) => - world.getTileEntity(BlockPosition(x, GuiType.extractY(y), z)) match { + world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { case t: tileentity.Adapter if id == GuiType.Adapter.id => - new gui.Adapter(player.inventory, t) + new gui.Adapter(containerId, player.inventory, t) case t: tileentity.Assembler if id == GuiType.Assembler.id => - new gui.Assembler(player.inventory, t) + new gui.Assembler(containerId, player.inventory, t) case t: tileentity.Case if id == GuiType.Case.id => - new gui.Case(player.inventory, t) + new gui.Case(containerId, player.inventory, t) case t: tileentity.Charger if id == GuiType.Charger.id => - new gui.Charger(player.inventory, t) + new gui.Charger(containerId, player.inventory, t) case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - new gui.Disassembler(player.inventory, t) + new gui.Disassembler(containerId, player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - new gui.DiskDrive(player.inventory, t) + new gui.DiskDrive(containerId, player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => - new gui.Printer(player.inventory, t) + new gui.Printer(containerId, player.inventory, t) case t: tileentity.Rack if id == GuiType.Rack.id => - new gui.Rack(player.inventory, t) + new gui.Rack(containerId, player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => - new gui.Raid(player.inventory, t) + new gui.Raid(containerId, player.inventory, t) case t: tileentity.Relay if id == GuiType.Relay.id => - new gui.Relay(player.inventory, t) + new gui.Relay(containerId, player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new gui.Robot(player.inventory, t.robot) + new gui.Robot(containerId, player.inventory, t.robot) case t: tileentity.Screen if id == GuiType.Screen.id => new gui.Screen(t.origin.buffer, t.tier > 0, () => t.origin.hasKeyboard, () => t.origin.buffer.isRenderingEnabled) case t: tileentity.Rack if id == GuiType.ServerInRack.id => val slot = GuiType.extractSlot(y) - new gui.Server(player.inventory, new ServerInventory { - override def container = t.getStackInSlot(slot) + new gui.Server(containerId, player.inventory, new ServerInventory { + override def container = t.getItem(slot) - override def isUsableByPlayer(player: EntityPlayer) = t.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity) = t.stillValid(player) }, Option(t), slot) case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => val slot = GuiType.extractSlot(y) - new gui.DiskDrive(player.inventory, new DiskDriveMountableInventory { - override def container: ItemStack = t.getStackInSlot(slot) + new gui.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { + override def container: ItemStack = t.getItem(slot) - override def isUsableByPlayer(player: EntityPlayer): Boolean = t.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity): Boolean = t.stillValid(player) }) case t: tileentity.Waypoint if id == GuiType.Waypoint.id => new gui.Waypoint(t) case _ => null } case Some(GuiType.Category.Entity) => - world.getEntityByID(x) match { + world.getEntity(x) match { case drone: entity.Drone if id == GuiType.Drone.id => - new gui.Drone(player.inventory, drone) + new gui.Drone(containerId, player.inventory, drone) case _ => null } case Some(GuiType.Category.Item) => { @@ -79,20 +80,20 @@ object GuiHandler extends CommonGuiHandler { case Some(drive: item.traits.FileSystemLike) if id == GuiType.Drive.id => new gui.Drive(player.inventory, () => itemStackInUse) case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => - new gui.Database(player.inventory, new DatabaseInventory { + new gui.Database(containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse - override def isUsableByPlayer(player: EntityPlayer) = player == player + override def stillValid(player: PlayerEntity) = player == player }) case Some(server: item.Server) if id == GuiType.Server.id => - new gui.Server(player.inventory, new ServerInventory { + new gui.Server(containerId, player.inventory, new ServerInventory { override def container = itemStackInUse - override def isUsableByPlayer(player: EntityPlayer) = player == player + override def stillValid(player: PlayerEntity) = player == player }) case Some(tablet: item.Tablet) if id == GuiType.Tablet.id => val stack = itemStackInUse - if (stack.hasTagCompound) { + if (stack.hasTag) { item.Tablet.get(stack, player).components.collect { case Some(buffer: api.internal.TextBuffer) => buffer }.headOption match { @@ -103,43 +104,39 @@ object GuiHandler extends CommonGuiHandler { else null case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => val stack = itemStackInUse - if (stack.hasTagCompound) { - new gui.Tablet(player.inventory, item.Tablet.get(stack, player)) + if (stack.hasTag) { + new gui.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) } else null case Some(_: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => - new gui.DiskDrive(player.inventory, new DiskDriveMountableInventory { + new gui.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { override def container = itemStackInUse - override def isUsableByPlayer(activePlayer : EntityPlayer): Boolean = activePlayer == player + override def stillValid(activePlayer : PlayerEntity): Boolean = activePlayer == player }) case Some(terminal: item.Terminal) if id == GuiType.Terminal.id => val stack = itemStackInUse - if (stack.hasTagCompound) { - val address = stack.getTagCompound.getString(Settings.namespace + "server") - val key = stack.getTagCompound.getString(Settings.namespace + "key") + if (stack.hasTag) { + val address = stack.getTag.getString(Settings.namespace + "server") + val key = stack.getTag.getString(Settings.namespace + "key") if (!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(address)) { component.TerminalServer.loaded.find(address) match { case Some(term) if term != null && term.rack != null => term.rack match { case rack: TileEntity with api.internal.Rack => - def inRange = player.isEntityAlive && !rack.isInvalid && rack.getDistanceSq(player.posX, player.posY, player.posZ) < term.range * term.range + def inRange = player.isAlive && !rack.isRemoved && player.distanceToSqr(rack.x + 0.5, rack.y + 0.5, rack.z + 0.5) < term.range * term.range if (inRange) { if (term.sidedKeys.contains(key)) return new gui.Screen(term.buffer, true, () => true, () => { // Check if someone else bound a term to our server. - if (stack.getTagCompound.getString(Settings.namespace + "key") != key) { - Minecraft.getMinecraft.displayGuiScreen(null) - } + if (stack.getTag.getString(Settings.namespace + "key") != key) Minecraft.getInstance.popGuiLayer // Check whether we're still in range. - if (!inRange) { - Minecraft.getMinecraft.displayGuiScreen(null) - } + if (!inRange) Minecraft.getInstance.popGuiLayer true }) - else player.sendMessage(Localization.Terminal.InvalidKey) + else player.displayClientMessage(Localization.Terminal.InvalidKey, true) } - else player.sendMessage(Localization.Terminal.OutOfRange) + else player.displayClientMessage(Localization.Terminal.OutOfRange, true) case _ => // Eh? } - case _ => player.sendMessage(Localization.Terminal.OutOfRange) + case _ => player.displayClientMessage(Localization.Terminal.OutOfRange, true) } } } diff --git a/src/main/scala/li/cil/oc/client/KeyBindings.scala b/src/main/scala/li/cil/oc/client/KeyBindings.scala index 688a1a9491..fafe6f1c87 100644 --- a/src/main/scala/li/cil/oc/client/KeyBindings.scala +++ b/src/main/scala/li/cil/oc/client/KeyBindings.scala @@ -1,44 +1,26 @@ package li.cil.oc.client import li.cil.oc.OpenComputers -import net.minecraft.client.settings.GameSettings +import net.minecraft.client.Minecraft import net.minecraft.client.settings.KeyBinding -import net.minecraftforge.fml.client.FMLClientHandler -import org.lwjgl.input.Keyboard -import org.lwjgl.input.Mouse +import net.minecraft.client.util.InputMappings +import net.minecraftforge.client.settings.KeyConflictContext +import org.lwjgl.glfw.GLFW import scala.collection.mutable object KeyBindings { - val keyBindingChecks = mutable.ArrayBuffer(isKeyBindingPressedVanilla _) + def showExtendedTooltips: Boolean = extendedTooltip.isDown - val keyBindingNameGetters = mutable.ArrayBuffer(getKeyBindingNameVanilla _) + def isAnalyzeCopyingAddress: Boolean = analyzeCopyAddr.isDown - def showExtendedTooltips: Boolean = isKeyBindingPressed(extendedTooltip) + def getKeyBindingName(keyBinding: KeyBinding) = keyBinding.getTranslatedKeyMessage.getString - def isPastingClipboard: Boolean = isKeyBindingPressed(clipboardPaste) + val extendedTooltip = Minecraft.getInstance.options.keyShift - def getKeyBindingName(keyBinding: KeyBinding): String = keyBindingNameGetters.map(_ (keyBinding)).collectFirst { - case Some(name) => name - }.getOrElse("???") + val analyzeCopyAddr = new KeyBinding("key.opencomputers.analyzeCopyAddress", KeyConflictContext.IN_GAME, + InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_CONTROL, OpenComputers.Name) - def isKeyBindingPressed(keyBinding: KeyBinding): Boolean = keyBindingChecks.forall(_ (keyBinding)) - - def getKeyBindingNameVanilla(keyBinding: KeyBinding): Option[String] = try Some(GameSettings.getKeyDisplayString(keyBinding.getKeyCode)) catch { - case _: Throwable => None - } - - def isKeyBindingPressedVanilla(keyBinding: KeyBinding): Boolean = try { - if (keyBinding.getKeyCode < 0) - Mouse.isCreated && Mouse.isButtonDown(keyBinding.getKeyCode + 100) - else - Keyboard.isCreated && Keyboard.isKeyDown(keyBinding.getKeyCode) - } - catch { - case _: Throwable => false - } - - def extendedTooltip: KeyBinding = FMLClientHandler.instance.getClient.gameSettings.keyBindSneak - - val clipboardPaste = new KeyBinding("key.clipboardPaste", Keyboard.KEY_INSERT, OpenComputers.Name) + val clipboardPaste = new KeyBinding("key.opencomputers.clipboardPaste", KeyConflictContext.GUI, + InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_INSERT, OpenComputers.Name) } diff --git a/src/main/scala/li/cil/oc/client/Manual.scala b/src/main/scala/li/cil/oc/client/Manual.scala index a0e81fb297..4c5c91fe7e 100644 --- a/src/main/scala/li/cil/oc/client/Manual.scala +++ b/src/main/scala/li/cil/oc/client/Manual.scala @@ -10,11 +10,10 @@ import li.cil.oc.api.manual.PathProvider import li.cil.oc.api.manual.TabIconRenderer import li.cil.oc.common.GuiType import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import net.minecraftforge.fml.common.FMLCommonHandler import scala.annotation.tailrec import scala.collection.convert.WrapAsJava._ @@ -87,7 +86,7 @@ object Manual extends ManualAPI { override def contentFor(path: String): java.lang.Iterable[String] = { val cleanPath = com.google.common.io.Files.simplifyPath(path) - val language = FMLCommonHandler.instance.getCurrentLanguage + val language = Minecraft.getInstance().getLanguageManager().getSelected().getCode() contentForWithRedirects(cleanPath.replaceAll(LanguageKey, language)). orElse(contentForWithRedirects(cleanPath.replaceAll(LanguageKey, FallbackLanguage))). orNull @@ -107,9 +106,9 @@ object Manual extends ManualAPI { null } - override def openFor(player: EntityPlayer): Unit = { - if (player.getEntityWorld.isRemote) { - player.openGui(OpenComputers, GuiType.Manual.id, player.getEntityWorld, 0, 0, 0) + override def openFor(player: PlayerEntity): Unit = { + if (player.level.isClientSide) { + OpenComputers.openGui(player, GuiType.Manual.id, player.level, 0, 0, 0) } } @@ -119,7 +118,7 @@ object Manual extends ManualAPI { } override def navigate(path: String): Unit = { - Minecraft.getMinecraft.currentScreen match { + Minecraft.getInstance.screen match { case manual: gui.Manual => manual.pushPage(path) case _ => history.push(new History(path)) } diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index 9cf0b967ab..c234b64c85 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -1,7 +1,10 @@ package li.cil.oc.client import java.io.EOFException +import java.io.InputStream +import com.mojang.blaze3d.systems.IRenderCall +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -23,32 +26,25 @@ import li.cil.oc.integration.jei.ModJEI import li.cil.oc.util.Audio import li.cil.oc.util.ExtendedWorld._ import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiScreen -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumParticleTypes +import net.minecraft.particles.IParticleData +import net.minecraft.util.Direction +import net.minecraft.util.Util import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvent -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.text.ChatType import net.minecraft.world.World import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.Optional -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent -import org.lwjgl.input.Keyboard +import net.minecraftforge.registries.ForgeRegistries object PacketHandler extends CommonPacketHandler { - @SubscribeEvent - def onPacket(e: ClientCustomPacketEvent): Unit = { - onPacketData(e.getManager.getNetHandler, e.getPacket.payload, Minecraft.getMinecraft.player) - } - - protected override def world(player: EntityPlayer, dimension: Int): Option[World] = { - val world = player.world - if (world.provider.getDimension == dimension) Some(world) + protected override def world(player: PlayerEntity, dimension: ResourceLocation): Option[World] = { + val world = player.level + if (world.dimension.location.equals(dimension)) Some(world) else None } @@ -115,23 +111,28 @@ object PacketHandler extends CommonPacketHandler { } def onAdapterState(p: PacketParser): Unit = - p.readTileEntity[Adapter]() match { + p.readBlockEntity[Adapter]() match { case Some(t) => t.openSides = t.uncompressSides(p.readByte()) - t.world.notifyBlockUpdate(t.getPos) + t.world.notifyBlockUpdate(t.getBlockPos) case _ => // Invalid packet. } def onAnalyze(p: PacketParser) { val address = p.readUTF() - if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) { - GuiScreen.setClipboardString(address) - p.player.sendMessage(Localization.Analyzer.AddressCopied) + if (KeyBindings.isAnalyzeCopyingAddress) { + RenderSystem.recordRenderCall(new IRenderCall { + override def execute = { + val mc = Minecraft.getInstance + mc.keyboardHandler.setClipboard(address) + mc.gui.handleChat(ChatType.SYSTEM, Localization.Analyzer.AddressCopied, Util.NIL_UUID) + } + }) } } def onChargerState(p: PacketParser): Unit = - p.readTileEntity[Charger]() match { + p.readBlockEntity[Charger]() match { case Some(t) => t.chargeSpeed = p.readDouble() t.hasPower = p.readBoolean() @@ -144,14 +145,17 @@ object PacketHandler extends CommonPacketHandler { } def onClipboard(p: PacketParser) { - GuiScreen.setClipboardString(p.readUTF()) + val contents = p.readUTF() + RenderSystem.recordRenderCall(new IRenderCall { + override def execute = Minecraft.getInstance.keyboardHandler.setClipboard(contents) + }) } def onColorChange(p: PacketParser): Unit = - p.readTileEntity[Colored]() match { + p.readBlockEntity[Colored]() match { case Some(t) => t.setColor(p.readInt()) - t.getWorld.notifyBlockUpdate(t.position) + t.getLevel.notifyBlockUpdate(t.position) case _ => // Invalid packet. } @@ -165,7 +169,7 @@ object PacketHandler extends CommonPacketHandler { } def onComputerState(p: PacketParser): Unit = - p.readTileEntity[Computer]() match { + p.readBlockEntity[Computer]() match { case Some(t) => t.setRunning(p.readBoolean()) t.hasErrored = p.readBoolean() @@ -173,7 +177,7 @@ object PacketHandler extends CommonPacketHandler { } def onComputerUserList(p: PacketParser): Unit = - p.readTileEntity[Computer]() match { + p.readBlockEntity[Computer]() match { case Some(t) => val count = p.readInt() t.setUsers((0 until count).map(_ => p.readUTF())) @@ -181,9 +185,9 @@ object PacketHandler extends CommonPacketHandler { } def onContainerUpdate(p: PacketParser): Unit = { - val windowId = p.readUnsignedByte() - if (p.player.openContainer != null && p.player.openContainer.windowId == windowId) { - p.player.openContainer match { + val containerId = p.readInt() + if (p.player.containerMenu != null && p.player.containerMenu.containerId == containerId) { + p.player.containerMenu match { case container: container.Player => container.updateCustomData(p.readNBT()) case _ => // Invalid packet. } @@ -191,7 +195,7 @@ object PacketHandler extends CommonPacketHandler { } def onDisassemblerActiveChange(p: PacketParser): Unit = - p.readTileEntity[Disassembler]() match { + p.readBlockEntity[Disassembler]() match { case Some(t) => t.isActive = p.readBoolean() case _ => // Invalid packet. } @@ -199,12 +203,12 @@ object PacketHandler extends CommonPacketHandler { def onFileSystemActivity(p: PacketParser): AnyVal = { val sound = p.readUTF() val data = CompressedStreamTools.read(p) - if (p.readBoolean()) p.readTileEntity[net.minecraft.tileentity.TileEntity]() match { + if (p.readBoolean()) p.readBlockEntity[net.minecraft.tileentity.TileEntity]() match { case Some(t) => MinecraftForge.EVENT_BUS.post(new FileSystemAccessEvent.Client(sound, t, data)) case _ => // Invalid packet. } - else world(p.player, p.readInt()) match { + else world(p.player, new ResourceLocation(p.readUTF())) match { case Some(world) => val x = p.readDouble() val y = p.readDouble() @@ -216,12 +220,12 @@ object PacketHandler extends CommonPacketHandler { def onNetworkActivity(p: PacketParser): AnyVal = { val data = CompressedStreamTools.read(p) - if (p.readBoolean()) p.readTileEntity[net.minecraft.tileentity.TileEntity]() match { + if (p.readBoolean()) p.readBlockEntity[net.minecraft.tileentity.TileEntity]() match { case Some(t) => MinecraftForge.EVENT_BUS.post(new NetworkActivityEvent.Client(t, data)) case _ => // Invalid packet. } - else world(p.player, p.readInt()) match { + else world(p.player, new ResourceLocation(p.readUTF())) match { case Some(world) => val x = p.readDouble() val y = p.readDouble() @@ -232,13 +236,13 @@ object PacketHandler extends CommonPacketHandler { } def onFloppyChange(p: PacketParser): Unit = - p.readTileEntity[DiskDrive]() match { - case Some(t) => t.setInventorySlotContents(0, p.readItemStack()) + p.readBlockEntity[DiskDrive]() match { + case Some(t) => t.setItem(0, p.readItemStack()) case _ => // Invalid packet. } def onHologramClear(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => for (i <- t.volume.indices) t.volume(i) = 0 t.needsRendering = true @@ -246,7 +250,7 @@ object PacketHandler extends CommonPacketHandler { } def onHologramColor(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => val index = p.readInt() val value = p.readInt() @@ -256,20 +260,20 @@ object PacketHandler extends CommonPacketHandler { } def onHologramPowerChange(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => t.hasPower = p.readBoolean() case _ => // Invalid packet. } def onHologramScale(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => t.scale = p.readDouble() case _ => // Invalid packet. } def onHologramArea(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => val fromX = p.readByte(): Int val untilX = p.readByte(): Int @@ -286,7 +290,7 @@ object PacketHandler extends CommonPacketHandler { } def onHologramValues(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => val count = p.readInt() for (i <- 0 until count) { @@ -301,17 +305,17 @@ object PacketHandler extends CommonPacketHandler { } def onHologramPositionOffsetY(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => val x = p.readDouble() val y = p.readDouble() val z = p.readDouble() - t.translation = new Vec3d(x, y, z) + t.translation = new Vector3d(x, y, z) case _ => // Invalid packet. } def onHologramRotation(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => t.rotationAngle = p.readFloat() t.rotationX = p.readFloat() @@ -321,7 +325,7 @@ object PacketHandler extends CommonPacketHandler { } def onHologramRotationSpeed(p: PacketParser): Unit = - p.readTileEntity[Hologram]() match { + p.readBlockEntity[Hologram]() match { case Some(t) => t.rotationSpeed = p.readFloat() t.rotationSpeedX = p.readFloat() @@ -336,15 +340,10 @@ object PacketHandler extends CommonPacketHandler { Loot.disksForClient += stack } if(Mods.JustEnoughItems.isModAvailable) { - addDiskToJEI(stack) + ModJEI.addDiskAtRuntime(stack) } } - @Optional.Method(modid = Mods.IDs.JustEnoughItems) - private def addDiskToJEI(stack: ItemStack): Unit = { - ModJEI.addDiskAtRuntime(stack) - } - def onCyclingDisk(p: PacketParser): Any = { val stack = p.readItemStack() if (!stack.isEmpty) { @@ -353,12 +352,12 @@ object PacketHandler extends CommonPacketHandler { } def onNanomachinesConfiguration(p: PacketParser): Unit = { - p.readEntity[EntityPlayer]() match { + p.readEntity[PlayerEntity]() match { case Some(player) => val hasController = p.readBoolean() if (hasController) { api.Nanomachines.installController(player) match { - case controller: ControllerImpl => controller.load(p.readNBT()) + case controller: ControllerImpl => controller.loadData(p.readNBT()) case _ => // Wat. } } @@ -370,7 +369,7 @@ object PacketHandler extends CommonPacketHandler { } def onNanomachinesInputs(p: PacketParser): Unit = { - p.readEntity[EntityPlayer]() match { + p.readEntity[PlayerEntity]() match { case Some(player) => api.Nanomachines.getController(player) match { case controller: ControllerImpl => val inputs = new Array[Byte](p.readInt()) @@ -388,7 +387,7 @@ object PacketHandler extends CommonPacketHandler { } def onNanomachinesPower(p: PacketParser): Unit = { - p.readEntity[EntityPlayer]() match { + p.readEntity[PlayerEntity]() match { case Some(player) => api.Nanomachines.getController(player) match { case controller: ControllerImpl => controller.storedEnergy = p.readDouble() case _ => // Wat. @@ -398,45 +397,47 @@ object PacketHandler extends CommonPacketHandler { } def onNetSplitterState(p: PacketParser): Unit = - p.readTileEntity[NetSplitter]() match { + p.readBlockEntity[NetSplitter]() match { case Some(t) => t.isInverted = p.readBoolean() t.openSides = t.uncompressSides(p.readByte()) - t.world.notifyBlockUpdate(t.getPos) + t.world.notifyBlockUpdate(t.getBlockPos) case _ => // Invalid packet. } def onParticleEffect(p: PacketParser): Unit = { - val dimension = p.readInt() - world(p.player, dimension) match { + world(p.player, new ResourceLocation(p.readUTF())) match { case Some(world) => val x = p.readInt() val y = p.readInt() val z = p.readInt() val velocity = p.readDouble() val direction = p.readDirection() - val particleType = EnumParticleTypes.getParticleFromId(p.readInt()) - val count = p.readUnsignedByte() - - for (i <- 0 until count) { - def rv(f: EnumFacing => Int) = direction match { - case Some(d) => world.rand.nextFloat - 0.5 + f(d) * 0.5 - case _ => world.rand.nextFloat * 2.0 - 1 - } - - val vx = rv(_.getFrontOffsetX) - val vy = rv(_.getFrontOffsetY) - val vz = rv(_.getFrontOffsetZ) - if (vx * vx + vy * vy + vz * vz < 1) { - def rp(x: Int, v: Double, f: EnumFacing => Int) = direction match { - case Some(d) => x + 0.5 + v * velocity * 0.5 + f(d) * velocity - case _ => x + 0.5 + v * velocity + val particleType = p.readRegistryEntry(ForgeRegistries.PARTICLE_TYPES) + if (particleType.isInstanceOf[IParticleData]) { + val particle = particleType.asInstanceOf[IParticleData] + val count = p.readUnsignedByte() + + for (i <- 0 until count) { + def rv(f: Direction => Int) = direction match { + case Some(d) => world.random.nextFloat - 0.5 + f(d) * 0.5 + case _ => world.random.nextFloat * 2.0 - 1 } - val px = rp(x, vx, _.getFrontOffsetX) - val py = rp(y, vy, _.getFrontOffsetY) - val pz = rp(z, vz, _.getFrontOffsetZ) - world.spawnParticle(particleType, px, py, pz, vx, vy + velocity * 0.25, vz) + val vx = rv(_.getStepX) + val vy = rv(_.getStepY) + val vz = rv(_.getStepZ) + if (vx * vx + vy * vy + vz * vz < 1) { + def rp(x: Int, v: Double, f: Direction => Int) = direction match { + case Some(d) => x + 0.5 + v * velocity * 0.5 + f(d) * velocity + case _ => x + 0.5 + v * velocity + } + + val px = rp(x, vx, _.getStepX) + val py = rp(y, vy, _.getStepY) + val pz = rp(z, vz, _.getStepZ) + world.addParticle(particle, px, py, pz, vx, vy + velocity * 0.25, vz) + } } } case _ => // Invalid packet. @@ -447,7 +448,7 @@ object PacketHandler extends CommonPacketHandler { if (!PetRenderer.isInitialized) { PetRenderer.isInitialized = true if (Settings.get.hideOwnPet) { - PetRenderer.hidden += Minecraft.getMinecraft.player.getName + PetRenderer.hidden += Minecraft.getInstance.player.getName.getString } PacketSender.sendPetVisibility() } @@ -465,7 +466,7 @@ object PacketHandler extends CommonPacketHandler { } def onPowerState(p: PacketParser): Unit = - p.readTileEntity[PowerInformation]() match { + p.readBlockEntity[PowerInformation]() match { case Some(t) => t.globalBuffer = p.readDouble() t.globalBufferSize = p.readDouble() @@ -473,7 +474,7 @@ object PacketHandler extends CommonPacketHandler { } def onPrinterState(p: PacketParser): Unit = - p.readTileEntity[Printer]() match { + p.readBlockEntity[Printer]() match { case Some(t) => if (p.readBoolean()) t.requiredEnergy = 9001 else t.requiredEnergy = 0 @@ -481,58 +482,58 @@ object PacketHandler extends CommonPacketHandler { } def onRackInventory(p: PacketParser): Unit = - p.readTileEntity[Rack]() match { + p.readBlockEntity[Rack]() match { case Some(t) => val count = p.readInt() for (_ <- 0 until count) { val slot = p.readInt() - t.setInventorySlotContents(slot, p.readItemStack()) + t.setItem(slot, p.readItemStack()) } case _ => // Invalid packet. } def onRackMountableData(p: PacketParser): Unit = - p.readTileEntity[Rack]() match { + p.readBlockEntity[Rack]() match { case Some(t) => val mountableIndex = p.readInt() t.lastData(mountableIndex) = p.readNBT() - t.getWorld.notifyBlockUpdate(t.getPos) + t.getLevel.notifyBlockUpdate(t.getBlockPos) case _ => // Invalid packet. } def onRaidStateChange(p: PacketParser): Unit = - p.readTileEntity[Raid]() match { + p.readBlockEntity[Raid]() match { case Some(t) => - for (slot <- 0 until t.getSizeInventory) { + for (slot <- 0 until t.getContainerSize) { t.presence(slot) = p.readBoolean() } case _ => // Invalid packet. } def onRedstoneState(p: PacketParser): Unit = - p.readTileEntity[RedstoneAware]() match { + p.readBlockEntity[RedstoneAware]() match { case Some(t) => t.setOutputEnabled(p.readBoolean()) - for (d <- EnumFacing.values) { + for (d <- Direction.values) { t.setOutput(d, p.readByte()) } case _ => // Invalid packet. } def onRobotAnimateSwing(p: PacketParser): Unit = - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => t.robot.setAnimateSwing(p.readInt()) case _ => // Invalid packet. } def onRobotAnimateTurn(p: PacketParser): Unit = - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => t.robot.setAnimateTurn(p.readByte(), p.readInt()) case _ => // Invalid packet. } def onRobotAssemblingState(p: PacketParser): Unit = - p.readTileEntity[Assembler]() match { + p.readBlockEntity[Assembler]() match { case Some(t) => if (p.readBoolean()) t.requiredEnergy = 9001 else t.requiredEnergy = 0 @@ -540,26 +541,26 @@ object PacketHandler extends CommonPacketHandler { } def onRobotInventoryChange(p: PacketParser): Unit = - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => val robot = t.robot val slot = p.readInt() val stack = p.readItemStack() - if (slot >= robot.getSizeInventory - robot.componentCount) { - robot.info.components(slot - (robot.getSizeInventory - robot.componentCount)) = stack + if (slot >= robot.getContainerSize - robot.componentCount) { + robot.info.components(slot - (robot.getContainerSize - robot.componentCount)) = stack } - else t.robot.setInventorySlotContents(slot, stack) + else t.robot.setItem(slot, stack) case _ => // Invalid packet. } def onRobotLightChange(p: PacketParser): Unit = - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => t.robot.info.lightColor = p.readInt() case _ => // Invalid packet. } def onRobotNameChange(p: PacketParser) = { - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => { val len = p.readShort() val name = new Array[Char](len) @@ -573,28 +574,28 @@ object PacketHandler extends CommonPacketHandler { } def onRobotMove(p: PacketParser): AnyVal = { - val dimension = p.readInt() + val dimension = new ResourceLocation(p.readUTF()) val x = p.readInt() val y = p.readInt() val z = p.readInt() val direction = p.readDirection() - (p.getTileEntity[RobotProxy](dimension, x, y, z), direction) match { + (p.getBlockEntity[RobotProxy](dimension, x, y, z), direction) match { case (Some(t), Some(d)) => t.robot.move(d) case (_, Some(d)) => // Invalid packet, robot may be coming from outside our loaded area. - PacketSender.sendRobotStateRequest(dimension, x + d.getFrontOffsetX, y + d.getFrontOffsetY, z + d.getFrontOffsetZ) + PacketSender.sendRobotStateRequest(dimension, x + d.getStepX, y + d.getStepY, z + d.getStepZ) case _ => // Invalid packet. } } def onRobotSelectedSlotChange(p: PacketParser): Unit = - p.readTileEntity[RobotProxy]() match { + p.readBlockEntity[RobotProxy]() match { case Some(t) => t.robot.selectedSlot = p.readInt() case _ => // Invalid packet. } def onRotatableState(p: PacketParser): Unit = - p.readTileEntity[Rotatable]() match { + p.readBlockEntity[Rotatable]() match { case Some(t) => t.pitch = p.readDirection().get t.yaw = p.readDirection().get @@ -602,41 +603,41 @@ object PacketHandler extends CommonPacketHandler { } def onSwitchActivity(p: PacketParser): Unit = - p.readTileEntity[Relay]() match { + p.readBlockEntity[Relay]() match { case Some(t) => t.lastMessage = System.currentTimeMillis() case _ => // Invalid packet. } def onTextBufferPowerChange(p: PacketParser): Unit = - ComponentTracker.get(p.player.getEntityWorld, p.readUTF()) match { + ComponentTracker.get(p.player.level, p.readUTF()) match { case Some(buffer: api.internal.TextBuffer) => buffer.setRenderingEnabled(p.readBoolean()) case _ => // Invalid packet. } def onTextBufferInit(p: PacketParser) { - ComponentTracker.get(p.player.getEntityWorld, p.readUTF()) match { + ComponentTracker.get(p.player.level, p.readUTF()) match { case Some(buffer: li.cil.oc.common.component.TextBuffer) => val nbt = p.readNBT() - if (nbt.hasKey("maxWidth")) { - val maxWidth = nbt.getInteger("maxWidth") - val maxHeight = nbt.getInteger("maxHeight") + if (nbt.contains("maxWidth")) { + val maxWidth = nbt.getInt("maxWidth") + val maxHeight = nbt.getInt("maxHeight") buffer.setMaximumResolution(maxWidth, maxHeight) } - buffer.data.load(nbt) - if (nbt.hasKey("viewportWidth")) { - val viewportWidth = nbt.getInteger("viewportWidth") - val viewportHeight = nbt.getInteger("viewportHeight") + buffer.data.loadData(nbt) + if (nbt.contains("viewportWidth")) { + val viewportWidth = nbt.getInt("viewportWidth") + val viewportHeight = nbt.getInt("viewportHeight") buffer.setViewport(viewportWidth, viewportHeight) } - buffer.proxy.markDirty() + buffer.proxy.setChanged() buffer.markInitialized() case _ => // Invalid packet. } } def onTextBufferMulti(p: PacketParser): Unit = - if (p.player != null) ComponentTracker.get(p.player.getEntityWorld, p.readUTF()) match { + if (p.player != null) ComponentTracker.get(p.player.level, p.readUTF()) match { case Some(buffer: api.internal.TextBuffer) => try while (true) { p.readPacketType() match { @@ -815,14 +816,13 @@ object PacketHandler extends CommonPacketHandler { } def onScreenTouchMode(p: PacketParser): Unit = - p.readTileEntity[Screen]() match { + p.readBlockEntity[Screen]() match { case Some(t) => t.invertTouchMode = p.readBoolean() case _ => // Invalid packet. } def onSoundEffect(p: PacketParser) { - val dimension = p.readInt() - world(p.player, dimension) match { + world(p.player, new ResourceLocation(p.readUTF())) match { case Some(world) => val x = p.readDouble() val y = p.readDouble() @@ -836,8 +836,7 @@ object PacketHandler extends CommonPacketHandler { } def onSound(p: PacketParser) { - val dimension = p.readInt() - if (world(p.player, dimension).isDefined) { + if (world(p.player, new ResourceLocation(p.readUTF())).isDefined) { val x = p.readInt() val y = p.readInt() val z = p.readInt() @@ -848,8 +847,7 @@ object PacketHandler extends CommonPacketHandler { } def onSoundPattern(p: PacketParser) { - val dimension = p.readInt() - if (world(p.player, dimension).isDefined) { + if (world(p.player, new ResourceLocation(p.readUTF())).isDefined) { val x = p.readInt() val y = p.readInt() val z = p.readInt() @@ -859,14 +857,16 @@ object PacketHandler extends CommonPacketHandler { } def onTransposerActivity(p: PacketParser): Unit = - p.readTileEntity[Transposer]() match { + p.readBlockEntity[Transposer]() match { case Some(transposer) => transposer.lastOperation = System.currentTimeMillis() case _ => // Invalid packet. } def onWaypointLabel(p: PacketParser): Unit = - p.readTileEntity[Waypoint]() match { + p.readBlockEntity[Waypoint]() match { case Some(waypoint) => waypoint.label = p.readUTF() case _ => // Invalid packet. } + + protected override def createParser(stream: InputStream, player: PlayerEntity) = new PacketParser(stream, player) } diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index 90f2351d6b..ea7956f38e 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -8,15 +8,14 @@ import li.cil.oc.common.entity.Drone import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity.traits.Computer import net.minecraft.client.Minecraft -import net.minecraft.client.audio.PositionedSoundRecord -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.SoundEvents +import net.minecraft.client.audio.SimpleSound +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.SoundEvents import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory -import scala.tools.nsc.doc.model.Entity - object PacketSender { // Timestamp after which the next clipboard message may be sent. Used to // avoid spamming large packets on key repeat. @@ -77,9 +76,8 @@ object PacketSender { def sendClipboard(address: String, value: String) { if (value != null && !value.isEmpty) { if (value.length > 64 * 1024 || System.currentTimeMillis() < clipboardCooldown) { - val player = Minecraft.getMinecraft.player - val handler = Minecraft.getMinecraft.getSoundHandler - handler.playSound(new PositionedSoundRecord(SoundEvents.BLOCK_NOTE_HARP, SoundCategory.MASTER, 1, 1, player.posX.toFloat, player.posY.toFloat, player.posZ.toFloat)) + val handler = Minecraft.getInstance.getSoundManager + handler.play(SimpleSound.forUI(SoundEvents.NOTE_BLOCK_HARP, 1, 1)) } else { clipboardCooldown = System.currentTimeMillis() + value.length / 10 @@ -159,7 +157,7 @@ object PacketSender { pb.sendToServer() } - def sendRackMountableMapping(t: Rack, mountableIndex: Int, nodeIndex: Int, side: Option[EnumFacing]) { + def sendRackMountableMapping(t: Rack, mountableIndex: Int, nodeIndex: Int, side: Option[Direction]) { val pb = new SimplePacketBuilder(PacketType.RackMountableMapping) pb.writeTileEntity(t) @@ -187,10 +185,10 @@ object PacketSender { pb.sendToServer() } - def sendRobotStateRequest(dimension: Int, x: Int, y: Int, z: Int) { + def sendRobotStateRequest(dimension: ResourceLocation, x: Int, y: Int, z: Int) { val pb = new SimplePacketBuilder(PacketType.RobotStateRequest) - pb.writeInt(dimension) + pb.writeUTF(dimension.toString) pb.writeInt(x) pb.writeInt(y) pb.writeInt(z) diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index c8a912dbeb..e929f5be9b 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -12,69 +12,71 @@ import li.cil.oc.client.renderer.block.ModelInitialization import li.cil.oc.client.renderer.block.NetSplitterModel import li.cil.oc.client.renderer.entity.DroneRenderer import li.cil.oc.client.renderer.tileentity._ +import li.cil.oc.common +import li.cil.oc.common.{PacketHandler => CommonPacketHandler} +import li.cil.oc.common.{Proxy => CommonProxy} import li.cil.oc.common.component.TextBuffer import li.cil.oc.common.entity.Drone import li.cil.oc.common.event.NanomachinesHandler import li.cil.oc.common.event.RackMountableRenderHandler import li.cil.oc.common.item.traits.Delegate import li.cil.oc.common.tileentity -import li.cil.oc.common.{Proxy => CommonProxy} import li.cil.oc.util.Audio import net.minecraft.block.Block +import net.minecraft.client.Minecraft +import net.minecraft.client.entity.player.ClientPlayerEntity +import net.minecraft.client.gui.screen.Screen +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item +import net.minecraft.world.World import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.client.registry.ClientRegistry import net.minecraftforge.fml.client.registry.RenderingRegistry -import net.minecraftforge.fml.common.event.FMLInitializationEvent -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent -import net.minecraftforge.fml.common.network.NetworkRegistry -import org.lwjgl.opengl.GLContext +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent +import net.minecraftforge.fml.network.NetworkRegistry +@Deprecated private[oc] class Proxy extends CommonProxy { - override def preInit(e: FMLPreInitializationEvent) { + override def preInit(e: FMLCommonSetupEvent) { super.preInit(e) api.API.manual = client.Manual - CommandHandler.register() - MinecraftForge.EVENT_BUS.register(Textures) MinecraftForge.EVENT_BUS.register(NetSplitterModel) ModelInitialization.preInit() } - override def init(e: FMLInitializationEvent) { + override def init(e: FMLCommonSetupEvent) { super.init(e) - OpenComputers.channel.register(client.PacketHandler) + CommonPacketHandler.clientHandler = PacketHandler ColorHandler.init() - RenderingRegistry.registerEntityRenderingHandler(classOf[Drone], DroneRenderer) - - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Adapter], AdapterRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Assembler], AssemblerRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Case], CaseRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Charger], ChargerRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Disassembler], DisassemblerRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.DiskDrive], DiskDriveRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Geolyzer], GeolyzerRenderer) - if (GLContext.getCapabilities.OpenGL15) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRenderer) - else - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRendererFallback) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Printer], PrinterRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Rack], RackRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Relay], RelayRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Transposer], TransposerRenderer) - + RenderingRegistry.registerEntityRenderingHandler(null, DroneRenderer) // TEMP + + ClientRegistry.bindTileEntityRenderer(null, AdapterRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, AssemblerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, CaseRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, ChargerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, DisassemblerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, DiskDriveRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, GeolyzerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, HologramRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, MicrocontrollerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, NetSplitterRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, PowerDistributorRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, PrinterRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, RaidRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, RackRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, RelayRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, RobotRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, ScreenRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(null, TransposerRenderer) // TEMP + + ClientRegistry.registerKeyBinding(KeyBindings.analyzeCopyAddr) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) MinecraftForge.EVENT_BUS.register(HighlightRenderer) @@ -86,8 +88,6 @@ private[oc] class Proxy extends CommonProxy { MinecraftForge.EVENT_BUS.register(MFUTargetRenderer) MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) - NetworkRegistry.INSTANCE.registerGuiHandler(OpenComputers, GuiHandler) - MinecraftForge.EVENT_BUS.register(Audio) MinecraftForge.EVENT_BUS.register(HologramRenderer) MinecraftForge.EVENT_BUS.register(PetRenderer) @@ -95,6 +95,19 @@ private[oc] class Proxy extends CommonProxy { MinecraftForge.EVENT_BUS.register(TextBufferRenderCache) } + override def getGuiHandler(): common.GuiHandler = client.GuiHandler + + @Deprecated + override def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = { + player match { + case _: ClientPlayerEntity => { + val screen = getGuiHandler.getClientGuiElement(guiId, 0, player, world, x, y, z).asInstanceOf[Screen] + if (screen != null) Minecraft.getInstance.pushGuiLayer(screen) + } + case _ => super.openGui(player, guiId, world, x, y, z) + } + } + override def registerModel(instance: Delegate, id: String): Unit = ModelInitialization.registerModel(instance, id) override def registerModel(instance: Item, id: String): Unit = ModelInitialization.registerModel(instance, id) diff --git a/src/main/scala/li/cil/oc/client/Sound.scala b/src/main/scala/li/cil/oc/client/Sound.scala index e578d1cddb..4c1ff23482 100644 --- a/src/main/scala/li/cil/oc/client/Sound.scala +++ b/src/main/scala/li/cil/oc/client/Sound.scala @@ -12,71 +12,34 @@ import com.google.common.base.Charsets import li.cil.oc.OpenComputers import li.cil.oc.Settings import net.minecraft.client.Minecraft -import net.minecraft.client.audio.SoundManager -import net.minecraft.server.integrated.IntegratedServer +import net.minecraft.client.audio.ITickableSound +import net.minecraft.client.audio.LocatableSound +import net.minecraft.client.audio.SoundEngine import net.minecraft.tileentity.TileEntity import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory -import net.minecraftforge.client.event.sound.SoundLoadEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.event.TickEvent.ClientTickEvent import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.client.FMLClientHandler -import net.minecraftforge.fml.common.FMLCommonHandler -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent -import paulscode.sound.SoundSystemConfig import scala.collection.mutable -import scala.io.Source object Sound { private val sources = mutable.Map.empty[TileEntity, PseudoLoopingStream] private val commandQueue = mutable.PriorityQueue.empty[Command] - private var lastVolume = FMLClientHandler.instance.getClient.gameSettings.getSoundLevel(SoundCategory.BLOCKS) - private val updateTimer = new Timer("OpenComputers-SoundUpdater", true) if (Settings.get.soundVolume > 0) { updateTimer.scheduleAtFixedRate(new TimerTask { override def run() { - sources.synchronized(updateCallable = Some(() => { - updateVolume() - processQueue() - })) + sources.synchronized(updateCallable = Some(() => processQueue())) } }, 500, 50) } private var updateCallable = None: Option[() => Unit] - // Set in init event. - var manager: SoundManager = _ - - def soundSystem = if (manager != null) manager.sndSystem else null - - private def updateVolume() { - val volume = - if (isGamePaused) 0f - else FMLClientHandler.instance.getClient.gameSettings.getSoundLevel(SoundCategory.BLOCKS) - if (volume != lastVolume) { - lastVolume = volume - sources.synchronized { - for (sound <- sources.values) { - sound.updateVolume() - } - } - } - } - - private def isGamePaused = { - val server = FMLCommonHandler.instance.getMinecraftServerInstance - // Check outside of match to avoid client side class access. - server != null && !server.isDedicatedServer && (server match { - case integrated: IntegratedServer => Minecraft.getMinecraft.isGamePaused - case _ => false - }) - } - private def processQueue() { if (commandQueue.nonEmpty) { commandQueue.synchronized { @@ -113,42 +76,11 @@ object Sound { } } - @SubscribeEvent - def onSoundLoad(event: SoundLoadEvent) { - manager = event.getManager - } - - private var hasPreloaded = Settings.get.soundVolume <= 0 - @SubscribeEvent def onTick(e: ClientTickEvent) { - if (soundSystem != null) { - if (!hasPreloaded) { - hasPreloaded = true - new Thread(new Runnable() { - override def run(): Unit = { - val preloadConfigLocation = new ResourceLocation(Settings.resourceDomain, "sounds/preload.cfg") - val preloadConfigResource = Minecraft.getMinecraft.getResourceManager.getResource(preloadConfigLocation) - for (location <- Source.fromInputStream(preloadConfigResource.getInputStream)(Charsets.UTF_8).getLines()) { - val url = getClass.getClassLoader.getResource(location) - if (url != null) try { - val sourceName = "preload_" + location - soundSystem.newSource(false, sourceName, url, location, true, 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 16) - soundSystem.activate(sourceName) - soundSystem.removeSource(sourceName) - } catch { - case _: Throwable => // Meh. - } - else OpenComputers.log.warn(s"Couldn't preload sound $location!") - } - } - }) - } - - sources.synchronized { - updateCallable.foreach(_ ()) - updateCallable = None - } + sources.synchronized { + updateCallable.foreach(_ ()) + updateCallable = None } } @@ -170,7 +102,11 @@ object Sound { private class StartCommand(when: Long, tileEntity: TileEntity, val name: String, val volume: Float) extends Command(when, tileEntity) { override def apply() { sources.synchronized { - sources.getOrElseUpdate(tileEntity, new PseudoLoopingStream(tileEntity, volume)).play(name) + val current = sources.getOrElse(tileEntity, null) + if (current == null || !current.getLocation.getPath.equals(name)) { + if (current != null) current.stop() + sources(tileEntity) = new PseudoLoopingStream(tileEntity, volume, name) + } } } } @@ -203,65 +139,34 @@ object Sound { } } - private class PseudoLoopingStream(val tileEntity: TileEntity, val volume: Float, val source: String = UUID.randomUUID.toString) { - var initialized = false + private class PseudoLoopingStream(val tileEntity: TileEntity, val subVolume: Float, name: String) + extends LocatableSound(new ResourceLocation(OpenComputers.ID, name), SoundCategory.BLOCKS) with ITickableSound { - def updateVolume() { - soundSystem.setVolume(source, lastVolume * volume * Settings.get.soundVolume) - } + var stopped = false + volume = subVolume * Settings.get.soundVolume + relative = tileEntity != null + looping = true + updatePosition() def updatePosition() { - if (tileEntity != null) soundSystem.setPosition(source, tileEntity.getPos.getX, tileEntity.getPos.getY, tileEntity.getPos.getZ) - else soundSystem.setPosition(source, 0, 0, 0) - } - - def play(name: String) { - val resourceName = s"${Settings.resourceDomain}:$name" - val sound = manager.sndHandler.getAccessor(new ResourceLocation(resourceName)) - // Specified return type because apparently this is ambiguous according to Jenkins. I don't even. - val resource = (sound.cloneEntry(): net.minecraft.client.audio.Sound).getSoundAsOggLocation - if (!initialized) { - initialized = true - if (tileEntity != null) soundSystem.newSource(false, source, toUrl(resource), resource.toString, true, tileEntity.getPos.getX, tileEntity.getPos.getY, tileEntity.getPos.getZ, SoundSystemConfig.ATTENUATION_LINEAR, 16) - else soundSystem.newSource(false, source, toUrl(resource), resource.toString, false, 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0) - updateVolume() - soundSystem.activate(source) + if (tileEntity != null) { + val pos = tileEntity.getBlockPos + x = pos.getX + 0.5 + y = pos.getY + 0.5 + z = pos.getZ + 0.5 } - soundSystem.play(source) } - def stop() { - if (soundSystem != null) try { - soundSystem.stop(source) - soundSystem.removeSource(source) - } - catch { - case _: Throwable => - } - } - } + override def canStartSilent() = true - // This is copied from SoundManager.getURLForSoundResource, which is private. - private def toUrl(resource: ResourceLocation): URL = { - val name = s"mcsounddomain:${resource.getResourceDomain}:${resource.getResourcePath}" - try { - new URL(null, name, new URLStreamHandler { - protected def openConnection(url: URL): URLConnection = new URLConnection(url) { - def connect() { - } + override def isStopped() = stopped - override def getInputStream = try { - Minecraft.getMinecraft.getResourceManager.getResource(resource).getInputStream - } catch { - case t: Throwable => - OpenComputers.log.warn(t) - null - } - } - }) - } - catch { - case _: MalformedURLException => null + // Required by ITickableSound, which is required to update position while playing + override def tick() = Unit + + def stop() { + stopped = true + looping = false } } } diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index f457d7322a..c84c971214 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -1,15 +1,17 @@ package li.cil.oc.client +import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft +import net.minecraft.inventory.container.PlayerContainer +import net.minecraft.client.renderer.texture.SimpleTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite -import net.minecraft.client.renderer.texture.TextureMap import net.minecraft.util.ResourceLocation import net.minecraftforge.client.event.TextureStitchEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.mutable @@ -19,9 +21,10 @@ object Textures { val Aliased = L("chars_aliased") val AntiAliased = L("chars") - override protected def basePath = "textures/font/%s.png" + override protected def basePath = "font/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = Textures.bind(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) } object GUI extends TextureBundle { @@ -65,9 +68,10 @@ object Textures { val UpgradeTab = L("upgrade_tab") val Waypoint = L("waypoint") - override protected def basePath = "textures/gui/%s.png" + override protected def basePath = "gui/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = Textures.bind(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) } object Icons extends TextureBundle { @@ -78,9 +82,10 @@ object Textures { def get(tier: Int) = ForTier.get(tier).orNull - override protected def basePath = "textures/icons/%s.png" + override protected def basePath = "icons/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = Textures.bind(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) } object Model extends TextureBundle { @@ -91,9 +96,10 @@ object Textures { val Drone = L("drone") val Robot = L("robot") - override protected def basePath = "textures/model/%s.png" + override protected def basePath = "model/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = Textures.bind(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) } object Item extends TextureBundle { @@ -102,7 +108,7 @@ object Textures { override protected def basePath = "items/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = map.registerSprite(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = e.addSprite(loc) } // These are kept in the block texture atlas to support animations. @@ -526,61 +532,52 @@ object Textures { Screen.makeSureThisIsInitialized() - def bind(): Unit = Textures.bind(TextureMap.LOCATION_BLOCKS_TEXTURE) + def bind(): Unit = Textures.bind(PlayerContainer.BLOCK_ATLAS) override protected def basePath = "blocks/%s" - override protected def loader(map: TextureMap, loc: ResourceLocation) = map.registerSprite(loc) + override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = e.addSprite(loc) } def bind(location: ResourceLocation): Unit = { - if (location == null) RenderState.bindTexture(0) - else { - val manager = Minecraft.getMinecraft.renderEngine - manager.bindTexture(location) - // IMPORTANT: manager.bindTexture uses GlStateManager.bindTexture, and - // that has borked caching, so binding textures will sometimes fail, - // because it'll think the texture is already bound although it isn't. - // So we do it manually. - val texture = manager.getTexture(location) - if (texture != null) { - RenderState.bindTexture(texture.getGlTextureId) - } - } + val texture = if (location != null) Minecraft.getInstance.textureManager.getTexture(location) else null + if (texture != null) texture.bind() + else RenderState.bindTexture(0) } - def getSprite(location: String): TextureAtlasSprite = Minecraft.getMinecraft.getTextureMapBlocks.getAtlasSprite(location) + def getSprite(location: String): TextureAtlasSprite = getSprite(new ResourceLocation(location)) - def getSprite(location: ResourceLocation): TextureAtlasSprite = getSprite(location.toString) + def getSprite(location: ResourceLocation): TextureAtlasSprite = + Minecraft.getInstance.getModelManager.getAtlas(PlayerContainer.BLOCK_ATLAS).getSprite(location) @SubscribeEvent def onTextureStitchPre(e: TextureStitchEvent.Pre): Unit = { - Font.init(e.getMap) - GUI.init(e.getMap) - Icons.init(e.getMap) - Model.init(e.getMap) - Item.init(e.getMap) - Block.init(e.getMap) + if (e.getMap.location.equals(PlayerContainer.BLOCK_ATLAS)) { + Font.init(e) + GUI.init(e) + Icons.init(e) + Model.init(e) + Item.init(e) + Block.init(e) + } } abstract class TextureBundle { private val locations = mutable.ArrayBuffer.empty[ResourceLocation] - protected def textureManager = Minecraft.getMinecraft.getTextureManager - - final def init(map: TextureMap): Unit = { - locations.foreach(loader(map, _)) + final def init(e: TextureStitchEvent.Pre): Unit = { + locations.foreach(loader(e, _)) } protected def L(name: String, load: Boolean = true) = { - val location = new ResourceLocation(Settings.resourceDomain, String.format(basePath, name)) + val location = new ResourceLocation(OpenComputers.ID, String.format(basePath, name)) if (load) locations += location location } protected def basePath: String - protected def loader(map: TextureMap, loc: ResourceLocation): Unit + protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation): Unit } } diff --git a/src/main/scala/li/cil/oc/client/gui/Adapter.scala b/src/main/scala/li/cil/oc/client/gui/Adapter.scala index 1b73504983..715cab0b94 100644 --- a/src/main/scala/li/cil/oc/client/gui/Adapter.scala +++ b/src/main/scala/li/cil/oc/client/gui/Adapter.scala @@ -3,13 +3,9 @@ package li.cil.oc.client.gui import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Adapter(playerInventory: InventoryPlayer, val adapter: tileentity.Adapter) extends DynamicGuiContainer(new container.Adapter(playerInventory, adapter)) { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(adapter.getName), - 8, 6, 0x404040) - } +class Adapter(id: Int, playerInventory: PlayerInventory, val adapter: tileentity.Adapter) + extends DynamicGuiContainer(new container.Adapter(id, playerInventory, adapter), + playerInventory, adapter.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 60146d69c2..2d99098534 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar @@ -9,27 +11,30 @@ import li.cil.oc.common.container.ComponentSlot import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.Slot +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.Slot import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ -class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Assembler) extends DynamicGuiContainer(new container.Assembler(playerInventory, assembler)) { - xSize = 176 - ySize = 192 +class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) + extends DynamicGuiContainer(new container.Assembler(id, playerInventory, assembler), + playerInventory, StringTextComponent.EMPTY) { - for (slot <- inventorySlots.inventorySlots) slot match { + imageWidth = 176 + imageHeight = 192 + + for (slot <- menu.slots) slot match { case component: ComponentSlot => component.changeListener = Option(onSlotChanged) case _ => } private def onSlotChanged(slot: Slot) { - runButton.enabled = canBuild - runButton.toggled = !runButton.enabled + runButton.active = canBuild + runButton.toggled = !runButton.active info = validate } @@ -37,53 +42,49 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse protected var runButton: ImageButton = _ - private val progress = addWidget(new ProgressBar(28, 92)) + private val progress = addCustomWidget(new ProgressBar(28, 92)) - private def validate = AssemblerTemplates.select(inventoryContainer.getSlot(0).getStack).map(_.validate(inventoryContainer.otherInventory)) + private def validate = AssemblerTemplates.select(inventoryContainer.getSlot(0).getItem).map(_.validate(inventoryContainer.otherInventory)) private def canBuild = !inventoryContainer.isAssembling && validate.exists(_._1) - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0 && canBuild) { - ClientPacketSender.sendRobotAssemblerStart(assembler) - } - } - - override def initGui() { - super.initGui() - runButton = new ImageButton(0, guiLeft + 7, guiTop + 89, 18, 18, Textures.GUI.ButtonRun, canToggle = true) - add(buttonList, runButton) + override protected def init() { + super.init() + runButton = new ImageButton(leftPos + 7, topPos + 89, 18, 18, new Button.IPressable { + override def onPress(b: Button) = if (canBuild) ClientPacketSender.sendRobotAssemblerStart(assembler) + }, Textures.GUI.ButtonRun, canToggle = true) + addButton(runButton) } - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int): Unit = { + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int): Unit = { RenderState.pushAttrib() if (!inventoryContainer.isAssembling) { val message = - if (!inventoryContainer.getSlot(0).getHasStack) { + if (!inventoryContainer.getSlot(0).hasItem) { Localization.Assembler.InsertTemplate } else info match { - case Some((_, value, _)) if value != null => value.getUnformattedText - case _ if inventoryContainer.getSlot(0).getHasStack => Localization.Assembler.CollectResult + case Some((_, value, _)) if value != null => value.getString + case _ if inventoryContainer.getSlot(0).hasItem => Localization.Assembler.CollectResult case _ => "" } - fontRenderer.drawString(message, 30, 94, 0x404040) - if (runButton.isMouseOver) { + font.draw(stack, message, 30, 94, 0x404040) + if (runButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.add(Localization.Assembler.Run) info.foreach { case (valid, _, warnings) => if (valid && warnings.length > 0) { - tooltip.addAll(warnings.map(_.getUnformattedText).toList) + tooltip.addAll(warnings.map(_.getString).toList) } } - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } } else if (isPointInRegion(progress.x, progress.y, progress.width, progress.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] val timeRemaining = formatTime(inventoryContainer.assemblyRemainingTime) tooltip.add(Localization.Assembler.Progress(inventoryContainer.assemblyProgress, timeRemaining)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() } @@ -94,15 +95,15 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse else f"${seconds / 60}:${seconds % 60}%02d" } - override def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) // Required under Linux. + override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) // Required under Linux. Textures.bind(Textures.GUI.RobotAssembler) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) if (inventoryContainer.isAssembling) progress.level = inventoryContainer.assemblyProgress / 100.0 else progress.level = 0 - drawWidgets() - drawInventorySlots() + drawWidgets(stack) + drawInventorySlots(stack) } - override protected def drawDisabledSlot(slot: ComponentSlot) {} + override protected def drawDisabledSlot(stack: MatrixStack, slot: ComponentSlot) {} } diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index 7900bce06b..ffb3ea85e4 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -1,51 +1,49 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.entity.player.PlayerInventory import scala.collection.convert.WrapAsJava._ -class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) extends DynamicGuiContainer(new container.Case(playerInventory, computer)) { - protected var powerButton: ImageButton = _ +class Case(id: Int, playerInventory: PlayerInventory, val computer: tileentity.Case) + extends DynamicGuiContainer(new container.Case(id, playerInventory, computer), + playerInventory, computer.getName) { - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - ClientPacketSender.sendComputerPower(computer, !computer.isRunning) - } - } + protected var powerButton: ImageButton = _ - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { powerButton.toggled = computer.isRunning - super.drawScreen(mouseX, mouseY, dt) + super.render(stack, mouseX, mouseY, dt) } - override def initGui() { - super.initGui() - powerButton = new ImageButton(0, guiLeft + 70, guiTop + 33, 18, 18, Textures.GUI.ButtonPower, canToggle = true) - add(buttonList, powerButton) + override protected def init() { + super.init() + powerButton = new ImageButton(leftPos + 70, topPos + 33, 18, 18, new Button.IPressable { + override def onPress(b: Button) = ClientPacketSender.sendComputerPower(computer, !computer.isRunning) + }, Textures.GUI.ButtonPower, canToggle = true) + addButton(powerButton) } - override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(computer.getName), - 8, 6, 0x404040) - if (powerButton.isMouseOver) { + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) + font.draw(stack, computer.getName, 8, 6, 0x404040) + if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.addAll(asJavaCollection(if (computer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } } - override def drawSecondaryBackgroundLayer() { - GlStateManager.color(1, 1, 1) + override def drawSecondaryBackgroundLayer(stack: MatrixStack) { + RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Computer) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Charger.scala b/src/main/scala/li/cil/oc/client/gui/Charger.scala index 1cf35a188f..96cc61e011 100644 --- a/src/main/scala/li/cil/oc/client/gui/Charger.scala +++ b/src/main/scala/li/cil/oc/client/gui/Charger.scala @@ -3,13 +3,9 @@ package li.cil.oc.client.gui import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Charger(playerInventory: InventoryPlayer, val charger: tileentity.Charger) extends DynamicGuiContainer(new container.Charger(playerInventory, charger)) { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(charger.getName), - 8, 6, 0x404040) - } +class Charger(id: Int, playerInventory: PlayerInventory, val charger: tileentity.Charger) + extends DynamicGuiContainer(new container.Charger(id, playerInventory, charger), + playerInventory, charger.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala index 9eaf3998f2..feacf698a1 100644 --- a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala @@ -2,13 +2,21 @@ package li.cil.oc.client.gui import java.util +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.gui.widget.WidgetContainer import li.cil.oc.util.RenderState import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.gui.inventory.GuiContainer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.screen.inventory.ContainerScreen +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.RenderHelper -import net.minecraft.inventory.Container +import net.minecraft.client.renderer.Tessellator +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.Container +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.ITextProperties +import net.minecraft.util.text.LanguageMap +import net.minecraft.util.text.StringTextComponent import scala.collection.convert.WrapAsScala._ @@ -16,30 +24,43 @@ import scala.collection.convert.WrapAsScala._ // transformations that break things! Such fun. Many annoyed. And yes, this // is a common issue, have a look at EnderIO and Enchanting Plus. They have // to work around this, too. -abstract class CustomGuiContainer[C <: Container](val inventoryContainer: C) extends GuiContainer(inventoryContainer) with WidgetContainer { - override def windowX = guiLeft +abstract class CustomGuiContainer[C <: Container](val inventoryContainer: C, inv: PlayerInventory, title: ITextComponent) + extends ContainerScreen(inventoryContainer, inv, title) with WidgetContainer { - override def windowY = guiTop + override def windowX = leftPos - override def windowZ = zLevel + override def windowY = topPos - override def doesGuiPauseGame = false + override def windowZ = getBlitOffset + + override def isPauseScreen = false protected def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) // Pretty much Scalaified copy-pasta from base-class. - override def drawHoveringText(text: util.List[String], x: Int, y: Int, font: FontRenderer): Unit = { - copiedDrawHoveringText(text, x, y, font) + override def renderWrappedToolTip(stack: MatrixStack, text: util.List[_ <: ITextProperties], x: Int, y: Int, font: FontRenderer): Unit = { + copiedDrawHoveringText0(stack, text, x, y, font) } - protected def copiedDrawHoveringText(text: util.List[String], x: Int, y: Int, font: FontRenderer): Unit = { + protected def isPointInRegion(rectX: Int, rectY: Int, rectWidth: Int, rectHeight: Int, pointX: Int, pointY: Int): Boolean = + pointX >= rectX - 1 && pointX < rectX + rectWidth + 1 && pointY >= rectY - 1 && pointY < rectY + rectHeight + 1 + + protected def copiedDrawHoveringText(stack: MatrixStack, lines: util.List[String], x: Int, y: Int, font: FontRenderer): Unit = { + val text = new util.ArrayList[StringTextComponent]() + for (line <- lines) { + text.add(new StringTextComponent(line)) + } + copiedDrawHoveringText0(stack, text, x, y, font) + } + + protected def copiedDrawHoveringText0(stack: MatrixStack, text: util.List[_ <: ITextProperties], x: Int, y: Int, font: FontRenderer): Unit = { if (!text.isEmpty) { - GlStateManager.disableRescaleNormal() - RenderHelper.disableStandardItemLighting() - GlStateManager.disableLighting() - GlStateManager.disableDepth() + RenderSystem.disableRescaleNormal() + RenderHelper.turnOff() + RenderSystem.disableLighting() + RenderSystem.disableDepthTest() - val textWidth = text.map(line => font.getStringWidth(line)).max + val textWidth = text.map(line => font.width(line)).max var posX = x + 12 var posY = y - 12 @@ -54,46 +75,50 @@ abstract class CustomGuiContainer[C <: Container](val inventoryContainer: C) ext posY = height - textHeight - 6 } - zLevel = 300f - itemRender.zLevel = 300f + setBlitOffset(300) + itemRenderer.blitOffset = 300f val bg = 0xF0100010 - drawGradientRect(posX - 3, posY - 4, posX + textWidth + 3, posY - 3, bg, bg) - drawGradientRect(posX - 3, posY + textHeight + 3, posX + textWidth + 3, posY + textHeight + 4, bg, bg) - drawGradientRect(posX - 3, posY - 3, posX + textWidth + 3, posY + textHeight + 3, bg, bg) - drawGradientRect(posX - 4, posY - 3, posX - 3, posY + textHeight + 3, bg, bg) - drawGradientRect(posX + textWidth + 3, posY - 3, posX + textWidth + 4, posY + textHeight + 3, bg, bg) + fillGradient(stack, posX - 3, posY - 4, posX + textWidth + 3, posY - 3, bg, bg) + fillGradient(stack, posX - 3, posY + textHeight + 3, posX + textWidth + 3, posY + textHeight + 4, bg, bg) + fillGradient(stack, posX - 3, posY - 3, posX + textWidth + 3, posY + textHeight + 3, bg, bg) + fillGradient(stack, posX - 4, posY - 3, posX - 3, posY + textHeight + 3, bg, bg) + fillGradient(stack, posX + textWidth + 3, posY - 3, posX + textWidth + 4, posY + textHeight + 3, bg, bg) val color1 = 0x505000FF val color2 = (color1 & 0x00FEFEFE) >> 1 | (color1 & 0xFF000000) - drawGradientRect(posX - 3, posY - 3 + 1, posX - 3 + 1, posY + textHeight + 3 - 1, color1, color2) - drawGradientRect(posX + textWidth + 2, posY - 3 + 1, posX + textWidth + 3, posY + textHeight + 3 - 1, color1, color2) - drawGradientRect(posX - 3, posY - 3, posX + textWidth + 3, posY - 3 + 1, color1, color1) - drawGradientRect(posX - 3, posY + textHeight + 2, posX + textWidth + 3, posY + textHeight + 3, color2, color2) - + fillGradient(stack, posX - 3, posY - 3 + 1, posX - 3 + 1, posY + textHeight + 3 - 1, color1, color2) + fillGradient(stack, posX + textWidth + 2, posY - 3 + 1, posX + textWidth + 3, posY + textHeight + 3 - 1, color1, color2) + fillGradient(stack, posX - 3, posY - 3, posX + textWidth + 3, posY - 3 + 1, color1, color1) + fillGradient(stack, posX - 3, posY + textHeight + 2, posX + textWidth + 3, posY + textHeight + 3, color2, color2) + + stack.pushPose() + stack.translate(0, 0, 400) + val renderType = IRenderTypeBuffer.immediate(Tessellator.getInstance.getBuilder()) for ((line, index) <- text.zipWithIndex) { - font.drawStringWithShadow(line.asInstanceOf[String], posX, posY, -1) + font.drawInBatch(LanguageMap.getInstance.getVisualOrder(line), posX, posY, -1, true, stack.last.pose, renderType, false, 0, 15728880) if (index == 0) { posY += 2 } posY += 10 } - zLevel = 0f - itemRender.zLevel = 0f - - GlStateManager.enableLighting() - GlStateManager.enableDepth() - RenderHelper.enableStandardItemLighting() - GlStateManager.enableRescaleNormal() + stack.popPose() + setBlitOffset(0) + itemRenderer.blitOffset = 0f + + RenderSystem.enableLighting() + RenderSystem.enableDepthTest() + RenderHelper.turnBackOn() + RenderSystem.enableRescaleNormal() } } - override def drawGradientRect(left: Int, top: Int, right: Int, bottom: Int, startColor: Int, endColor: Int): Unit = { - super.drawGradientRect(left, top, right, bottom, startColor, endColor) + override def fillGradient(stack: MatrixStack, left: Int, top: Int, right: Int, bottom: Int, startColor: Int, endColor: Int): Unit = { + super.fillGradient(stack, left, top, right, bottom, startColor, endColor) RenderState.makeItBlend() } - override def drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float): Unit = { - this.drawDefaultBackground() - super.drawScreen(mouseX, mouseY, partialTicks) - this.renderHoveredToolTip(mouseX, mouseY) + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, partialTicks: Float): Unit = { + this.renderBackground(stack) + super.render(stack, mouseX, mouseY, partialTicks) + this.renderTooltip(stack, mouseX, mouseY) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 36b42f1894..6cbc0f6a29 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -1,32 +1,38 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.Tier import li.cil.oc.common.container import li.cil.oc.common.inventory.DatabaseInventory -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.StringTextComponent -class Database(playerInventory: InventoryPlayer, val databaseInventory: DatabaseInventory) extends DynamicGuiContainer(new container.Database(playerInventory, databaseInventory)) with traits.LockedHotbar { - ySize = 256 +class Database(id: Int, playerInventory: PlayerInventory, val databaseInventory: DatabaseInventory) + extends DynamicGuiContainer(new container.Database(id, playerInventory, databaseInventory), + playerInventory, StringTextComponent.EMPTY) + with traits.LockedHotbar[container.Database] { + + imageHeight = 256 override def lockedStack = databaseInventory.container - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) {} + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) {} - override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1, 1) + override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color4f(1, 1, 1, 1) Textures.bind(Textures.GUI.Database) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) if (databaseInventory.tier > Tier.One) { Textures.bind(Textures.GUI.Database1) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } if (databaseInventory.tier > Tier.Two) { Textures.bind(Textures.GUI.Database2) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } } } diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 1a90a66b3e..f8debe2a80 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -1,27 +1,25 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentity.Disassembler) extends DynamicGuiContainer(new container.Disassembler(playerInventory, disassembler)) { - val progress = addWidget(new ProgressBar(18, 65)) +class Disassembler(id: Int, playerInventory: PlayerInventory, val disassembler: tileentity.Disassembler) + extends DynamicGuiContainer(new container.Disassembler(id, playerInventory, disassembler), + playerInventory, disassembler.getName) { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - fontRenderer.drawString( - Localization.localizeImmediately(disassembler.getName), - 8, 6, 0x404040) - } + val progress = addCustomWidget(new ProgressBar(18, 65)) - override def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) + override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Disassembler) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) progress.level = inventoryContainer.disassemblyProgress / 100.0 - drawWidgets() + drawWidgets(stack) } } diff --git a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala index 14ab5bd7ed..c5a88c11f2 100644 --- a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala @@ -2,14 +2,10 @@ package li.cil.oc.client.gui import li.cil.oc.Localization import li.cil.oc.common.container -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.IInventory +import li.cil.oc.common.inventory.SimpleInventory +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.PlayerInventory -class DiskDrive(playerInventory: InventoryPlayer, val drive: IInventory) extends DynamicGuiContainer(new container.DiskDrive(playerInventory, drive)) { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(drive.getName), - 8, 6, 0x404040) - } +class DiskDrive(id: Int, playerInventory: PlayerInventory, val drive: SimpleInventory) + extends DynamicGuiContainer(new container.DiskDrive(id, playerInventory, drive), playerInventory, drive.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Drive.scala b/src/main/scala/li/cil/oc/client/gui/Drive.scala index 74185a031e..b2de3d31b0 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drive.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drive.scala @@ -1,15 +1,17 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.item.data.DriveData -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.gui.GuiScreen -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.client.gui.screen +import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack +import net.minecraft.util.text.StringTextComponent -class Drive(playerInventory: InventoryPlayer, val driveStack: () => ItemStack) extends GuiScreen with traits.Window { +class Drive(playerInventory: PlayerInventory, val driveStack: () => ItemStack) extends screen.Screen(StringTextComponent.EMPTY) with traits.Window { override val windowHeight = 120 override def backgroundImage = Textures.GUI.Drive @@ -18,46 +20,43 @@ class Drive(playerInventory: InventoryPlayer, val driveStack: () => ItemStack) e protected var unmanagedButton: ImageButton = _ protected var lockedButton: ImageButton = _ - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - ClientPacketSender.sendDriveMode(unmanaged = false) - DriveData.setUnmanaged(driveStack(), unmanaged = false) - } else if (button.id == 1) { - ClientPacketSender.sendDriveMode(unmanaged = true) - DriveData.setUnmanaged(driveStack(), unmanaged = true) - } else if (button.id == 2) { - ClientPacketSender.sendDriveLock() - DriveData.lock(driveStack(), playerInventory.player) - } - updateButtonStates() - } - def updateButtonStates(): Unit = { val data = new DriveData(driveStack()) unmanagedButton.toggled = data.isUnmanaged managedButton.toggled = !unmanagedButton.toggled lockedButton.toggled = data.isLocked - lockedButton.enabled = !data.isLocked + lockedButton.active = !data.isLocked } - override def initGui(): Unit = { - super.initGui() - managedButton = new ImageButton(0, guiLeft + 11, guiTop + 11, 74, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.Managed, textColor = 0x608060, canToggle = true) - unmanagedButton = new ImageButton(1, guiLeft + 91, guiTop + 11, 74, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.Unmanaged, textColor = 0x608060, canToggle = true) - lockedButton = new ImageButton(2, guiLeft + 11, guiTop + windowHeight - 42, 44, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.ReadOnlyLock, textColor = 0x608060, canToggle = true) - add(buttonList, managedButton) - add(buttonList, unmanagedButton) - add(buttonList, lockedButton) + override protected def init(): Unit = { + super.init() + managedButton = new ImageButton(leftPos + 11, topPos + 11, 74, 18, new Button.IPressable { + override def onPress(b: Button) = { + ClientPacketSender.sendDriveMode(unmanaged = false) + DriveData.setUnmanaged(driveStack(), unmanaged = false) + } + }, Textures.GUI.ButtonDriveMode, text = new StringTextComponent(Localization.Drive.Managed), textColor = 0x608060, canToggle = true) + unmanagedButton = new ImageButton(leftPos + 91, topPos + 11, 74, 18, new Button.IPressable { + override def onPress(b: Button) = { + ClientPacketSender.sendDriveMode(unmanaged = true) + DriveData.setUnmanaged(driveStack(), unmanaged = true) + } + }, Textures.GUI.ButtonDriveMode, text = new StringTextComponent(Localization.Drive.Unmanaged), textColor = 0x608060, canToggle = true) + lockedButton = new ImageButton(leftPos + 11, topPos + windowHeight - 42, 44, 18, new Button.IPressable { + override def onPress(b: Button) = { + ClientPacketSender.sendDriveLock() + DriveData.lock(driveStack(), playerInventory.player) + } + }, Textures.GUI.ButtonDriveMode, text = new StringTextComponent(Localization.Drive.ReadOnlyLock), textColor = 0x608060, canToggle = true) + addButton(managedButton) + addButton(unmanagedButton) + addButton(lockedButton) updateButtonStates() } - override def updateScreen(): Unit = { - super.updateScreen() - } - - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - super.drawScreen(mouseX, mouseY, dt) - fontRenderer.drawSplitString(Localization.Drive.Warning, guiLeft + 11, guiTop + 37, xSize - 20, 0x404040) - fontRenderer.drawSplitString(Localization.Drive.LockWarning, guiLeft + 61, guiTop + windowHeight - 48, xSize - 68, 0x404040) + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.render(stack, mouseX, mouseY, dt) + font.drawWordWrap(new StringTextComponent(Localization.Drive.Warning), leftPos + 11, topPos + 37, imageWidth - 20, 0x404040) + font.drawWordWrap(new StringTextComponent(Localization.Drive.LockWarning), leftPos + 61, topPos + windowHeight - 48, imageWidth - 68, 0x404040) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index e144077cc9..2c149a33fa 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar @@ -11,18 +13,22 @@ import li.cil.oc.common.entity import li.cil.oc.util.PackedColor import li.cil.oc.util.RenderState import li.cil.oc.util.TextBuffer -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava._ -class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends DynamicGuiContainer(new container.Drone(playerInventory, drone)) with traits.DisplayBuffer { - xSize = 176 - ySize = 148 +class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) + extends DynamicGuiContainer(new container.Drone(id, playerInventory, drone), + playerInventory, StringTextComponent.EMPTY) + with traits.DisplayBuffer { + + imageWidth = 176 + imageHeight = 148 protected var powerButton: ImageButton = _ @@ -47,48 +53,44 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D private val inventoryX = 97 private val inventoryY = 7 - private val power = addWidget(new ProgressBar(28, 48)) + private val power = addCustomWidget(new ProgressBar(28, 48)) private val selectionSize = 20 private val selectionsStates = 17 - private val selectionStepV = 1 / selectionsStates.toDouble - - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - ClientPacketSender.sendDronePower(drone, !drone.isRunning) - } - } + private val selectionStepV = 1 / selectionsStates.toFloat - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { powerButton.toggled = drone.isRunning bufferRenderer.dirty = drone.statusText.lines.zipWithIndex.exists { case (line, i) => buffer.set(0, i, line, vertical = false) } - super.drawScreen(mouseX, mouseY, dt) + super.render(stack, mouseX, mouseY, dt) } - override def initGui() { - super.initGui() - powerButton = new ImageButton(0, guiLeft + 7, guiTop + 45, 18, 18, Textures.GUI.ButtonPower, canToggle = true) - add(buttonList, powerButton) + override protected def init() { + super.init() + powerButton = new ImageButton(leftPos + 7, topPos + 45, 18, 18, new Button.IPressable { + override def onPress(b: Button) = ClientPacketSender.sendDronePower(drone, !drone.isRunning) + }, Textures.GUI.ButtonPower, canToggle = true) + addButton(powerButton) } - override protected def drawBuffer() { - GlStateManager.translate(bufferX, bufferY, 0) + override protected def drawBuffer(stack: MatrixStack) { + stack.translate(bufferX, bufferY, 0) RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.scale(scale, scale, 1) + stack.scale(scale.toFloat, scale.toFloat, 1) RenderState.pushAttrib() - GlStateManager.depthMask(false) - GlStateManager.color(0.5f, 0.5f, 1f) - TextBufferRenderCache.render(bufferRenderer) + RenderSystem.depthMask(false) + RenderSystem.color3f(0.5f, 0.5f, 1f) + TextBufferRenderCache.render(stack, bufferRenderer) RenderState.popAttrib() } override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0 - override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { - drawBufferLayer() + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { + drawBufferLayer(stack) RenderState.pushAttrib() if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] @@ -97,50 +99,50 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D drone.globalBuffer * 100 / math.max(drone.globalBufferSize, 1), drone.globalBuffer, drone.globalBufferSize)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } - if (powerButton.isMouseOver) { + if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.addAll(asJavaCollection(if (drone.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() } - override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) + override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Drone) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - power.level = drone.globalBuffer.toDouble / math.max(drone.globalBufferSize.toDouble, 1.0) - drawWidgets() - if (drone.mainInventory.getSizeInventory > 0) { - drawSelection() + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) + power.level = drone.globalBuffer.toFloat / math.max(drone.globalBufferSize.toFloat, 1.0f) + drawWidgets(stack) + if (drone.mainInventory.getContainerSize > 0) { + drawSelection(stack) } - drawInventorySlots() + drawInventorySlots(stack) } // No custom slots, we just extend DynamicGuiContainer for the highlighting. - override protected def drawSlotBackground(x: Int, y: Int) {} + override protected def drawSlotBackground(stack: MatrixStack, x: Int, y: Int) {} - private def drawSelection() { + private def drawSelection(stack: MatrixStack) { val slot = drone.selectedSlot if (slot >= 0 && slot < 16) { RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) - val now = System.currentTimeMillis() / 1000.0 + val now = System.currentTimeMillis() / 1000.0f val offsetV = ((now - now.toInt) * selectionsStates).toInt * selectionStepV - val x = guiLeft + inventoryX - 1 + (slot % 4) * (selectionSize - 2) - val y = guiTop + inventoryY - 1 + (slot / 4) * (selectionSize - 2) + val x = leftPos + inventoryX - 1 + (slot % 4) * (selectionSize - 2) + val y = topPos + inventoryY - 1 + (slot / 4) * (selectionSize - 2) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y, zLevel).tex(0, offsetV).endVertex() - r.pos(x, y + selectionSize, zLevel).tex(0, offsetV + selectionStepV).endVertex() - r.pos(x + selectionSize, y + selectionSize, zLevel).tex(1, offsetV + selectionStepV).endVertex() - r.pos(x + selectionSize, y, zLevel).tex(1, offsetV).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y, getBlitOffset).uv(0, offsetV).endVertex() + r.vertex(stack.last.pose, x, y + selectionSize, getBlitOffset).uv(0, offsetV + selectionStepV).endVertex() + r.vertex(stack.last.pose, x + selectionSize, y + selectionSize, getBlitOffset).uv(1, offsetV + selectionStepV).endVertex() + r.vertex(stack.last.pose, x + selectionSize, y, getBlitOffset).uv(1, offsetV).endVertex() + t.end() } } } diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index bee3a20419..ff3d3ea238 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common @@ -11,134 +13,128 @@ import li.cil.oc.integration.util.ItemSearch import li.cil.oc.util.RenderState import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.client.gui.Gui -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.AbstractGui import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.inventory.Container -import net.minecraft.inventory.Slot -import net.minecraftforge.fml.common.Optional +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.Container +import net.minecraft.inventory.container.Slot +import net.minecraft.util.text.ITextComponent import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ -abstract class DynamicGuiContainer[C <: Container](container: C) extends CustomGuiContainer(container) { - protected var hoveredSlot: Option[Slot] = None +abstract class DynamicGuiContainer[C <: Container](container: C, inv: PlayerInventory, title: ITextComponent) + extends CustomGuiContainer(container, inv, title) { protected var hoveredStackNEI: StackOption = EmptyStack - protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { - fontRenderer.drawString( + protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { + font.drawShadow(stack, Localization.localizeImmediately("container.inventory"), - 8, ySize - 96 + 2, 0x404040) + 8, imageHeight - 96 + 2, 0x404040) } - override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { + super.renderLabels(stack, mouseX, mouseY) RenderState.pushAttrib() - drawSecondaryForegroundLayer(mouseX, mouseY) + drawSecondaryForegroundLayer(stack, mouseX, mouseY) - for (slot <- 0 until inventorySlots.inventorySlots.size()) { - drawSlotHighlight(inventorySlots.inventorySlots.get(slot)) + for (slot <- 0 until menu.slots.size()) { + drawSlotHighlight(stack, menu.getSlot(slot)) } RenderState.popAttrib() } - protected def drawSecondaryBackgroundLayer() {} + protected def drawSecondaryBackgroundLayer(stack: MatrixStack) {} - override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1, 1) + override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color4f(1, 1, 1, 1) Textures.bind(Textures.GUI.Background) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - drawSecondaryBackgroundLayer() + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) + drawSecondaryBackgroundLayer(stack) RenderState.makeItBlend() - GlStateManager.disableLighting() + RenderSystem.disableLighting() - drawInventorySlots() + drawInventorySlots(stack) } - protected def drawInventorySlots(): Unit = { - GlStateManager.pushMatrix() - GlStateManager.translate(guiLeft, guiTop, 0) - GlStateManager.disableDepth() - for (slot <- 0 until inventorySlots.inventorySlots.size()) { - drawSlotInventory(inventorySlots.inventorySlots.get(slot)) + protected def drawInventorySlots(stack: MatrixStack): Unit = { + stack.pushPose() + stack.translate(leftPos, topPos, 0) + RenderSystem.disableDepthTest() + for (slot <- 0 until menu.slots.size()) { + drawSlotInventory(stack, menu.getSlot(slot)) } - GlStateManager.enableDepth() - GlStateManager.popMatrix() + RenderSystem.enableDepthTest() + stack.popPose() RenderState.makeItBlend() } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { - hoveredSlot = (inventorySlots.inventorySlots collect { - case slot: Slot if isPointInRegion(slot.xPos, slot.yPos, 16, 16, mouseX, mouseY) => slot - }).headOption + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { hoveredStackNEI = ItemSearch.hoveredStack(this, mouseX, mouseY) - super.drawScreen(mouseX, mouseY, dt) - - if (Mods.JustEnoughItems.isModAvailable) { - drawJEIHighlights() - } + super.render(stack, mouseX, mouseY, dt) } - protected def drawSlotInventory(slot: Slot) { - GlStateManager.enableBlend() + protected def drawSlotInventory(stack: MatrixStack, slot: Slot) { + RenderSystem.enableBlend() slot match { case component: ComponentSlot if component.slot == common.Slot.None || component.tier == common.Tier.None => - if (!slot.getHasStack && slot.xPos >= 0 && slot.yPos >= 0 && component.tierIcon != null) { - drawDisabledSlot(component) + if (!slot.hasItem && slot.x >= 0 && slot.y >= 0 && component.tierIcon != null) { + drawDisabledSlot(stack, component) } case _ => - zLevel += 1 + setBlitOffset(getBlitOffset + 1) if (!isInPlayerInventory(slot)) { - drawSlotBackground(slot.xPos - 1, slot.yPos - 1) + drawSlotBackground(stack, slot.x - 1, slot.y - 1) } - if (!slot.getHasStack) { + if (!slot.hasItem) { slot match { case component: ComponentSlot => if (component.tierIcon != null) { Textures.bind(component.tierIcon) - Gui.drawModalRectWithCustomSizedTexture(slot.xPos, slot.yPos, 0, 0, 16, 16, 16, 16) + AbstractGui.blit(stack, slot.x, slot.y, getBlitOffset, 0, 0, 16, 16, 16, 16) } if (component.hasBackground) { - Textures.bind(slot.getBackgroundLocation) - Gui.drawModalRectWithCustomSizedTexture(slot.xPos, slot.yPos, 0, 0, 16, 16, 16, 16) + Textures.bind(component.getBackgroundLocation) + AbstractGui.blit(stack, slot.x, slot.y, getBlitOffset, 0, 0, 16, 16, 16, 16) } case _ => } - zLevel -= 1 + setBlitOffset(getBlitOffset - 1) } } - GlStateManager.disableBlend() + RenderSystem.disableBlend() } - protected def drawSlotHighlight(slot: Slot) { - if (mc.player.inventory.getItemStack.isEmpty) slot match { + protected def drawSlotHighlight(stack: MatrixStack, slot: Slot) { + if (minecraft.player.inventory.getCarried.isEmpty) slot match { case component: ComponentSlot if component.slot == common.Slot.None || component.tier == common.Tier.None => // Ignore. case _ => val currentIsInPlayerInventory = isInPlayerInventory(slot) val drawHighlight = hoveredSlot match { - case Some(hovered) => + case hovered: Slot => val hoveredIsInPlayerInventory = isInPlayerInventory(hovered) (currentIsInPlayerInventory != hoveredIsInPlayerInventory) && - ((currentIsInPlayerInventory && slot.getHasStack && isSelectiveSlot(hovered) && hovered.isItemValid(slot.getStack)) || - (hoveredIsInPlayerInventory && hovered.getHasStack && isSelectiveSlot(slot) && slot.isItemValid(hovered.getStack))) + ((currentIsInPlayerInventory && slot.hasItem && isSelectiveSlot(hovered) && hovered.mayPlace(slot.getItem)) || + (hoveredIsInPlayerInventory && hovered.hasItem && isSelectiveSlot(slot) && slot.mayPlace(hovered.getItem))) case _ => hoveredStackNEI match { - case SomeStack(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.isItemValid(stack) + case SomeStack(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.mayPlace(stack) case _ => false } } if (drawHighlight) { - zLevel += 100 - drawGradientRect( - slot.xPos, slot.yPos, - slot.xPos + 16, slot.yPos + 16, + setBlitOffset(getBlitOffset + 100) + fillGradient(stack, + slot.x, slot.y, + slot.x + 16, slot.y + 16, 0x80FFFFFF, 0x80FFFFFF) - zLevel -= 100 + setBlitOffset(getBlitOffset - 100) } } } @@ -148,49 +144,27 @@ abstract class DynamicGuiContainer[C <: Container](container: C) extends CustomG case _ => false } - protected def drawDisabledSlot(slot: ComponentSlot) { - GlStateManager.color(1, 1, 1, 1) + protected def drawDisabledSlot(stack: MatrixStack, slot: ComponentSlot) { + RenderSystem.color4f(1, 1, 1, 1) Textures.bind(slot.tierIcon) - Gui.drawModalRectWithCustomSizedTexture(slot.xPos, slot.yPos, 0, 0, 16, 16, 16, 16) + AbstractGui.blit(stack, slot.x, slot.y, getBlitOffset, 0, 0, 16, 16, 16, 16) } - protected def drawSlotBackground(x: Int, y: Int) { - GlStateManager.color(1, 1, 1, 1) + protected def drawSlotBackground(stack: MatrixStack, x: Int, y: Int) { + RenderSystem.color4f(1, 1, 1, 1) Textures.bind(Textures.GUI.Slot) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y + 18, zLevel + 1).tex(0, 1).endVertex() - r.pos(x + 18, y + 18, zLevel + 1).tex(1, 1).endVertex() - r.pos(x + 18, y, zLevel + 1).tex(1, 0).endVertex() - r.pos(x, y, zLevel + 1).tex(0, 0).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y + 18, getBlitOffset + 1).uv(0, 1).endVertex() + r.vertex(stack.last.pose, x + 18, y + 18, getBlitOffset + 1).uv(1, 1).endVertex() + r.vertex(stack.last.pose, x + 18, y, getBlitOffset + 1).uv(1, 0).endVertex() + r.vertex(stack.last.pose, x, y, getBlitOffset + 1).uv(0, 0).endVertex() + t.end() } private def isInPlayerInventory(slot: Slot) = container match { - case player: Player => slot.inventory == player.playerInventory + case player: Player => slot.container == player.playerInventory case _ => false } - - override def onGuiClosed(): Unit = { - super.onGuiClosed() - if(Mods.JustEnoughItems.isModAvailable) { - resetJEIHighlights() - } - } - - @Optional.Method(modid = Mods.IDs.JustEnoughItems) - private def drawJEIHighlights(): Unit = { - ModJEI.runtime.foreach { runtime => - val overlay = runtime.getItemListOverlay - hoveredSlot match { - case Some(hovered) if !isInPlayerInventory(hovered) && isSelectiveSlot(hovered) => - overlay.highlightStacks(overlay.getVisibleStacks.filter(hovered.isItemValid)) - case _ => overlay.highlightStacks(List[Nothing]()) - } - } - } - - @Optional.Method(modid = Mods.IDs.JustEnoughItems) - private def resetJEIHighlights() = ModJEI.runtime.foreach(_.getItemListOverlay.highlightStacks(List[Nothing]())) } diff --git a/src/main/scala/li/cil/oc/client/gui/ImageButton.scala b/src/main/scala/li/cil/oc/client/gui/ImageButton.scala index 837b665b49..7e68bfdbb3 100644 --- a/src/main/scala/li/cil/oc/client/gui/ImageButton.scala +++ b/src/main/scala/li/cil/oc/client/gui/ImageButton.scala @@ -1,84 +1,87 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.AbstractGui +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.client.gui.widget.button.Button.IPressable import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.ResourceLocation -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import org.lwjgl.opengl.GL11 -@SideOnly(Side.CLIENT) -class ImageButton(id: Int, xPos: Int, yPos: Int, w: Int, h: Int, +@OnlyIn(Dist.CLIENT) +class ImageButton(xPos: Int, yPos: Int, w: Int, h: Int, + handler: IPressable, val image: ResourceLocation = null, - text: String = null, + text: ITextComponent = StringTextComponent.EMPTY, val canToggle: Boolean = false, val textColor: Int = 0xE0E0E0, val textDisabledColor: Int = 0xA0A0A0, val textHoverColor: Int = 0xFFFFA0, - val textIndent: Int = -1) extends GuiButton(id, xPos, yPos, w, h, text) { + val textIndent: Int = -1) extends Button(xPos, yPos, w, h, text, handler) { var toggled = false var hoverOverride = false - override def drawButton(mc: Minecraft, mouseX: Int, mouseY: Int, partialTicks: Float) { + override def renderButton(stack: MatrixStack, mouseX: Int, mouseY: Int, partialTicks: Float) { if (visible) { Textures.bind(image) - GlStateManager.color(1, 1, 1, 1) - hovered = mouseX >= x && mouseY >= y && mouseX < x + width && mouseY < y + height + RenderSystem.color4f(1, 1, 1, 1) + isHovered = isMouseOver(mouseX, mouseY) val x0 = x val x1 = x + width val y0 = y val y1 = y + height - val isHovered = hoverOverride || getHoverState(isMouseOver) == 2 + val drawHover = hoverOverride || getYImage(isHovered) == 2 val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder if (image != null) { - val u0 = if (toggled) 0.5 else 0 - val u1 = u0 + (if (canToggle) 0.5 else 1) - val v0 = if (hoverOverride || getHoverState(hovered) == 2) 0.5 else 0 - val v1 = v0 + 0.5 + val u0 = if (toggled) 0.5f else 0 + val u1 = u0 + (if (canToggle) 0.5f else 1) + val v0 = if (drawHover) 0.5f else 0 + val v1 = v0 + 0.5f r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x0, y1, zLevel).tex(u0, v1).endVertex() - r.pos(x1, y1, zLevel).tex(u1, v1).endVertex() - r.pos(x1, y0, zLevel).tex(u1, v0).endVertex() - r.pos(x0, y0, zLevel).tex(u0, v0).endVertex() - } - else if (isHovered) { - GlStateManager.color(1, 1, 1, 0.8f) - - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION) - r.pos(x0, y1, zLevel).endVertex() - r.pos(x1, y1, zLevel).endVertex() - r.pos(x1, y0, zLevel).endVertex() - r.pos(x0, y0, zLevel).endVertex() + r.vertex(stack.last.pose, x0, y1, getBlitOffset).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, x1, y1, getBlitOffset).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, x1, y0, getBlitOffset).uv(u1, v0).endVertex() + r.vertex(stack.last.pose, x0, y0, getBlitOffset).uv(u0, v0).endVertex() } else { - GlStateManager.color(1, 1, 1, 0.4f) + if (drawHover) { + RenderSystem.color4f(1, 1, 1, 0.8f) + } + else { + RenderSystem.color4f(1, 1, 1, 0.4f) + } r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION) - r.pos(x0, y1, zLevel).endVertex() - r.pos(x1, y1, zLevel).endVertex() - r.pos(x1, y0, zLevel).endVertex() - r.pos(x0, y0, zLevel).endVertex() + r.vertex(stack.last.pose, x0, y1, getBlitOffset).endVertex() + r.vertex(stack.last.pose, x1, y1, getBlitOffset).endVertex() + r.vertex(stack.last.pose, x1, y0, getBlitOffset).endVertex() + r.vertex(stack.last.pose, x0, y0, getBlitOffset).endVertex() } - t.draw() + t.end() - if (displayString != null) { + if (getMessage != StringTextComponent.EMPTY) { val color = - if (!enabled) textDisabledColor - else if (hoverOverride || hovered) textHoverColor + if (!active) textDisabledColor + else if (hoverOverride || isHovered) textHoverColor else textColor - if (textIndent >= 0) drawString(mc.fontRenderer, displayString, textIndent + x, y + (height - 8) / 2, color) - else drawCenteredString(mc.fontRenderer, displayString, x + width / 2, y + (height - 8) / 2, color) + val mc = Minecraft.getInstance + if (textIndent >= 0) AbstractGui.drawString(stack, mc.font, getMessage, textIndent + x, y + (height - 8) / 2, color) + else AbstractGui.drawCenteredString(stack, mc.font, getMessage, x + width / 2, y + (height - 8) / 2, color) } } } diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index a5f7d6f8d1..fc9c5de99d 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.api import li.cil.oc.client.Textures @@ -8,15 +10,17 @@ import li.cil.oc.client.renderer.markdown.segment.InteractiveSegment import li.cil.oc.client.renderer.markdown.segment.Segment import li.cil.oc.client.{Manual => ManualAPI} import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.renderer.GlStateManager -import org.lwjgl.input.Mouse +import net.minecraft.client.gui.screen +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.client.util.InputMappings +import net.minecraft.util.text.ITextProperties +import net.minecraft.util.text.StringTextComponent +import org.lwjgl.glfw.GLFW import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ -class Manual extends GuiScreen with traits.Window { +class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window { final val documentMaxWidth = 230 final val documentMaxHeight = 176 final val scrollPosX = 244 @@ -34,7 +38,6 @@ class Manual extends GuiScreen with traits.Window { override def backgroundImage = Textures.GUI.Manual - var isDragging = false var document: Segment = null var documentHeight = 0 var currentSegment = None: Option[InteractiveSegment] @@ -58,7 +61,7 @@ class Manual extends GuiScreen with traits.Window { val content = Option(api.Manual.contentFor(ManualAPI.history.top.path)). getOrElse(asJavaIterable(Iterable("Document not found: " + ManualAPI.history.top.path))) document = Document.parse(content) - documentHeight = Document.height(document, documentMaxWidth, fontRenderer) + documentHeight = Document.height(document, documentMaxWidth, font) scrollTo(offset) } @@ -75,122 +78,139 @@ class Manual extends GuiScreen with traits.Window { refreshPage() } else { - Minecraft.getMinecraft.player.closeScreen() + minecraft.player.closeContainer() } } - override def actionPerformed(button: GuiButton): Unit = { - if (button.id >= 0 && button.id < ManualAPI.tabs.length) { - api.Manual.navigate(ManualAPI.tabs(button.id).path) - } - } - - override def initGui(): Unit = { - super.initGui() + override protected def init(): Unit = { + super.init() for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { - val x = guiLeft + tabPosX - val y = guiTop + tabPosY + i * (tabHeight - 1) - add(buttonList, new ImageButton(i, x, y, tabWidth, tabHeight, Textures.GUI.ManualTab)) + val x = leftPos + tabPosX + val y = topPos + tabPosY + i * (tabHeight - 1) + addButton(new ImageButton(x, y, tabWidth, tabHeight, new Button.IPressable { + override def onPress(b: Button) = api.Manual.navigate(tab.path) + }, Textures.GUI.ManualTab)) } - scrollButton = new ImageButton(-1, guiLeft + scrollPosX, guiTop + scrollPosY, 6, 13, Textures.GUI.ButtonScroll) - add(buttonList, scrollButton) + scrollButton = new ImageButton(leftPos + scrollPosX, topPos + scrollPosY, 6, 13, new Button.IPressable { + override def onPress(b: Button) = Unit + }, Textures.GUI.ButtonScroll) + addButton(scrollButton) refreshPage() } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - super.drawScreen(mouseX, mouseY, dt) + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.render(stack, mouseX, mouseY, dt) - scrollButton.enabled = canScroll + scrollButton.active = canScroll scrollButton.hoverOverride = isDragging for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { - val button = buttonList.get(i).asInstanceOf[ImageButton] - GlStateManager.pushMatrix() - GlStateManager.translate(button.x + 5, button.y + 5, zLevel) + val button = buttons.get(i).asInstanceOf[ImageButton] + stack.pushPose() + stack.translate(button.x + 5, button.y + 5, getBlitOffset) tab.renderer.render() - GlStateManager.popMatrix() + stack.popPose() } - currentSegment = Document.render(document, guiLeft + 8, guiTop + 8, documentMaxWidth, documentMaxHeight, offset, fontRenderer, mouseX, mouseY) + currentSegment = Document.render(stack, document, leftPos + 8, topPos + 8, documentMaxWidth, documentMaxHeight, offset, font, mouseX, mouseY) + def localizeAndWrap(text: String): java.util.List[_ <: ITextProperties] = { + val lines = Localization.localizeImmediately(text).lines.map(new StringTextComponent(_)) + seqAsJavaList(lines.toSeq) + } if (!isDragging) currentSegment match { case Some(segment) => segment.tooltip match { - case Some(text) if text.nonEmpty => drawHoveringText(seqAsJavaList(Localization.localizeImmediately(text).lines.toSeq), mouseX, mouseY, fontRenderer) + case Some(text) if text.nonEmpty => renderWrappedToolTip(stack, localizeAndWrap(text), mouseX, mouseY, font) case _ => } case _ => } if (!isDragging) for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { - val button = buttonList.get(i).asInstanceOf[ImageButton] + val button = buttons.get(i).asInstanceOf[ImageButton] if (mouseX > button.x && mouseX < button.x + tabWidth && mouseY > button.y && mouseY < button.y + tabHeight) tab.tooltip.foreach(text => { - drawHoveringText(seqAsJavaList(Localization.localizeImmediately(text).lines.toSeq), mouseX, mouseY, fontRenderer) + renderWrappedToolTip(stack, localizeAndWrap(text), mouseX, mouseY, font) }) } - if (canScroll && (isCoordinateOverScrollBar(mouseX - guiLeft, mouseY - guiTop) || isDragging)) { - drawHoveringText(seqAsJavaList(Seq(s"${100 * offset / maxOffset}%")), guiLeft + scrollPosX + scrollWidth, scrollButton.y + scrollButton.height + 1, fontRenderer) + if (canScroll && (isCoordinateOverScrollBar(mouseX - leftPos, mouseY - topPos) || isDragging)) { + val lines = seqAsJavaList(Seq(new StringTextComponent(s"${100 * offset / maxOffset}%"))) + renderWrappedToolTip(stack, lines, leftPos + scrollPosX + scrollWidth, scrollButton.y + scrollButton.getHeight + 1, font) } } - override def keyTyped(char: Char, code: Int): Unit = { - if (code == mc.gameSettings.keyBindJump.getKeyCode) { + override def keyPressed(keyCode: Int, scanCode: Int, mods: Int): Boolean = { + val input = InputMappings.getKey(keyCode, scanCode) + if (minecraft.options.keyJump.isActiveAndMatches(input)) { popPage() + return true } - else if (code == mc.gameSettings.keyBindInventory.getKeyCode) { - mc.player.closeScreen() + if (minecraft.options.keyInventory.isActiveAndMatches(input)) { + onClose() + return true } - else super.keyTyped(char, code) + super.keyPressed(keyCode, scanCode, mods) } - override def handleMouseInput(): Unit = { - super.handleMouseInput() - if (Mouse.hasWheel && Mouse.getEventDWheel != 0) { - if (math.signum(Mouse.getEventDWheel) < 0) scrollDown() - else scrollUp() - } + override def mouseScrolled(mouseX: Double, mouseY: Double, scroll: Double): Boolean = { + if (scroll < 0) scrollDown() + else scrollUp() + true } - override def mouseClicked(mouseX: Int, mouseY: Int, button: Int): Unit = { - super.mouseClicked(mouseX, mouseY, button) - - if (canScroll && button == 0 && isCoordinateOverScrollBar(mouseX - guiLeft, mouseY - guiTop)) { - isDragging = true + override def mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean = { + if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverScrollBar(mouseX.asInstanceOf[Int] - leftPos, mouseY.asInstanceOf[Int] - topPos)) { + setDragging(true) scrollMouse(mouseY) + return true + } + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + currentSegment.foreach(_.onMouseClick(mouseX.asInstanceOf[Int], mouseY.asInstanceOf[Int])) + return true } - else if (button == 0) currentSegment.foreach(_.onMouseClick(mouseX, mouseY)) - else if (button == 1) popPage() + if (button == GLFW.GLFW_MOUSE_BUTTON_RIGHT) { + popPage() + return true + } + super.mouseClicked(mouseX, mouseY, button) } - override protected def mouseClickMove(mouseX: Int, mouseY: Int, lastButtonClicked: Int, timeSinceMouseClick: Long) { - super.mouseClickMove(mouseX, mouseY, lastButtonClicked, timeSinceMouseClick) + override def mouseMoved(mouseX: Double, mouseY: Double): Unit = { + if (isDragging) scrollMouse(mouseY) + super.mouseMoved(mouseX, mouseY) + } + + override def mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean = { if (isDragging) { scrollMouse(mouseY) + return true } + super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY) } - override protected def mouseReleased(mouseX: Int, mouseY: Int, button: Int) { - super.mouseReleased(mouseX, mouseY, button) - if (button == 0) { - isDragging = false + override def mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean = { + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + setDragging(false) + return true } + super.mouseReleased(mouseX, mouseY, button) } - private def scrollMouse(mouseY: Int) { - scrollTo(math.round((mouseY - guiTop - scrollPosY - 6.5) * maxOffset / (scrollHeight - 13.0)).toInt) + private def scrollMouse(mouseY: Double) { + scrollTo(math.round((mouseY - topPos - scrollPosY - 6.5) * maxOffset / (scrollHeight - 13.0)).toInt) } - private def scrollUp() = scrollTo(offset - Document.lineHeight(fontRenderer) * 3) + private def scrollUp() = scrollTo(offset - Document.lineHeight(font) * 3) - private def scrollDown() = scrollTo(offset + Document.lineHeight(fontRenderer) * 3) + private def scrollDown() = scrollTo(offset + Document.lineHeight(font) * 3) private def scrollTo(row: Int): Unit = { ManualAPI.history.top.offset = math.max(0, math.min(maxOffset, row)) - val yMin = guiTop + scrollPosY + val yMin = topPos + scrollPosY if (maxOffset > 0) { scrollButton.y = yMin + (scrollHeight - 13) * offset / maxOffset } diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index 41a96f69e9..5fa53572fd 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar @@ -7,28 +9,30 @@ import li.cil.oc.common.container import li.cil.oc.common.container.ComponentSlot import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) extends DynamicGuiContainer(new container.Printer(playerInventory, printer)) { - xSize = 176 - ySize = 166 +class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity.Printer) + extends DynamicGuiContainer(new container.Printer(id, playerInventory, printer), + playerInventory, printer.getName) { - private val materialBar = addWidget(new ProgressBar(40, 21) { + imageWidth = 176 + imageHeight = 166 + + private val materialBar = addCustomWidget(new ProgressBar(40, 21) { override def width = 62 override def height = 12 override def barTexture = Textures.GUI.PrinterMaterial }) - private val inkBar = addWidget(new ProgressBar(40, 53) { + private val inkBar = addCustomWidget(new ProgressBar(40, 53) { override def width = 62 override def height = 12 override def barTexture = Textures.GUI.PrinterInk }) - private val progressBar = addWidget(new ProgressBar(105, 20) { + private val progressBar = addCustomWidget(new ProgressBar(105, 20) { override def width = 46 override def height = 46 @@ -36,39 +40,32 @@ class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) override def barTexture = Textures.GUI.PrinterProgress }) - override def initGui() { - super.initGui() - } - - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(printer.getName), - 8, 6, 0x404040) + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) RenderState.pushAttrib() if (isPointInRegion(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.add(inventoryContainer.amountMaterial + "/" + printer.maxAmountMaterial) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } if (isPointInRegion(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.add(inventoryContainer.amountInk + "/" + printer.maxAmountInk) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() } - override def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) + override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Printer) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) materialBar.level = inventoryContainer.amountMaterial / printer.maxAmountMaterial.toDouble inkBar.level = inventoryContainer.amountInk / printer.maxAmountInk.toDouble progressBar.level = inventoryContainer.progress - drawWidgets() - drawInventorySlots() + drawWidgets(stack) + drawInventorySlots(stack) } - override protected def drawDisabledSlot(slot: ComponentSlot) {} + override protected def drawDisabledSlot(stack: MatrixStack, slot: ComponentSlot) {} } diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index 8929fe3ac2..ed6ff9847e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -1,23 +1,28 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.util.EnumFacing +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.Direction +import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava.asJavaCollection -class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends DynamicGuiContainer(new container.Rack(playerInventory, rack)) { - ySize = 210 +class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) + extends DynamicGuiContainer(new container.Rack(id, playerInventory, rack), + playerInventory, rack.getName) { + + imageHeight = 210 final val busMasterBlankUVs = (195, 14, 3, 5) final val busMasterPresentUVs = (194, 20, 5, 5) @@ -73,109 +78,93 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends (83, 104) ) - final val busToSide = EnumFacing.values().filter(_ != EnumFacing.SOUTH) + final val busToSide = Direction.values().filter(_ != Direction.SOUTH) final val sideToBus = busToSide.zipWithIndex.toMap var relayButton: ImageButton = _ // bus -> mountable -> connectable - var wireButtons = Array.fill(rack.getSizeInventory)(Array.fill(4)(Array.fill(5)(null: ImageButton))) - - def sideName(side: EnumFacing) = side match { - case EnumFacing.UP => Localization.Rack.Top - case EnumFacing.DOWN => Localization.Rack.Bottom - case EnumFacing.EAST => Localization.Rack.Left - case EnumFacing.WEST => Localization.Rack.Right - case EnumFacing.NORTH => Localization.Rack.Back + var wireButtons = Array.fill(rack.getContainerSize)(Array.fill(4)(Array.fill(5)(null: ImageButton))) + + def sideName(side: Direction) = side match { + case Direction.UP => Localization.Rack.Top + case Direction.DOWN => Localization.Rack.Bottom + case Direction.EAST => Localization.Rack.Left + case Direction.WEST => Localization.Rack.Right + case Direction.NORTH => Localization.Rack.Back case _ => Localization.Rack.None } - def encodeButtonId(mountable: Int, connectable: Int, bus: Int) = { - // +1 to offset for relay button - 1 + mountable * 4 * 5 + connectable * 5 + bus - } - - def decodeButtonId(buttonId: Int) = { - // -1 to offset for relay button - val bus = (buttonId - 1) % 5 - val connectable = ((buttonId - 1) / 5) % 4 - val mountable = (buttonId - 1) / 5 / 4 - (mountable, connectable, bus) - } - - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - ClientPacketSender.sendRackRelayState(rack, !rack.isRelayEnabled) + protected def onRackButton(mountable: Int, connectable: Int, bus: Int) { + if (rack.nodeMapping(mountable)(connectable).contains(busToSide(bus))) { + ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, None) } else { - val (mountable, connectable, bus) = decodeButtonId(button.id) - if (rack.nodeMapping(mountable)(connectable).contains(busToSide(bus))) { - ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, None) - } - else { - ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, Option(busToSide(bus))) - } + ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, Option(busToSide(bus))) } } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { for (bus <- 0 until 5) { - for (mountable <- 0 until rack.getSizeInventory) { + for (mountable <- 0 until rack.getContainerSize) { val presence = inventoryContainer.nodePresence(mountable) for (connectable <- 0 until 4) { wireButtons(mountable)(connectable)(bus).visible = presence(connectable) } } } - relayButton.displayString = if (rack.isRelayEnabled) Localization.Rack.RelayEnabled else Localization.Rack.RelayDisabled - super.drawScreen(mouseX, mouseY, dt) + val relayMessage = if (rack.isRelayEnabled) Localization.Rack.RelayEnabled else Localization.Rack.RelayDisabled + relayButton.setMessage(new StringTextComponent(relayMessage)) + super.render(stack, mouseX, mouseY, dt) } - override def initGui() { - super.initGui() + override protected def init() { + super.init() - relayButton = new ImageButton(0, guiLeft + 101, guiTop + 96, 65, 18, Textures.GUI.ButtonRelay, Localization.Rack.RelayDisabled, textIndent = 18) - add(buttonList, relayButton) + relayButton = new ImageButton(leftPos + 101, topPos + 96, 65, 18, new Button.IPressable { + override def onPress(b: Button) = ClientPacketSender.sendRackRelayState(rack, !rack.isRelayEnabled) + }, Textures.GUI.ButtonRelay, new StringTextComponent(Localization.Rack.RelayDisabled), textIndent = 18) + addButton(relayButton) val (mw, mh) = hoverMasterSize val (sw, sh) = hoverSlaveSize val (_, _, _, mbh) = busMasterBlankUVs val (_, _, _, sbh) = busSlaveBlankUVs for (bus <- 0 until 5) { - for (mountable <- 0 until rack.getSizeInventory) { + for (mountable <- 0 until rack.getContainerSize) { val offset = mountable * (mbh + sbh * 3 + busGap) val (bx, by) = busStart(bus) { - val button = new ImageButton(encodeButtonId(mountable, 0, bus), guiLeft + bx, guiTop + by + offset + 1, mw, mh) - add(buttonList, button) + val button = new ImageButton(leftPos + bx, topPos + by + offset + 1, mw, mh, new Button.IPressable { + override def onPress(b: Button) = onRackButton(mountable, 0, bus) + }) + addButton(button) wireButtons(mountable)(0)(bus) = button } for (connectable <- 0 until 3) { - val button = new ImageButton(encodeButtonId(mountable, connectable + 1, bus), guiLeft + bx, guiTop + by + offset + 1 + mbh + sbh * connectable, sw, sh) - add(buttonList, button) + val button = new ImageButton(leftPos + bx, topPos + by + offset + 1 + mbh + sbh * connectable, sw, sh, new Button.IPressable { + override def onPress(b: Button) = onRackButton(mountable, connectable, bus) + }) + addButton(button) wireButtons(mountable)(connectable + 1)(bus) = button } } } } - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) RenderState.pushAttrib() // Prevents NEI render glitch. - fontRenderer.drawString( - Localization.localizeImmediately(rack.getName), - 8, 6, 0x404040) - - GlStateManager.color(1, 1, 1) - mc.renderEngine.bindTexture(Textures.GUI.Rack) + RenderSystem.color3f(1, 1, 1) + minecraft.getTextureManager.bind(Textures.GUI.Rack) if (rack.isRelayEnabled) { val (left, top, w, h) = relayModeUVs for ((x, y) <- wireRelay) { - drawRect(x, y, w, h, left, top) + drawRect(stack, x, y, w, h, left, top) } } @@ -185,20 +174,20 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends val (scx, scy, scw, sch) = connectorSlaveUVs val (sbx, sby, sbw, sbh) = busSlaveBlankUVs val (spx, spy, spw, sph) = busSlavePresentUVs - for (mountable <- 0 until rack.getSizeInventory) { + for (mountable <- 0 until rack.getContainerSize) { val presence = inventoryContainer.nodePresence(mountable) // Draw connectable indicators next to item slots. val (cx, cy) = connectorStart(mountable) if (presence(0)) { - drawRect(cx, cy, mcw, mch, mcx, mcy) + drawRect(stack, cx, cy, mcw, mch, mcx, mcy) rack.nodeMapping(mountable)(0) match { case Some(side) => val bus = sideToBus(side) val (mwx, mwy, mww, mwh) = wireMasterUVs(bus) for (i <- 0 to bus) { val xOffset = mcw + i * (mpw + mww) - drawRect(cx + xOffset, cy, mww, mwh, mwx, mwy) + drawRect(stack, cx + xOffset, cy, mww, mwh, mwx, mwy) } case _ => } @@ -210,7 +199,7 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends val yOffset = (mch + connectorGap) + (sch + connectorGap) * (connectable - 1) for (i <- 0 to bus) { val xOffset = scw + i * (spw + sww) - drawRect(cx + xOffset, cy + yOffset, sww, swh, swx, swy) + drawRect(stack, cx + xOffset, cy + yOffset, sww, swh, swx, swy) } case _ => } @@ -219,7 +208,7 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends for (connectable <- 1 until 4) { if (presence(connectable)) { val yOffset = (mch + connectorGap) + (sch + connectorGap) * (connectable - 1) - drawRect(cx, cy + yOffset, scw, sch, scx, scy) + drawRect(stack, cx, cy + yOffset, scw, sch, scx, scy) } } @@ -228,17 +217,17 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends for (bus <- 0 until 5) { val (bx, by) = busStart(bus) if (presence(0)) { - drawRect(bx - 1, by + yOffset, mpw, mph, mpx, mpy) + drawRect(stack, bx - 1, by + yOffset, mpw, mph, mpx, mpy) } else { - drawRect(bx, by + yOffset, mbw, mbh, mbx, mby) + drawRect(stack, bx, by + yOffset, mbw, mbh, mbx, mby) } for (connectable <- 0 until 3) { if (presence(connectable + 1)) { - drawRect(bx - 1, by + yOffset + mph + sph * connectable, spw, sph, spx, spy) + drawRect(stack, bx - 1, by + yOffset + mph + sph * connectable, spw, sph, spx, spy) } else { - drawRect(bx, by + yOffset + mbh + sbh * connectable, sbw, sbh, sbx, sby) + drawRect(stack, bx, by + yOffset + mbh + sbh * connectable, sbw, sbh, sbx, sby) } } } @@ -248,38 +237,38 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends val x = 122 val y = 20 + bus * 11 - fontRenderer.drawString( + font.draw(stack, Localization.localizeImmediately(sideName(busToSide(bus))), x, y, 0x404040) } - if (relayButton.isMouseOver) { + if (relayButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.addAll(asJavaCollection(Localization.Rack.RelayModeTooltip.lines.toIterable)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() } - override def drawSecondaryBackgroundLayer() { - GlStateManager.color(1, 1, 1) // Required under Linux. - mc.renderEngine.bindTexture(Textures.GUI.Rack) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + override def drawSecondaryBackgroundLayer(stack: MatrixStack) { + RenderSystem.color3f(1, 1, 1) // Required under Linux. + minecraft.getTextureManager.bind(Textures.GUI.Rack) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } - private def drawRect(x: Int, y: Int, w: Int, h: Int, u: Int, v: Int): Unit = { + private def drawRect(stack: MatrixStack, x: Int, y: Int, w: Int, h: Int, u: Int, v: Int): Unit = { val u0 = u / 256f val v0 = v / 256f val u1 = u0 + w / 256f val v1 = v0 + h / 256f val t = Tessellator.getInstance() - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y, windowZ).tex(u0, v0).endVertex() - r.pos(x, y + h, windowZ).tex(u0, v1).endVertex() - r.pos(x + w, y + h, windowZ).tex(u1, v1).endVertex() - r.pos(x + w, y, windowZ).tex(u1, v0).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y, windowZ).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, x, y + h, windowZ).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, x + w, y + h, windowZ).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, x + w, y, windowZ).uv(u1, v0).endVertex() + t.end() } } diff --git a/src/main/scala/li/cil/oc/client/gui/Raid.scala b/src/main/scala/li/cil/oc/client/gui/Raid.scala index 56e75f707e..cea87d4555 100644 --- a/src/main/scala/li/cil/oc/client/gui/Raid.scala +++ b/src/main/scala/li/cil/oc/client/gui/Raid.scala @@ -1,27 +1,20 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Raid(playerInventory: InventoryPlayer, val raid: tileentity.Raid) extends DynamicGuiContainer(new container.Raid(playerInventory, raid)) { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(raid.getName), - 8, 6, 0x404040) +class Raid(id: Int, playerInventory: PlayerInventory, val raid: tileentity.Raid) + extends DynamicGuiContainer(new container.Raid(id, playerInventory, raid), + playerInventory, raid.getName) { - fontRenderer.drawSplitString( - Localization.Raid.Warning, - 8, 46, 0x404040, width - 16) - } - - override def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) // Required under Linux. + override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) // Required under Linux. Textures.bind(Textures.GUI.Raid) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala index fcbe9d50a4..62dd995059 100644 --- a/src/main/scala/li/cil/oc/client/gui/Relay.scala +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -2,90 +2,91 @@ package li.cil.oc.client.gui import java.text.DecimalFormat +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.Rectangle2d import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory import org.lwjgl.opengl.GL11 -import org.lwjgl.util.Rectangle -class Relay(playerInventory: InventoryPlayer, val relay: tileentity.Relay) extends DynamicGuiContainer(new container.Relay(playerInventory, relay)) { +class Relay(id: Int, playerInventory: PlayerInventory, val relay: tileentity.Relay) + extends DynamicGuiContainer(new container.Relay(id, playerInventory, relay), + playerInventory, relay.getName) { + private val format = new DecimalFormat("#.##hz") - val tabPosition = new Rectangle(xSize, 10, 23, 26) + val tabPosition = new Rectangle2d(imageWidth, 10, 23, 26) - override protected def drawSecondaryBackgroundLayer(): Unit = { - super.drawSecondaryBackgroundLayer() + override protected def drawSecondaryBackgroundLayer(stack: MatrixStack): Unit = { + super.drawSecondaryBackgroundLayer(stack) // Tab background. - GlStateManager.color(1, 1, 1, 1) - Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.GUI.UpgradeTab) + RenderSystem.color4f(1, 1, 1, 1) + Minecraft.getInstance.getTextureManager.bind(Textures.GUI.UpgradeTab) val x = windowX + tabPosition.getX val y = windowY + tabPosition.getY val w = tabPosition.getWidth val h = tabPosition.getHeight val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y + h, zLevel).tex(0, 1).endVertex() - r.pos(x + w, y + h, zLevel).tex(1, 1).endVertex() - r.pos(x + w, y, zLevel).tex(1, 0).endVertex() - r.pos(x, y, zLevel).tex(0, 0).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y + h, getBlitOffset).uv(0, 1).endVertex() + r.vertex(stack.last.pose, x + w, y + h, getBlitOffset).uv(1, 1).endVertex() + r.vertex(stack.last.pose, x + w, y, getBlitOffset).uv(1, 0).endVertex() + r.vertex(stack.last.pose, x, y, getBlitOffset).uv(0, 0).endVertex() + t.end() } - override def mouseClicked(mouseX: Int, mouseY: Int, button: Int): Unit = { + override def mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean = { // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... - val originalWidth = xSize + val originalWidth = imageWidth try { - xSize += tabPosition.getWidth + imageWidth += tabPosition.getWidth super.mouseClicked(mouseX, mouseY, button) } finally { - xSize = originalWidth + imageWidth = originalWidth } } - override def mouseReleased(mouseX: Int, mouseY: Int, button: Int): Unit = { + override def mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean = { // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... - val originalWidth = xSize + val originalWidth = imageWidth try { - xSize += tabPosition.getWidth + imageWidth += tabPosition.getWidth super.mouseReleased(mouseX, mouseY, button) } finally { - xSize = originalWidth + imageWidth = originalWidth } } - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int): Unit = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(relay.getName), - 8, 6, 0x404040) + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int): Unit = { + super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) - fontRenderer.drawString( + font.draw(stack, Localization.Switch.TransferRate, 14, 20, 0x404040) - fontRenderer.drawString( + font.draw(stack, Localization.Switch.PacketsPerCycle, 14, 39, 0x404040) - fontRenderer.drawString( + font.draw(stack, Localization.Switch.QueueSize, 14, 58, 0x404040) - fontRenderer.drawString( + font.draw(stack, format.format(20f / inventoryContainer.relayDelay), 108, 20, 0x404040) - fontRenderer.drawString( + font.draw(stack, inventoryContainer.packetsPerCycleAvg + " / " + inventoryContainer.relayAmount, 108, 39, thresholdBasedColor(inventoryContainer.packetsPerCycleAvg, math.ceil(inventoryContainer.relayAmount / 2f).toInt, inventoryContainer.relayAmount)) - fontRenderer.drawString( + font.draw(stack, inventoryContainer.queueSize + " / " + inventoryContainer.maxQueueSize, 108, 58, thresholdBasedColor(inventoryContainer.queueSize, inventoryContainer.maxQueueSize / 2, inventoryContainer.maxQueueSize)) } diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index a2ad0ec5dd..e3b4a167c6 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api @@ -13,18 +15,21 @@ import li.cil.oc.common.container import li.cil.oc.common.tileentity import li.cil.oc.integration.opencomputers import li.cil.oc.util.RenderState -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.player.InventoryPlayer -import org.lwjgl.input.Keyboard -import org.lwjgl.input.Mouse +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.StringTextComponent +import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava._ -class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) extends DynamicGuiContainer(new container.Robot(playerInventory, robot)) with traits.InputBuffer { +class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Robot) + extends DynamicGuiContainer(new container.Robot(id, playerInventory, robot), + playerInventory, StringTextComponent.EMPTY) + with traits.InputBuffer { + override protected val buffer: TextBuffer = robot.components.collect { case Some(buffer: api.internal.TextBuffer) => buffer }.headOption.orNull @@ -36,8 +41,8 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten private val deltaY = if (buffer != null) 0 else withScreenHeight - noScreenHeight - xSize = 256 - ySize = 256 - deltaY + imageWidth = 256 + imageHeight = 256 - deltaY protected var powerButton: ImageButton = _ @@ -45,7 +50,6 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten // Scroll offset for robot inventory. private var inventoryOffset = 0 - private var isDragging = false private def canScroll = robot.inventorySize > 16 @@ -72,63 +76,61 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten private val scrollWidth = 8 private val scrollHeight = 94 - private val power = addWidget(new ProgressBar(26, 156 - deltaY)) + private val power = addCustomWidget(new ProgressBar(26, 156 - deltaY)) private val selectionSize = 20 private val selectionsStates = 17 - private val selectionStepV = 1 / selectionsStates.toDouble - - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - ClientPacketSender.sendComputerPower(robot, !robot.isRunning) - } - } + private val selectionStepV = 1 / selectionsStates.toFloat - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { powerButton.toggled = robot.isRunning - scrollButton.enabled = canScroll + scrollButton.active = canScroll scrollButton.hoverOverride = isDragging if (robot.inventorySize < 16 + inventoryOffset * 4) { scrollTo(0) } - super.drawScreen(mouseX, mouseY, dt) + super.render(stack, mouseX, mouseY, dt) } - override def initGui() { - super.initGui() - powerButton = new ImageButton(0, guiLeft + 5, guiTop + 153 - deltaY, 18, 18, Textures.GUI.ButtonPower, canToggle = true) - scrollButton = new ImageButton(1, guiLeft + scrollX + 1, guiTop + scrollY + 1, 6, 13, Textures.GUI.ButtonScroll) - add(buttonList, powerButton) - add(buttonList, scrollButton) + override protected def init() { + super.init() + powerButton = new ImageButton(leftPos + 5, topPos + 153 - deltaY, 18, 18, new Button.IPressable { + override def onPress(b: Button) = ClientPacketSender.sendComputerPower(robot, !robot.isRunning) + }, Textures.GUI.ButtonPower, canToggle = true) + scrollButton = new ImageButton(leftPos + scrollX + 1, topPos + scrollY + 1, 6, 13, new Button.IPressable { + override def onPress(b: Button) = Unit + }, Textures.GUI.ButtonScroll) + addButton(powerButton) + addButton(scrollButton) } - override def drawBuffer() { + override def drawBuffer(stack: MatrixStack) { if (buffer != null) { - GlStateManager.translate(bufferX, bufferY, 0) + stack.translate(bufferX, bufferY, 0) RenderState.disableEntityLighting() - GlStateManager.pushMatrix() - GlStateManager.translate(-3, -3, 0) - GlStateManager.color(1, 1, 1, 1) + stack.pushPose() + stack.translate(-3, -3, 0) + RenderSystem.color4f(1, 1, 1, 1) BufferRenderer.drawBackground() - GlStateManager.popMatrix() + stack.popPose() RenderState.makeItBlend() val scaleX = bufferRenderWidth / buffer.renderWidth val scaleY = bufferRenderHeight / buffer.renderHeight - val scale = math.min(scaleX, scaleY) + val scale = math.min(scaleX, scaleY).toFloat if (scaleX > scale) { - GlStateManager.translate(buffer.renderWidth * (scaleX - scale) / 2, 0, 0) + stack.translate(buffer.renderWidth * (scaleX - scale) / 2, 0, 0) } else if (scaleY > scale) { - GlStateManager.translate(0, buffer.renderHeight * (scaleY - scale) / 2, 0) + stack.translate(0, buffer.renderHeight * (scaleY - scale) / 2, 0) } - GlStateManager.scale(scale, scale, scale) - GlStateManager.scale(this.scale, this.scale, 1) - BufferRenderer.drawText(buffer) + stack.scale(scale, scale, scale) + stack.scale(this.scale.toFloat, this.scale.toFloat, 1) + BufferRenderer.drawText(stack, buffer) } } - override protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { - drawBufferLayer() + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { + drawBufferLayer(stack) RenderState.pushAttrib() if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] @@ -137,75 +139,65 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten ((robot.globalBuffer / robot.globalBufferSize) * 100).toInt, robot.globalBuffer.toInt, robot.globalBufferSize.toInt)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } - if (powerButton.isMouseOver) { + if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.addAll(asJavaCollection(if (robot.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() } - override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { - GlStateManager.color(1, 1, 1) + override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { + RenderSystem.color3f(1, 1, 1) if (buffer != null) Textures.bind(Textures.GUI.Robot) else Textures.bind(Textures.GUI.RobotNoScreen) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) power.level = robot.globalBuffer / robot.globalBufferSize - drawWidgets() + drawWidgets(stack) if (robot.inventorySize > 0) { - drawSelection() + drawSelection(stack) } - drawInventorySlots() + drawInventorySlots(stack) } // No custom slots, we just extend DynamicGuiContainer for the highlighting. - override protected def drawSlotBackground(x: Int, y: Int) {} + override protected def drawSlotBackground(stack: MatrixStack, x: Int, y: Int) {} - override protected def keyTyped(char: Char, code: Int) { - if (code == Keyboard.KEY_ESCAPE) { - super.keyTyped(char, code) - } - } - - override protected def mouseClicked(mouseX: Int, mouseY: Int, button: Int) { - super.mouseClicked(mouseX, mouseY, button) - if (canScroll && button == 0 && isCoordinateOverScrollBar(mouseX - guiLeft, mouseY - guiTop)) { - isDragging = true + override def mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean = { + val mx = mouseX.asInstanceOf[Int] + val my = mouseY.asInstanceOf[Int] + if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverScrollBar(mx - leftPos, my - topPos)) { + setDragging(true) scrollMouse(mouseY) + true } + else super.mouseClicked(mouseX, mouseY, button) } - override protected def mouseReleased(mouseX: Int, mouseY: Int, button: Int) { - super.mouseReleased(mouseX, mouseY, button) - if (button == 0) { - isDragging = false - } - } - - override protected def mouseClickMove(mouseX: Int, mouseY: Int, lastButtonClicked: Int, timeSinceMouseClick: Long) { - super.mouseClickMove(mouseX, mouseY, lastButtonClicked, timeSinceMouseClick) + override def mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean = { if (isDragging) { scrollMouse(mouseY) + true } + else super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY) } - private def scrollMouse(mouseY: Int) { - scrollTo(math.round((mouseY - guiTop - scrollY + 1 - 6.5) * maxOffset / (scrollHeight - 13.0)).toInt) + private def scrollMouse(mouseY: Double) { + scrollTo(math.round((mouseY - topPos - scrollY + 1 - 6.5) * maxOffset / (scrollHeight - 13.0)).toInt) } - override def handleMouseInput() { - super.handleMouseInput() - if (Mouse.hasWheel && Mouse.getEventDWheel != 0) { - val mouseX = Mouse.getEventX * width / mc.displayWidth - guiLeft - val mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1 - guiTop - if (isCoordinateOverInventory(mouseX, mouseY) || isCoordinateOverScrollBar(mouseX, mouseY)) { - if (math.signum(Mouse.getEventDWheel) < 0) scrollDown() - else scrollUp() - } + override def mouseScrolled(mouseX: Double, mouseY: Double, scroll: Double): Boolean = { + val mx = mouseX.asInstanceOf[Int] + val my = mouseY.asInstanceOf[Int] + if (isCoordinateOverInventory(mx, my) || isCoordinateOverScrollBar(mx, my)) { + if (scroll < 0) scrollDown() + else scrollUp() + true } + else super.mouseScrolled(mouseX, mouseY, scroll) } private def isCoordinateOverInventory(x: Int, y: Int) = @@ -222,26 +214,7 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten private def scrollTo(row: Int) { inventoryOffset = math.max(0, math.min(maxOffset, row)) - for (index <- 4 until 68) { - val slot = inventorySlots.getSlot(index) - val displayIndex = index - inventoryOffset * 4 - 4 - if (displayIndex >= 0 && displayIndex < 16) { - slot.xPos = 1 + inventoryX + (displayIndex % 4) * slotSize - slot.yPos = 1 + inventoryY + (displayIndex / 4) * slotSize - } - else { - // Hide the rest! - slot.xPos = -10000 - slot.yPos = -10000 - } - } - val yMin = guiTop + scrollY + 1 - if (maxOffset > 0) { - scrollButton.y = yMin + (scrollHeight - 15) * inventoryOffset / maxOffset - } - else { - scrollButton.y = yMin - } + menu.generateSlotsFor(inventoryOffset) } override protected def changeSize(w: Double, h: Double, recompile: Boolean): Double = { @@ -255,24 +228,24 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten math.min(scaleX, scaleY) } - private def drawSelection() { + private def drawSelection(stack: MatrixStack) { val slot = robot.selectedSlot - inventoryOffset * 4 if (slot >= 0 && slot < 16) { RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) - val now = System.currentTimeMillis() / 1000.0 + val now = System.currentTimeMillis() / 1000.0f val offsetV = ((now - now.toInt) * selectionsStates).toInt * selectionStepV - val x = guiLeft + inventoryX - 1 + (slot % 4) * (selectionSize - 2) - val y = guiTop + inventoryY - 1 + (slot / 4) * (selectionSize - 2) + val x = leftPos + inventoryX - 1 + (slot % 4) * (selectionSize - 2) + val y = topPos + inventoryY - 1 + (slot / 4) * (selectionSize - 2) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y, zLevel).tex(0, offsetV).endVertex() - r.pos(x, y + selectionSize, zLevel).tex(0, offsetV + selectionStepV).endVertex() - r.pos(x + selectionSize, y + selectionSize, zLevel).tex(1, offsetV + selectionStepV).endVertex() - r.pos(x + selectionSize, y, zLevel).tex(1, offsetV).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y, getBlitOffset).uv(0, offsetV).endVertex() + r.vertex(stack.last.pose, x, y + selectionSize, getBlitOffset).uv(0, offsetV + selectionStepV).endVertex() + r.vertex(stack.last.pose, x + selectionSize, y + selectionSize, getBlitOffset).uv(1, offsetV + selectionStepV).endVertex() + r.vertex(stack.last.pose, x + selectionSize, y, getBlitOffset).uv(1, offsetV).endVertex() + t.end() } } } diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index 104b2518a5..60a4d43c8e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -1,13 +1,18 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import org.lwjgl.input.Mouse +import net.minecraft.client.gui.screen +import net.minecraft.util.text.StringTextComponent +import org.lwjgl.glfw.GLFW + +class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val hasKeyboardCallback: () => Boolean, val hasPower: () => Boolean) + extends screen.Screen(StringTextComponent.EMPTY) with traits.InputBuffer { -class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val hasKeyboardCallback: () => Boolean, val hasPower: () => Boolean) extends traits.InputBuffer { override protected def hasKeyboard = hasKeyboardCallback() override protected def bufferX = 8 + x @@ -22,54 +27,56 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has private var mx, my = -1 - override def handleMouseInput() { - super.handleMouseInput() - if (hasMouse && Mouse.hasWheel && Mouse.getEventDWheel != 0) { - val mouseX = Mouse.getEventX * width / mc.displayWidth - val mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1 + override def mouseScrolled(mouseX: Double, mouseY: Double, scroll: Double): Boolean = { + if (hasMouse) { toBufferCoordinates(mouseX, mouseY) match { case Some((bx, by)) => - val scroll = math.signum(Mouse.getEventDWheel) - buffer.mouseScroll(bx, by, scroll, null) + buffer.mouseScroll(bx, by, math.signum(scroll).asInstanceOf[Int], null) + return true case _ => // Ignore when out of bounds. } } + super.mouseScrolled(mouseX, mouseY, scroll) } - override protected def mouseClicked(mouseX: Int, mouseY: Int, button: Int) { - super.mouseClicked(mouseX, mouseY, button) + override def mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean = { if (hasMouse) { - if (button == 0 || button == 1) { + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT || button == GLFW.GLFW_MOUSE_BUTTON_RIGHT) { clickOrDrag(mouseX, mouseY, button) + return true } } + super.mouseClicked(mouseX, mouseY, button) } - protected override def mouseClickMove(mouseX: Int, mouseY: Int, button: Int, timeSinceLast: Long) { - super.mouseClickMove(mouseX, mouseY, button, timeSinceLast) - if (hasMouse && timeSinceLast > 10) { - if (button == 0 || button == 1) { + override def mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean = { + if (hasMouse) { + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT || button == GLFW.GLFW_MOUSE_BUTTON_RIGHT) { clickOrDrag(mouseX, mouseY, button) + return true } } + super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY) } - override protected def mouseReleased(mouseX: Int, mouseY: Int, button: Int) { - super.mouseReleased(mouseX, mouseY, button) - if (hasMouse && button >= 0) { + override def mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean = { + if (hasMouse) { if (didClick) { toBufferCoordinates(mouseX, mouseY) match { case Some((bx, by)) => buffer.mouseUp(bx, by, button, null) case _ => buffer.mouseUp(-1.0, -1.0, button, null) } } + val hasClicked = didClick didClick = false mx = -1 my = -1 + if (hasClicked) return true } + super.mouseReleased(mouseX, mouseY, button) } - private def clickOrDrag(mouseX: Int, mouseY: Int, button: Int) { + private def clickOrDrag(mouseX: Double, mouseY: Double, button: Int) { toBufferCoordinates(mouseX, mouseY) match { case Some((bx, by)) if bx.toInt != mx || (by*2).toInt != my => if (mx >= 0 && my >= 0) buffer.mouseDrag(bx, by, button, null) @@ -81,7 +88,7 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has } } - private def toBufferCoordinates(mouseX: Int, mouseY: Int): Option[(Double, Double)] = { + private def toBufferCoordinates(mouseX: Double, mouseY: Double): Option[(Double, Double)] = { val bx = (mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth val by = (mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight val bw = buffer.getViewportWidth @@ -90,19 +97,19 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has else None } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - super.drawScreen(mouseX, mouseY, dt) - drawBufferLayer() + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.render(stack, mouseX, mouseY, dt) + drawBufferLayer(stack) } - override def drawBuffer() { - GlStateManager.translate(x, y, 0) + override def drawBuffer(stack: MatrixStack) { + stack.translate(x, y, 0) BufferRenderer.drawBackground() if (hasPower()) { - GlStateManager.translate(bufferMargin, bufferMargin, 0) - GlStateManager.scale(scale, scale, 1) + stack.translate(bufferMargin, bufferMargin, 0) + stack.scale(scale.toFloat, scale.toFloat, 1) RenderState.makeItBlend() - BufferRenderer.drawText(buffer) + BufferRenderer.drawText(stack, buffer) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index 98f4af99cf..f2afe79dee 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -1,5 +1,7 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} @@ -7,61 +9,57 @@ import li.cil.oc.common.container import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.common.tileentity import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.client.gui.widget.button.Button +import net.minecraft.entity.player.PlayerInventory import scala.collection.convert.WrapAsJava.asJavaCollection -class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory, val rack: Option[tileentity.Rack] = None, val slot: Int = 0) extends DynamicGuiContainer(new container.Server(playerInventory, serverInventory)) with traits.LockedHotbar { +class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val rack: Option[tileentity.Rack] = None, val slot: Int = 0) + extends DynamicGuiContainer(new container.Server(id, playerInventory, serverInventory), + playerInventory, serverInventory.getName) + with traits.LockedHotbar[container.Server] { + protected var powerButton: ImageButton = _ override def lockedStack = serverInventory.container - protected override def actionPerformed(button: GuiButton) { - if (button.id == 0) { - rack match { - case Some(t) => ClientPacketSender.sendServerPower(t, slot, !inventoryContainer.isRunning) - case _ => - } - } - } - - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { // Close GUI if item is removed from rack. rack match { - case Some(t) if t.getStackInSlot(slot) != serverInventory.container => - Minecraft.getMinecraft.displayGuiScreen(null) + case Some(t) if t.getItem(slot) != serverInventory.container => + onClose() return case _ => } powerButton.visible = !inventoryContainer.isItem powerButton.toggled = inventoryContainer.isRunning - super.drawScreen(mouseX, mouseY, dt) + super.render(stack, mouseX, mouseY, dt) } - override def initGui() { - super.initGui() - powerButton = new ImageButton(0, guiLeft + 48, guiTop + 33, 18, 18, Textures.GUI.ButtonPower, canToggle = true) - add(buttonList, powerButton) + override protected def init() { + super.init() + powerButton = new ImageButton(leftPos + 48, topPos + 33, 18, 18, new Button.IPressable { + override def onPress(b: Button) = rack match { + case Some(t) => ClientPacketSender.sendServerPower(t, slot, !inventoryContainer.isRunning) + case _ => + } + }, Textures.GUI.ButtonPower, canToggle = true) + addButton(powerButton) } - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(serverInventory.getName), - 8, 6, 0x404040) - if (powerButton.isMouseOver) { + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { + super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) + if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] tooltip.addAll(asJavaCollection(if (inventoryContainer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) - copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer) - } + copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) + } } - override def drawSecondaryBackgroundLayer() { - GlStateManager.color(1, 1, 1) + override def drawSecondaryBackgroundLayer(stack: MatrixStack) { + RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Server) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Tablet.scala b/src/main/scala/li/cil/oc/client/gui/Tablet.scala index 94a934ed52..a77c33b995 100644 --- a/src/main/scala/li/cil/oc/client/gui/Tablet.scala +++ b/src/main/scala/li/cil/oc/client/gui/Tablet.scala @@ -3,15 +3,12 @@ package li.cil.oc.client.gui import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Tablet(playerInventory: InventoryPlayer, val tablet: TabletWrapper) extends DynamicGuiContainer(new container.Tablet(playerInventory, tablet)) with traits.LockedHotbar { - override def lockedStack = tablet.stack +class Tablet(id: Int, playerInventory: PlayerInventory, val tablet: TabletWrapper) + extends DynamicGuiContainer(new container.Tablet(id, playerInventory, tablet), + playerInventory, tablet.getName) + with traits.LockedHotbar[container.Tablet] { - override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { - super.drawSecondaryForegroundLayer(mouseX, mouseY) - fontRenderer.drawString( - Localization.localizeImmediately(tablet.getName), - 8, 6, 0x404040) - } + override def lockedStack = tablet.stack } diff --git a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala index 6cee5dc1d6..a9f0ea06d9 100644 --- a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala +++ b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala @@ -1,78 +1,75 @@ package li.cil.oc.client.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.PacketSender import li.cil.oc.client.Textures import li.cil.oc.common.tileentity -import li.cil.oc.util.OldScaledResolution -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.GuiTextField -import net.minecraft.client.gui.ScaledResolution -import net.minecraft.client.renderer.GlStateManager -import org.lwjgl.input.Keyboard +import net.minecraft.client.gui.screen +import net.minecraft.client.gui.widget.TextFieldWidget +import net.minecraft.util.text.StringTextComponent +import org.lwjgl.glfw.GLFW -class Waypoint(val waypoint: tileentity.Waypoint) extends GuiScreen { - var guiLeft = 0 - var guiTop = 0 - var xSize = 0 - var ySize = 0 +class Waypoint(val waypoint: tileentity.Waypoint) extends screen.Screen(StringTextComponent.EMPTY) { + val imageWidth = 176 + val imageHeight = 24 + var leftPos = 0 + var topPos = 0 + passEvents = false - var textField: GuiTextField = _ + var textField: TextFieldWidget = _ - override def updateScreen(): Unit = { - super.updateScreen() - if (mc.player.getDistanceSq(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) > 64) { - mc.player.closeScreen() + override def tick(): Unit = { + super.tick() + textField.tick() + if (minecraft.player.distanceToSqr(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) > 64) { + minecraft.player.closeContainer() } } - override def doesGuiPauseGame(): Boolean = false + override def isPauseScreen(): Boolean = false - override def initGui(): Unit = { - super.initGui() + override protected def init(): Unit = { + super.init() + leftPos = (width - imageWidth) / 2 + topPos = (height - imageHeight) / 2 - val screenSize = new ScaledResolution(mc) - val guiSize = new OldScaledResolution(mc, 176, 24) - val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) - guiLeft = midX - guiSize.getScaledWidth / 2 - guiTop = midY - guiSize.getScaledHeight / 2 - xSize = guiSize.getScaledWidth - ySize = guiSize.getScaledHeight - - textField = new GuiTextField(0, fontRenderer, guiLeft + 7, guiTop + 8, 164 - 12, 12) - textField.setMaxStringLength(32) - textField.setEnableBackgroundDrawing(false) + textField = new TextFieldWidget(font, leftPos + 7, topPos + 8, 164 - 12, 12, StringTextComponent.EMPTY) { + override def keyPressed(keyCode: Int, scanCode: Int, mods: Int): Boolean = { + if (keyCode == GLFW.GLFW_KEY_ENTER) { + val label = textField.getValue.take(32) + if (label != waypoint.label) { + waypoint.label = label + PacketSender.sendWaypointLabel(waypoint) + onClose() + } + return true + } + super.keyPressed(keyCode, scanCode, mods) + } + } + textField.setMaxLength(32) + textField.setBordered(false) textField.setCanLoseFocus(false) - textField.setFocused(true) + textField.setFocus(true) textField.setTextColor(0xFFFFFF) - textField.setText(waypoint.label) + textField.setValue(waypoint.label) + addWidget(textField) - Keyboard.enableRepeatEvents(true) + setInitialFocus(textField) + minecraft.keyboardHandler.setSendRepeatsToGui(true) } - override def onGuiClosed(): Unit = { - super.onGuiClosed() - Keyboard.enableRepeatEvents(false) - } - - override def keyTyped(char: Char, code: Int): Unit = { - if (!textField.textboxKeyTyped(char, code)) { - if (code == Keyboard.KEY_RETURN) { - val label = textField.getText.take(32) - if (label != waypoint.label) { - waypoint.label = label - PacketSender.sendWaypointLabel(waypoint) - mc.player.closeScreen() - } - } - else super.keyTyped(char, code) - } + override def removed(): Unit = { + super.removed() + minecraft.keyboardHandler.setSendRepeatsToGui(false) } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - super.drawScreen(mouseX, mouseY, dt) - GlStateManager.color(1, 1, 1) // Required under Linux. - mc.renderEngine.bindTexture(Textures.GUI.Waypoint) - drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - textField.drawTextBox() + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.render(stack, mouseX, mouseY, dt) + RenderSystem.color3f(1, 1, 1) // Required under Linux. + minecraft.getTextureManager.bind(Textures.GUI.Waypoint) + blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) + textField.render(stack, mouseX, mouseY, dt) } } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala b/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala index 05fe40575f..c8014c24f5 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala @@ -1,12 +1,13 @@ package li.cil.oc.client.gui.traits +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.gui.screen.Screen -trait DisplayBuffer extends GuiScreen { +trait DisplayBuffer extends Screen { protected def bufferX: Int protected def bufferY: Int @@ -21,13 +22,13 @@ trait DisplayBuffer extends GuiScreen { protected var scale = 0.0 - override def initGui() = { - super.initGui() - BufferRenderer.init(Minecraft.getMinecraft.renderEngine) + override protected def init() = { + super.init() + BufferRenderer.init(Minecraft.getInstance.textureManager) guiSizeChanged = true } - protected def drawBufferLayer() { + protected def drawBufferLayer(stack: MatrixStack) { val oldWidth = currentWidth val oldHeight = currentHeight currentWidth = bufferColumns @@ -36,15 +37,15 @@ trait DisplayBuffer extends GuiScreen { RenderState.checkError(getClass.getName + ".drawBufferLayer: entering (aka: wasntme)") - GlStateManager.pushMatrix() + stack.pushPose() RenderState.disableEntityLighting() - drawBuffer() - GlStateManager.popMatrix() + drawBuffer(stack) + stack.popPose() RenderState.checkError(getClass.getName + ".drawBufferLayer: buffer layer") } - protected def drawBuffer(): Unit + protected def drawBuffer(stack: MatrixStack): Unit protected def changeSize(w: Double, h: Double, recompile: Boolean): Double } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala index e80873bdd3..ff10fd2020 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala @@ -1,16 +1,19 @@ package li.cil.oc.client.gui.traits +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.KeyBindings import li.cil.oc.client.Textures import li.cil.oc.integration.util.ItemSearch import li.cil.oc.util.RenderState -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.inventory.GuiContainer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.inventory.ContainerScreen import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.input.Keyboard +import net.minecraft.client.util.InputMappings +import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.GL11 import scala.collection.mutable @@ -24,102 +27,119 @@ trait InputBuffer extends DisplayBuffer { protected def hasKeyboard: Boolean - private val pressedKeys = mutable.Map.empty[Int, Char] + private val pressedKeys = mutable.Set.empty[Int] private var showKeyboardMissing = 0L - override def doesGuiPauseGame = false + override def isPauseScreen = false - override def initGui() = { - super.initGui() - Keyboard.enableRepeatEvents(true) + override protected def init() = { + super.init() + Minecraft.getInstance.keyboardHandler.setSendRepeatsToGui(true) } - override protected def drawBufferLayer() { - super.drawBufferLayer() + override protected def drawBufferLayer(stack: MatrixStack) { + super.drawBufferLayer(stack) if (System.currentTimeMillis() - showKeyboardMissing < 1000) { Textures.bind(Textures.GUI.KeyboardMissing) - GlStateManager.disableDepth() + RenderSystem.disableDepthTest() val x = bufferX + buffer.renderWidth - 16 val y = bufferY + buffer.renderHeight - 16 val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y + 16, 0).tex(0, 1).endVertex() - r.pos(x + 16, y + 16, 0).tex(1, 1).endVertex() - r.pos(x + 16, y, 0).tex(1, 0).endVertex() - r.pos(x, y, 0).tex(0, 0).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y + 16, 0).uv(0, 1).endVertex() + r.vertex(stack.last.pose, x + 16, y + 16, 0).uv(1, 1).endVertex() + r.vertex(stack.last.pose, x + 16, y, 0).uv(1, 0).endVertex() + r.vertex(stack.last.pose, x, y, 0).uv(0, 0).endVertex() + t.end() - GlStateManager.enableDepth() + RenderSystem.enableDepthTest() RenderState.checkError(getClass.getName + ".drawBufferLayer: keyboard icon") } } - override def onGuiClosed() = { - super.onGuiClosed() - if (buffer != null) for ((code, char) <- pressedKeys) { - buffer.keyUp(char, code, null) + override def removed() = { + super.removed() + Minecraft.getInstance.keyboardHandler.setSendRepeatsToGui(false) + if (buffer != null) for (code <- pressedKeys) { + buffer.keyUp('\u0000', code, null) } - Keyboard.enableRepeatEvents(false) } - override def handleKeyboardInput() { - super.handleKeyboardInput() - - if (this.isInstanceOf[GuiContainer] && ItemSearch.isInputFocused) return + def onInput(input: InputMappings.Input): Boolean = { + if (KeyBindings.clipboardPaste.isActiveAndMatches(input)) { + if (buffer != null) { + if (hasKeyboard) buffer.clipboard(Minecraft.getInstance.keyboardHandler.getClipboard, null) + else showKeyboardMissing = System.currentTimeMillis() + } + return true + } + false + } - val code = Keyboard.getEventKey - if (buffer != null && code != Keyboard.KEY_ESCAPE && code != Keyboard.KEY_F11) { - if (hasKeyboard) { - if (Keyboard.getEventKeyState) { - val char = Keyboard.getEventCharacter - if (!pressedKeys.contains(code) || !ignoreRepeat(char, code)) { - buffer.keyDown(char, code, null) - pressedKeys += code -> char - } - } - else pressedKeys.remove(code) match { - case Some(char) => buffer.keyUp(char, code, null) - case _ => // Wasn't pressed while viewing the screen. + override def charTyped(codePt: Char, mods: Int): Boolean = { + if (!this.isInstanceOf[ContainerScreen[_]] || !ItemSearch.isInputFocused) { + if (buffer != null) { + if (hasKeyboard) { + buffer.keyDown(codePt, 0, null) + buffer.keyUp(codePt, 0, null) } + else showKeyboardMissing = System.currentTimeMillis() + return true + } + } + super.charTyped(codePt, mods) + } - if (KeyBindings.isPastingClipboard) { - buffer.clipboard(GuiScreen.getClipboardString, null) + override def keyPressed(keyCode: Int, scanCode: Int, mods: Int): Boolean = { + if (onInput(InputMappings.getKey(keyCode, scanCode))) return true + if (!this.isInstanceOf[ContainerScreen[_]] || !ItemSearch.isInputFocused) { + if (buffer != null && keyCode != GLFW.GLFW_KEY_UNKNOWN) { + if (hasKeyboard) { + if (pressedKeys.add(keyCode) || !ignoreRepeat(keyCode)) { + buffer.keyDown('\u0000', keyCode, null) + } } - } - else { - showKeyboardMissing = System.currentTimeMillis() + else showKeyboardMissing = System.currentTimeMillis() + return true } } + super.keyPressed(keyCode, scanCode, mods) } - override protected def mouseClicked(x: Int, y: Int, button: Int) { - super.mouseClicked(x, y, button) - val isMiddleMouseButton = button == 2 - val isBoundMouseButton = KeyBindings.isPastingClipboard - if (buffer != null && (isMiddleMouseButton || isBoundMouseButton)) { - if (hasKeyboard) { - buffer.clipboard(GuiScreen.getClipboardString, null) - } - else { - showKeyboardMissing = System.currentTimeMillis() - } + override def keyReleased(keyCode: Int, scanCode: Int, mods: Int): Boolean = { + if (pressedKeys.remove(keyCode)) { + buffer.keyUp('\u0000', keyCode, null) + return true } + // Wasn't pressed while viewing the screen. + super.keyReleased(keyCode, scanCode, mods) + } + + override def mouseClicked(x: Double, y: Double, button: Int): Boolean = { + if (onInput(InputMappings.Type.MOUSE.getOrCreate(button))) return true + if (buffer != null && button == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) { + if (hasKeyboard) buffer.clipboard(Minecraft.getInstance.keyboardHandler.getClipboard, null) + else showKeyboardMissing = System.currentTimeMillis() + return true + } + super.mouseClicked(x, y, button) } - private def ignoreRepeat(char: Char, code: Int) = { - code == Keyboard.KEY_LCONTROL || - code == Keyboard.KEY_RCONTROL || - code == Keyboard.KEY_LMENU || - code == Keyboard.KEY_RMENU || - code == Keyboard.KEY_LSHIFT || - code == Keyboard.KEY_RSHIFT || - code == Keyboard.KEY_LMETA || - code == Keyboard.KEY_RMETA + private def ignoreRepeat(keyCode: Int) = { + keyCode == GLFW.GLFW_KEY_LEFT_CONTROL || + keyCode == GLFW.GLFW_KEY_RIGHT_CONTROL || + keyCode == GLFW.GLFW_KEY_MENU || + keyCode == GLFW.GLFW_KEY_LEFT_ALT || + keyCode == GLFW.GLFW_KEY_RIGHT_ALT || + keyCode == GLFW.GLFW_KEY_LEFT_SHIFT || + keyCode == GLFW.GLFW_KEY_RIGHT_SHIFT || + keyCode == GLFW.GLFW_KEY_LEFT_SUPER || + keyCode == GLFW.GLFW_KEY_RIGHT_SUPER } } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala b/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala index 57c9a8c2e9..37eb9cc1fd 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala @@ -1,18 +1,17 @@ package li.cil.oc.client.gui.traits -import net.minecraft.client.gui.inventory.GuiContainer -import net.minecraft.inventory.ClickType -import net.minecraft.inventory.Slot +import net.minecraft.client.gui.screen.inventory.ContainerScreen +import net.minecraft.inventory.container.ClickType +import net.minecraft.inventory.container.Container +import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -trait LockedHotbar extends GuiContainer { +trait LockedHotbar[C <: Container] extends ContainerScreen[C] { def lockedStack: ItemStack - override def handleMouseClick(slot: Slot, slotId: Int, mouseButton: Int, clickType: ClickType): Unit = { - if (slot == null || !slot.getStack.isItemEqual(lockedStack)) { - super.handleMouseClick(slot, slotId, mouseButton, clickType) + override def slotClicked(slot: Slot, slotId: Int, mouseButton: Int, clickType: ClickType): Unit = { + if (slot == null || !slot.getItem.sameItem(lockedStack)) { + super.slotClicked(slot, slotId, mouseButton, clickType) } } - - protected override def checkHotbarKeys(keyCode: Int) = false } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/Window.scala b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala index 9ca7e0b09a..18487bded8 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/Window.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala @@ -2,44 +2,39 @@ package li.cil.oc.client.gui.traits import java.util -import li.cil.oc.util.OldScaledResolution -import net.minecraft.client.gui.Gui -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.ScaledResolution +import com.mojang.blaze3d.matrix.MatrixStack +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.AbstractGui +import net.minecraft.client.gui.screen.Screen import net.minecraft.util.ResourceLocation -trait Window extends GuiScreen { - var guiLeft = 0 - var guiTop = 0 - var xSize = 0 - var ySize = 0 +trait Window extends Screen { + var leftPos = 0 + var topPos = 0 + var imageWidth = 0 + var imageHeight = 0 val windowWidth = 176 val windowHeight = 166 def backgroundImage: ResourceLocation - protected def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + override def isPauseScreen = false - override def doesGuiPauseGame = false + override protected def init(): Unit = { + super.init() - override def initGui(): Unit = { - super.initGui() - - val screenSize = new ScaledResolution(mc) - val guiSize = new OldScaledResolution(mc, windowWidth, windowHeight) - val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) - guiLeft = midX - guiSize.getScaledWidth / 2 - guiTop = midY - guiSize.getScaledHeight / 2 - xSize = guiSize.getScaledWidth - ySize = guiSize.getScaledHeight + imageWidth = windowWidth + imageHeight = windowHeight + leftPos = (width - imageWidth) / 2 + topPos = (height - imageHeight) / 2 } - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - mc.renderEngine.bindTexture(backgroundImage) - Gui.drawModalRectWithCustomSizedTexture(guiLeft, guiTop, 0, 0, xSize, ySize, windowWidth, windowHeight) - - super.drawScreen(mouseX, mouseY, dt) + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.render(stack, mouseX, mouseY, dt) + Minecraft.getInstance.getTextureManager.bind(backgroundImage) + // Texture width and height are intentionally backwards. + AbstractGui.blit(stack, leftPos, topPos, getBlitOffset, 0, 0, imageWidth, imageHeight, windowHeight, windowWidth) } } diff --git a/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala b/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala index beb5ef240a..a1abc50fbb 100644 --- a/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala +++ b/src/main/scala/li/cil/oc/client/gui/widget/ProgressBar.scala @@ -1,5 +1,6 @@ package li.cil.oc.client.gui.widget +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.client.Textures import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats @@ -14,25 +15,25 @@ class ProgressBar(val x: Int, val y: Int) extends Widget { var level = 0.0 - def draw() { + def draw(stack: MatrixStack) { if (level > 0) { val u0 = 0 - val u1 = level + val u1 = level.toFloat val v0 = 0 val v1 = 1 val tx = owner.windowX + x val ty = owner.windowY + y - val w = width * level + val w = (width * level).toFloat Textures.bind(barTexture) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(tx, ty, owner.windowZ).tex(u0, v0).endVertex() - r.pos(tx, ty + height, owner.windowZ).tex(u0, v1).endVertex() - r.pos(tx + w, ty + height, owner.windowZ).tex(u1, v1).endVertex() - r.pos(tx + w, ty, owner.windowZ).tex(u1, v0).endVertex() - t.draw() + r.vertex(stack.last.pose, tx, ty, owner.windowZ).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, tx, ty + height, owner.windowZ).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, tx + w, ty + height, owner.windowZ).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, tx + w, ty, owner.windowZ).uv(u1, v0).endVertex() + t.end() } } } diff --git a/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala b/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala index db15521376..9c55386ddd 100644 --- a/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala +++ b/src/main/scala/li/cil/oc/client/gui/widget/Widget.scala @@ -1,5 +1,8 @@ package li.cil.oc.client.gui.widget +import com.mojang.blaze3d.matrix.MatrixStack + +@Deprecated abstract class Widget { var owner: WidgetContainer = _ @@ -11,5 +14,5 @@ abstract class Widget { def height: Int - def draw(): Unit + def draw(stack: MatrixStack): Unit } diff --git a/src/main/scala/li/cil/oc/client/gui/widget/WidgetContainer.scala b/src/main/scala/li/cil/oc/client/gui/widget/WidgetContainer.scala index e94089dac4..6b476e2888 100644 --- a/src/main/scala/li/cil/oc/client/gui/widget/WidgetContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/widget/WidgetContainer.scala @@ -1,11 +1,14 @@ package li.cil.oc.client.gui.widget +import com.mojang.blaze3d.matrix.MatrixStack + import scala.collection.mutable +@Deprecated trait WidgetContainer { protected val widgets = mutable.ArrayBuffer.empty[Widget] - def addWidget[T <: Widget](widget: T) = { + def addCustomWidget[T <: Widget](widget: T) = { widgets += widget widget.owner = this widget @@ -17,7 +20,7 @@ trait WidgetContainer { def windowZ = 0f - def drawWidgets() { - widgets.foreach(_.draw()) + def drawWidgets(stack: MatrixStack) { + widgets.foreach(_.draw(stack)) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala index 9af67f8e5e..dc292ede06 100644 --- a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala @@ -1,16 +1,22 @@ package li.cil.oc.client.renderer +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.util.ExtendedAABB._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.{BlockPosition, RenderState} import li.cil.oc.{Constants, Settings, api, common} +import net.minecraft.client.Minecraft import net.minecraft.client.renderer._ import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.{RayTraceResult, Vec3d} -import net.minecraftforge.client.event.DrawBlockHighlightEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.math.AxisAlignedBB +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.vector.Vector3d +import net.minecraftforge.client.event.DrawHighlightEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.opengl.GL11 import scala.util.Random @@ -21,139 +27,131 @@ object HighlightRenderer { lazy val tablet = api.Items.get(Constants.ItemName.Tablet) @SubscribeEvent - def onDrawBlockHighlight(e: DrawBlockHighlightEvent): Unit = if (e.getTarget != null && e.getTarget.getBlockPos != null) { + def onDrawBlockHighlight(e: DrawHighlightEvent.HighlightBlock): Unit = if (e.getTarget != null && e.getTarget.getBlockPos != null) { val hitInfo = e.getTarget - val world = e.getPlayer.getEntityWorld + val world = Minecraft.getInstance.level val blockPos = BlockPosition(hitInfo.getBlockPos, world) - if (hitInfo.typeOfHit == RayTraceResult.Type.BLOCK && api.Items.get(e.getPlayer.getHeldItemMainhand) == tablet) { + val stack = e.getMatrix + if (api.Items.get(Minecraft.getInstance.player.getItemInHand(Hand.MAIN_HAND)) == tablet) { val isAir = world.isAirBlock(blockPos) if (!isAir) { - val block = world.getBlock(blockPos) - val bounds = block.getSelectedBoundingBox(world.getBlockState(hitInfo.getBlockPos), world, hitInfo.getBlockPos).offset(-blockPos.x, -blockPos.y, -blockPos.z) - val sideHit = hitInfo.sideHit - val playerPos = new Vec3d( - e.getPlayer.prevPosX + (e.getPlayer.posX - e.getPlayer.prevPosX) * e.getPartialTicks, - e.getPlayer.prevPosY + (e.getPlayer.posY - e.getPlayer.prevPosY) * e.getPartialTicks, - e.getPlayer.prevPosZ + (e.getPlayer.posZ - e.getPlayer.prevPosZ) * e.getPartialTicks) - val renderPos = blockPos.offset(-playerPos.x, -playerPos.y, -playerPos.z) - - GlStateManager.pushMatrix() + val shape = world.getBlockState(hitInfo.getBlockPos).getShape(world, hitInfo.getBlockPos, ISelectionContext.of(e.getInfo.getEntity)) + val (minX, minY, minZ) = (shape.min(Direction.Axis.X).toFloat, shape.min(Direction.Axis.Y).toFloat, shape.min(Direction.Axis.Z).toFloat) + val (maxX, maxY, maxZ) = (shape.max(Direction.Axis.X).toFloat, shape.max(Direction.Axis.Y).toFloat, shape.max(Direction.Axis.Z).toFloat) + val sideHit = hitInfo.getDirection + val renderPos = blockPos + + stack.pushPose() RenderState.pushAttrib() RenderState.makeItBlend() Textures.bind(Textures.Model.HologramEffect) - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - GlStateManager.color(0.0F, 1.0F, 0.0F, 0.4F) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + RenderSystem.color4f(0.0F, 1.0F, 0.0F, 0.4F) - GlStateManager.translate(renderPos.x, renderPos.y, renderPos.z) - GlStateManager.scale(1.002, 1.002, 1.002) + stack.translate(renderPos.x, renderPos.y, renderPos.z) + stack.scale(1.002f, 1.002f, 1.002f) if (Settings.get.hologramFlickerFrequency > 0 && random.nextDouble() < Settings.get.hologramFlickerFrequency) { - val (sx, sy, sz) = (1 - math.abs(sideHit.getFrontOffsetX), 1 - math.abs(sideHit.getFrontOffsetY), 1 - math.abs(sideHit.getFrontOffsetZ)) - GlStateManager.scale(1 + random.nextGaussian() * 0.01, 1 + random.nextGaussian() * 0.001, 1 + random.nextGaussian() * 0.01) - GlStateManager.translate(random.nextGaussian() * 0.01 * sx, random.nextGaussian() * 0.01 * sy, random.nextGaussian() * 0.01 * sz) + val (sx, sy, sz) = (1 - math.abs(sideHit.getStepX), 1 - math.abs(sideHit.getStepY), 1 - math.abs(sideHit.getStepZ)) + stack.scale(1f + (random.nextGaussian() * 0.01).toFloat, 1f + (random.nextGaussian() * 0.001).toFloat, 1f + (random.nextGaussian() * 0.01).toFloat) + stack.translate((random.nextGaussian() * 0.01 * sx).toFloat, (random.nextGaussian() * 0.01 * sy).toFloat, (random.nextGaussian() * 0.01 * sz).toFloat) } val t = Tessellator.getInstance() - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) sideHit match { - case EnumFacing.UP => - r.pos(bounds.maxX, bounds.maxY + 0.002, bounds.maxZ).tex(bounds.maxZ * 16, bounds.maxX * 16).endVertex() - r.pos(bounds.maxX, bounds.maxY + 0.002, bounds.minZ).tex(bounds.minZ * 16, bounds.maxX * 16).endVertex() - r.pos(bounds.minX, bounds.maxY + 0.002, bounds.minZ).tex(bounds.minZ * 16, bounds.minX * 16).endVertex() - r.pos(bounds.minX, bounds.maxY + 0.002, bounds.maxZ).tex(bounds.maxZ * 16, bounds.minX * 16).endVertex() - case EnumFacing.DOWN => - r.pos(bounds.maxX, bounds.minY - 0.002, bounds.minZ).tex(bounds.minZ * 16, bounds.maxX * 16).endVertex() - r.pos(bounds.maxX, bounds.minY - 0.002, bounds.maxZ).tex(bounds.maxZ * 16, bounds.maxX * 16).endVertex() - r.pos(bounds.minX, bounds.minY - 0.002, bounds.maxZ).tex(bounds.maxZ * 16, bounds.minX * 16).endVertex() - r.pos(bounds.minX, bounds.minY - 0.002, bounds.minZ).tex(bounds.minZ * 16, bounds.minX * 16).endVertex() - case EnumFacing.EAST => - r.pos(bounds.maxX + 0.002, bounds.maxY, bounds.minZ).tex(bounds.minZ * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.maxX + 0.002, bounds.maxY, bounds.maxZ).tex(bounds.maxZ * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.maxX + 0.002, bounds.minY, bounds.maxZ).tex(bounds.maxZ * 16, bounds.minY * 16).endVertex() - r.pos(bounds.maxX + 0.002, bounds.minY, bounds.minZ).tex(bounds.minZ * 16, bounds.minY * 16).endVertex() - case EnumFacing.WEST => - r.pos(bounds.minX - 0.002, bounds.maxY, bounds.maxZ).tex(bounds.maxZ * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.minX - 0.002, bounds.maxY, bounds.minZ).tex(bounds.minZ * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.minX - 0.002, bounds.minY, bounds.minZ).tex(bounds.minZ * 16, bounds.minY * 16).endVertex() - r.pos(bounds.minX - 0.002, bounds.minY, bounds.maxZ).tex(bounds.maxZ * 16, bounds.minY * 16).endVertex() - case EnumFacing.SOUTH => - r.pos(bounds.maxX, bounds.maxY, bounds.maxZ + 0.002).tex(bounds.maxX * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.maxZ + 0.002).tex(bounds.minX * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.minX, bounds.minY, bounds.maxZ + 0.002).tex(bounds.minX * 16, bounds.minY * 16).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.maxZ + 0.002).tex(bounds.maxX * 16, bounds.minY * 16).endVertex() + case Direction.UP => + r.vertex(stack.last.pose, maxX, maxY + 0.002f, maxZ).uv(maxZ * 16, maxX * 16).endVertex() + r.vertex(stack.last.pose, maxX, maxY + 0.002f, minZ).uv(minZ * 16, maxX * 16).endVertex() + r.vertex(stack.last.pose, minX, maxY + 0.002f, minZ).uv(minZ * 16, minX * 16).endVertex() + r.vertex(stack.last.pose, minX, maxY + 0.002f, maxZ).uv(maxZ * 16, minX * 16).endVertex() + case Direction.DOWN => + r.vertex(stack.last.pose, maxX, minY - 0.002f, minZ).uv(minZ * 16, maxX * 16).endVertex() + r.vertex(stack.last.pose, maxX, minY - 0.002f, maxZ).uv(maxZ * 16, maxX * 16).endVertex() + r.vertex(stack.last.pose, minX, minY - 0.002f, maxZ).uv(maxZ * 16, minX * 16).endVertex() + r.vertex(stack.last.pose, minX, minY - 0.002f, minZ).uv(minZ * 16, minX * 16).endVertex() + case Direction.EAST => + r.vertex(stack.last.pose, maxX + 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, minY, minZ).uv(minZ * 16, minY * 16).endVertex() + case Direction.WEST => + r.vertex(stack.last.pose, minX - 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, minY, minZ).uv(minZ * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).endVertex() + case Direction.SOUTH => + r.vertex(stack.last.pose, maxX, maxY, maxZ + 0.002f).uv(maxX * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, minX, maxY, maxZ + 0.002f).uv(minX * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, minX, minY, maxZ + 0.002f).uv(minX * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, maxX, minY, maxZ + 0.002f).uv(maxX * 16, minY * 16).endVertex() case _ => - r.pos(bounds.minX, bounds.maxY, bounds.minZ - 0.002).tex(bounds.minX * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.maxX, bounds.maxY, bounds.minZ - 0.002).tex(bounds.maxX * 16, bounds.maxY * 16).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.minZ - 0.002).tex(bounds.maxX * 16, bounds.minY * 16).endVertex() - r.pos(bounds.minX, bounds.minY, bounds.minZ - 0.002).tex(bounds.minX * 16, bounds.minY * 16).endVertex() + r.vertex(stack.last.pose, minX, maxY, minZ - 0.002f).uv(minX * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, maxX, maxY, minZ - 0.002f).uv(maxX * 16, maxY * 16).endVertex() + r.vertex(stack.last.pose, maxX, minY, minZ - 0.002f).uv(maxX * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, minX, minY, minZ - 0.002f).uv(minX * 16, minY * 16).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.popAttrib() - GlStateManager.popMatrix() + stack.popPose() } } - if (hitInfo.typeOfHit == RayTraceResult.Type.BLOCK) e.getPlayer.getEntityWorld.getTileEntity(hitInfo.getBlockPos) match { + Minecraft.getInstance.level.getBlockEntity(hitInfo.getBlockPos) match { case print: common.tileentity.Print if print.shapes.nonEmpty => - val pos = new Vec3d( - e.getPlayer.prevPosX + (e.getPlayer.posX - e.getPlayer.prevPosX) * e.getPartialTicks, - e.getPlayer.prevPosY + (e.getPlayer.posY - e.getPlayer.prevPosY) * e.getPartialTicks, - e.getPlayer.prevPosZ + (e.getPlayer.posZ - e.getPlayer.prevPosZ) * e.getPartialTicks) val expansion = 0.002f - // See RenderGlobal.drawSelectionBox. - GlStateManager.enableBlend() - OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) - GlStateManager.color(0, 0, 0, 0.4f) - GlStateManager.glLineWidth(2) - GlStateManager.disableTexture2D() - GlStateManager.depthMask(false) + // See WorldRenderer.renderHitOutline. + RenderSystem.enableBlend() + RenderSystem.blendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) + RenderSystem.color4f(0, 0, 0, 0.4f) + RenderSystem.lineWidth(2) + RenderSystem.disableTexture() + RenderSystem.depthMask(false) + val tesselator = Tessellator.getInstance + val buffer = tesselator.getBuilder() + buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION) for (shape <- print.shapes) { val bounds = shape.bounds.rotateTowards(print.facing) - RenderGlobal.drawSelectionBoundingBox(bounds.grow(expansion, expansion, expansion) - .offset(blockPos.x, blockPos.y, blockPos.z) - .offset(-pos.x, -pos.y, -pos.z), 0, 0, 0, 0x66/0xFFf.toFloat) + WorldRenderer.renderLineBox(stack, buffer, bounds.inflate(expansion, expansion, expansion) + .move(blockPos.x, blockPos.y, blockPos.z), 0, 0, 0, 0x66/0xFFf.toFloat) } + tesselator.end() - GlStateManager.depthMask(true) - GlStateManager.enableTexture2D() - GlStateManager.disableBlend() + RenderSystem.depthMask(true) + RenderSystem.enableTexture() + RenderSystem.disableBlend() e.setCanceled(true) case cable: common.tileentity.Cable => - // See RenderGlobal.drawSelectionBox. - GlStateManager.enableBlend() - OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) - GlStateManager.color(0, 0, 0, 0.4f) - GlStateManager.glLineWidth(2) - GlStateManager.disableTexture2D() - GlStateManager.depthMask(false) - GlStateManager.pushMatrix() - - val player = e.getPlayer - GlStateManager.translate( - blockPos.x - (player.lastTickPosX + (player.posX - player.lastTickPosX) * e.getPartialTicks), - blockPos.y - (player.lastTickPosY + (player.posY - player.lastTickPosY) * e.getPartialTicks), - blockPos.z - (player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * e.getPartialTicks) - ) + // See WorldRenderer.renderHitOutline. + RenderSystem.enableBlend() + RenderSystem.blendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) + RenderSystem.color4f(0, 0, 0, 0.4f) + RenderSystem.lineWidth(2) + RenderSystem.disableTexture() + RenderSystem.depthMask(false) + stack.pushPose() + + stack.translate(blockPos.x, blockPos.y, blockPos.z) val mask = common.block.Cable.neighbors(world, hitInfo.getBlockPos) val tesselator = Tessellator.getInstance - val buffer = tesselator.getBuffer + val buffer = tesselator.getBuilder() buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION) - Cable.drawOverlay(buffer, mask) - tesselator.draw() + Cable.drawOverlay(stack, buffer, mask) + tesselator.end() - GlStateManager.popMatrix() - GlStateManager.depthMask(true) - GlStateManager.enableTexture2D() - GlStateManager.disableBlend() + stack.popPose() + RenderSystem.depthMask(true) + RenderSystem.enableTexture() + RenderSystem.disableBlend() e.setCanceled(true) case _ => @@ -162,83 +160,83 @@ object HighlightRenderer { private object Cable { private final val EXPAND = 0.002f - private final val MIN = common.block.Cable.MIN - EXPAND - private final val MAX = common.block.Cable.MAX + EXPAND + private final val MIN = (common.block.Cable.MIN - EXPAND).toFloat + private final val MAX = (common.block.Cable.MAX + EXPAND).toFloat - def drawOverlay(buffer: BufferBuilder, mask: Int): Unit = { + def drawOverlay(stack: MatrixStack, buffer: BufferBuilder, mask: Int): Unit = { // Draw the cable arms - for (side <- EnumFacing.values) { - if (((1 << side.getIndex) & mask) != 0) { - val offset = if (side.getAxisDirection == EnumFacing.AxisDirection.NEGATIVE) -EXPAND else 1 + EXPAND - val centre = if (side.getAxisDirection == EnumFacing.AxisDirection.NEGATIVE) MIN else MAX + for (side <- Direction.values) { + if (((1 << side.get3DDataValue) & mask) != 0) { + val offset = if (side.getAxisDirection == Direction.AxisDirection.NEGATIVE) -EXPAND else 1 + EXPAND + val centre = if (side.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX // Draw the arm end quad - drawLineAdjacent(buffer, side.getAxis, offset, MIN, MIN, MIN, MAX) - drawLineAdjacent(buffer, side.getAxis, offset, MIN, MAX, MAX, MAX) - drawLineAdjacent(buffer, side.getAxis, offset, MAX, MAX, MAX, MIN) - drawLineAdjacent(buffer, side.getAxis, offset, MAX, MIN, MIN, MIN) + drawLineAdjacent(stack, buffer, side.getAxis, offset, MIN, MIN, MIN, MAX) + drawLineAdjacent(stack, buffer, side.getAxis, offset, MIN, MAX, MAX, MAX) + drawLineAdjacent(stack, buffer, side.getAxis, offset, MAX, MAX, MAX, MIN) + drawLineAdjacent(stack, buffer, side.getAxis, offset, MAX, MIN, MIN, MIN) // Draw the connecting lines to the middle - drawLineAlong(buffer, side.getAxis, MIN, MIN, offset, centre) - drawLineAlong(buffer, side.getAxis, MAX, MIN, offset, centre) - drawLineAlong(buffer, side.getAxis, MAX, MAX, offset, centre) - drawLineAlong(buffer, side.getAxis, MIN, MAX, offset, centre) + drawLineAlong(stack, buffer, side.getAxis, MIN, MIN, offset, centre) + drawLineAlong(stack, buffer, side.getAxis, MAX, MIN, offset, centre) + drawLineAlong(stack, buffer, side.getAxis, MAX, MAX, offset, centre) + drawLineAlong(stack, buffer, side.getAxis, MIN, MAX, offset, centre) } } // Draw the cable core - drawCore(buffer, mask, EnumFacing.WEST, EnumFacing.DOWN, EnumFacing.Axis.Z) - drawCore(buffer, mask, EnumFacing.WEST, EnumFacing.UP, EnumFacing.Axis.Z) - drawCore(buffer, mask, EnumFacing.EAST, EnumFacing.DOWN, EnumFacing.Axis.Z) - drawCore(buffer, mask, EnumFacing.EAST, EnumFacing.UP, EnumFacing.Axis.Z) - - drawCore(buffer, mask, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.Axis.Y) - drawCore(buffer, mask, EnumFacing.WEST, EnumFacing.SOUTH, EnumFacing.Axis.Y) - drawCore(buffer, mask, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.Axis.Y) - drawCore(buffer, mask, EnumFacing.EAST, EnumFacing.SOUTH, EnumFacing.Axis.Y) - - drawCore(buffer, mask, EnumFacing.DOWN, EnumFacing.NORTH, EnumFacing.Axis.X) - drawCore(buffer, mask, EnumFacing.DOWN, EnumFacing.SOUTH, EnumFacing.Axis.X) - drawCore(buffer, mask, EnumFacing.UP, EnumFacing.NORTH, EnumFacing.Axis.X) - drawCore(buffer, mask, EnumFacing.UP, EnumFacing.SOUTH, EnumFacing.Axis.X) + drawCore(stack, buffer, mask, Direction.WEST, Direction.DOWN, Direction.Axis.Z) + drawCore(stack, buffer, mask, Direction.WEST, Direction.UP, Direction.Axis.Z) + drawCore(stack, buffer, mask, Direction.EAST, Direction.DOWN, Direction.Axis.Z) + drawCore(stack, buffer, mask, Direction.EAST, Direction.UP, Direction.Axis.Z) + + drawCore(stack, buffer, mask, Direction.WEST, Direction.NORTH, Direction.Axis.Y) + drawCore(stack, buffer, mask, Direction.WEST, Direction.SOUTH, Direction.Axis.Y) + drawCore(stack, buffer, mask, Direction.EAST, Direction.NORTH, Direction.Axis.Y) + drawCore(stack, buffer, mask, Direction.EAST, Direction.SOUTH, Direction.Axis.Y) + + drawCore(stack, buffer, mask, Direction.DOWN, Direction.NORTH, Direction.Axis.X) + drawCore(stack, buffer, mask, Direction.DOWN, Direction.SOUTH, Direction.Axis.X) + drawCore(stack, buffer, mask, Direction.UP, Direction.NORTH, Direction.Axis.X) + drawCore(stack, buffer, mask, Direction.UP, Direction.SOUTH, Direction.Axis.X) } /** Draw part of the core object */ - private def drawCore(buffer: BufferBuilder, mask: Int, a: EnumFacing, b: EnumFacing, other: EnumFacing.Axis): Unit = { + private def drawCore(stack: MatrixStack, buffer: BufferBuilder, mask: Int, a: Direction, b: Direction, other: Direction.Axis): Unit = { if (((mask >> a.ordinal) & 1) != ((mask >> b.ordinal) & 1)) return - val offA = if (a.getAxisDirection == EnumFacing.AxisDirection.NEGATIVE) MIN else MAX - val offB = if (b.getAxisDirection == EnumFacing.AxisDirection.NEGATIVE) MIN else MAX - drawLineAlong(buffer, other, offA, offB, MIN, MAX) + val offA = if (a.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX + val offB = if (b.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX + drawLineAlong(stack, buffer, other, offA, offB, MIN, MAX) } /** Draw a line parallel to an axis */ - private def drawLineAlong(buffer: BufferBuilder, axis: EnumFacing.Axis, offA: Double, offB: Double, start: Double, end: Double): Unit = { + private def drawLineAlong(stack: MatrixStack, buffer: BufferBuilder, axis: Direction.Axis, offA: Float, offB: Float, start: Float, end: Float): Unit = { axis match { - case EnumFacing.Axis.X => - buffer.pos(start, offA, offB).endVertex() - buffer.pos(end, offA, offB).endVertex() - case EnumFacing.Axis.Y => - buffer.pos(offA, start, offB).endVertex() - buffer.pos(offA, end, offB).endVertex() - case EnumFacing.Axis.Z => - buffer.pos(offA, offB, start).endVertex() - buffer.pos(offA, offB, end).endVertex() + case Direction.Axis.X => + buffer.vertex(stack.last.pose, start, offA, offB).endVertex() + buffer.vertex(stack.last.pose, end, offA, offB).endVertex() + case Direction.Axis.Y => + buffer.vertex(stack.last.pose, offA, start, offB).endVertex() + buffer.vertex(stack.last.pose, offA, end, offB).endVertex() + case Direction.Axis.Z => + buffer.vertex(stack.last.pose, offA, offB, start).endVertex() + buffer.vertex(stack.last.pose, offA, offB, end).endVertex() } } /** Draw a line perpendicular to an axis */ - private def drawLineAdjacent(buffer: BufferBuilder, axis: EnumFacing.Axis, offset: Double, startA: Double, startB: Double, endA: Double, endB: Double): Unit = { + private def drawLineAdjacent(stack: MatrixStack, buffer: BufferBuilder, axis: Direction.Axis, offset: Float, startA: Float, startB: Float, endA: Float, endB: Float): Unit = { axis match { - case EnumFacing.Axis.X => - buffer.pos(offset, startA, startB).endVertex() - buffer.pos(offset, endA, endB).endVertex() - case EnumFacing.Axis.Y => - buffer.pos(startA, offset, startB).endVertex() - buffer.pos(endA, offset, endB).endVertex() - case EnumFacing.Axis.Z => - buffer.pos(startA, startB, offset).endVertex() - buffer.pos(endA, endB, offset).endVertex() + case Direction.Axis.X => + buffer.vertex(stack.last.pose, offset, startA, startB).endVertex() + buffer.vertex(stack.last.pose, offset, endA, endB).endVertex() + case Direction.Axis.Y => + buffer.vertex(stack.last.pose, startA, offset, startB).endVertex() + buffer.vertex(stack.last.pose, endA, offset, endB).endVertex() + case Direction.Axis.Z => + buffer.vertex(stack.last.pose, startA, startB, offset).endVertex() + buffer.vertex(stack.last.pose, endA, endB, offset).endVertex() } } } diff --git a/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala index 91850577b5..b861b05069 100644 --- a/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala @@ -7,9 +7,13 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.item.ItemStack +import net.minecraft.util.Hand +import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.vector.Vector4f +import net.minecraft.util.math.vector.Matrix4f import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.opengl.GL11 object MFUTargetRenderer { @@ -18,28 +22,30 @@ object MFUTargetRenderer { @SubscribeEvent def onRenderWorldLastEvent(e: RenderWorldLastEvent) { - val mc = Minecraft.getMinecraft + val mc = Minecraft.getInstance val player = mc.player if (player == null) return - player.getHeldItemMainhand match { - case stack: ItemStack if api.Items.get(stack) == mfu && stack.hasTagCompound => - val data = stack.getTagCompound - if (data.hasKey(Settings.namespace + "coord", NBT.TAG_INT_ARRAY)) { - val Array(x, y, z, dimension, side) = data.getIntArray(Settings.namespace + "coord") - if (player.getEntityWorld.provider.getDimension != dimension) return - if (player.getDistance(x, y, z) > 64) return + player.getItemInHand(Hand.MAIN_HAND) match { + case stack: ItemStack if api.Items.get(stack) == mfu && stack.hasTag => + val data = stack.getTag + if (data.contains(Settings.namespace + "coord", NBT.TAG_INT_ARRAY)) { + val dimension = new ResourceLocation(data.getString(Settings.namespace + "dimension")) + if (!player.level.dimension.location.equals(dimension)) return + val Array(x, y, z, side) = data.getIntArray(Settings.namespace + "coord") + if (player.distanceToSqr(x, y, z) > 64 * 64) return - val bounds = BlockPosition(x, y, z).bounds.grow(0.1, 0.1, 0.1) + val bounds = BlockPosition(x, y, z).bounds.inflate(0.1, 0.1, 0.1) - val px = player.lastTickPosX + (player.posX - player.lastTickPosX) * e.getPartialTicks - val py = player.lastTickPosY + (player.posY - player.lastTickPosY) * e.getPartialTicks - val pz = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * e.getPartialTicks + val px = player.xOld + (player.getX - player.xOld) * e.getPartialTicks + val py = player.yOld + (player.getY - player.yOld) * e.getPartialTicks + val pz = player.zOld + (player.getZ - player.zOld) * e.getPartialTicks RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: entering (aka: wasntme)") GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) - GL11.glPushMatrix() - GL11.glTranslated(-px, -py, -pz) + val matrix = e.getMatrixStack + matrix.pushPose() + matrix.translate(-px, -py, -pz) RenderState.makeItBlend() GL11.glDisable(GL11.GL_LIGHTING) GL11.glDisable(GL11.GL_TEXTURE_2D) @@ -52,11 +58,11 @@ object MFUTargetRenderer { ((color >> 0) & 0xFF) / 255f, 0.25f) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE) - drawBox(bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ) + drawBox(matrix.last.pose, new Vector4f(), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL) - drawFace(bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ, side) + drawFace(matrix.last.pose, new Vector4f(), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat, side) - GL11.glPopMatrix() + matrix.popPose() GL11.glPopAttrib() RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: leaving") @@ -65,88 +71,94 @@ object MFUTargetRenderer { } } - private def drawBox(minX: Double, minY: Double, minZ: Double, maxX: Double, maxY: Double, maxZ: Double) { + def glVertex(matrix: Matrix4f, temp: Vector4f, x: Float, y: Float, z: Float) { + temp.set(x, y, z, 1) + temp.transform(matrix) + GL11.glVertex3f(temp.x, temp.y, temp.z) + } + + def drawBox(matrix: Matrix4f, temp: Vector4f, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float) { GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, minY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, maxY, minZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, maxY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, minZ) GL11.glEnd() } - private def drawFace(minX: Double, minY: Double, minZ: Double, maxX: Double, maxY: Double, maxZ: Double, side: Int): Unit = { + private def drawFace(matrix: Matrix4f, temp: Vector4f, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float, side: Int): Unit = { side match { case 0 => // Down GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, minY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, minZ) GL11.glEnd() case 1 => // Up GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() case 2 => // North GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() case 3 => // South GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) GL11.glEnd() case 4 => // East GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, maxY, minZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) GL11.glEnd() case 5 => // West GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, maxY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, minZ) GL11.glEnd() case _ => // WTF? } diff --git a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala index fb1d5b2eef..2e180e3ba4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala @@ -4,16 +4,20 @@ import java.util.concurrent.Callable import java.util.concurrent.TimeUnit import com.google.common.cache.CacheBuilder +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api.event.RobotRenderEvent import li.cil.oc.client.renderer.tileentity.RobotRenderer import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.entity.Entity +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.math.vector.Vector3f import net.minecraftforge.client.event.RenderPlayerEvent -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import net.minecraftforge.eventbus.api.EventPriority +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.event.TickEvent.ClientTickEvent import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -47,45 +51,48 @@ object PetRenderer { @SubscribeEvent def onPlayerRender(e: RenderPlayerEvent.Pre) { - val uuid = e.getEntityPlayer.getUniqueID.toString + val uuid = e.getPlayer.getUUID.toString if (hidden.contains(uuid) || !entitledPlayers.contains(uuid)) return rendering = Some(entitledPlayers(uuid)) - val worldTime = e.getEntityPlayer.getEntityWorld.getTotalWorldTime - val timeJitter = e.getEntityPlayer.hashCode ^ 0xFF + val worldTime = e.getPlayer.level.getGameTime + val timeJitter = e.getPlayer.hashCode ^ 0xFF val offset = timeJitter + worldTime / 20.0 val hover = (math.sin(timeJitter + (worldTime + e.getPartialRenderTick) / 20.0) * 0.03).toFloat - val location = petLocations.get(e.getEntityPlayer, new Callable[PetLocation] { - override def call() = new PetLocation(e.getEntityPlayer) + val location = petLocations.get(e.getPlayer, new Callable[PetLocation] { + override def call() = new PetLocation(e.getPlayer) }) - GlStateManager.pushMatrix() + val stack = e.getMatrixStack + stack.pushPose() RenderState.pushAttrib() - val localPos = Minecraft.getMinecraft.player.getPositionEyes(e.getPartialRenderTick) - val playerPos = e.getEntityPlayer.getPositionEyes(e.getPartialRenderTick) - val correction = 1.62 - (if (e.getEntityPlayer.isSneaking) 0.125 else 0) - GlStateManager.translate( - playerPos.x - localPos.x, - playerPos.y - localPos.y + correction, - playerPos.z - localPos.z) + val self = Minecraft.getInstance.player + val lx = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick + val ly = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick + self.getEyeHeight(self.getPose) + val lz = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick + val other = e.getPlayer + val px = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick + val py = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick + other.getEyeHeight(other.getPose) + val pz = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick + stack.translate(px - lx, py - ly, pz - lz) RenderState.enableEntityLighting() - GlStateManager.disableBlend() - GlStateManager.enableRescaleNormal() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.disableBlend() + RenderSystem.enableRescaleNormal() + RenderSystem.color4f(1, 1, 1, 1) - location.applyInterpolatedTransformations(e.getPartialRenderTick) + location.applyInterpolatedTransformations(stack, e.getPartialRenderTick) - GlStateManager.scale(0.3f, 0.3f, 0.3f) - GlStateManager.translate(0, hover, 0) + stack.scale(0.3f, 0.3f, 0.3f) + stack.translate(0, hover, 0) - RobotRenderer.renderChassis(null, offset, isRunningOverride = true) + RobotRenderer.renderChassis(stack, offset, isRunningOverride = true) - GlStateManager.disableRescaleNormal() + RenderSystem.disableRescaleNormal() RenderState.popAttrib() - GlStateManager.popMatrix() + stack.popPose() rendering = None } @@ -93,7 +100,7 @@ object PetRenderer { @SubscribeEvent(priority = EventPriority.LOWEST) def onRobotRender(e: RobotRenderEvent) { rendering match { - case Some((r, g, b)) => GlStateManager.color(r.toFloat, g.toFloat, b.toFloat) + case Some((r, g, b)) => RenderSystem.color3f(r.toFloat, g.toFloat, b.toFloat) case _ => } } @@ -102,7 +109,7 @@ object PetRenderer { var x = 0.0 var y = 0.0 var z = 0.0 - var yaw = owner.rotationYaw + var yaw = owner.yRot var lastX = x var lastY = y @@ -110,10 +117,10 @@ object PetRenderer { var lastYaw = yaw def update() { - val dx = owner.lastTickPosX - owner.posX - val dy = owner.lastTickPosY - owner.posY - val dz = owner.lastTickPosZ - owner.posZ - val dYaw = owner.rotationYaw - yaw + val dx = owner.xOld - owner.getX + val dy = owner.yOld - owner.getY + val dz = owner.zOld - owner.getZ + val dYaw = owner.yRot - yaw lastX = x lastY = y lastZ = z @@ -127,23 +134,23 @@ object PetRenderer { yaw += dYaw * 0.2f } - def applyInterpolatedTransformations(dt: Float) { + def applyInterpolatedTransformations(stack: MatrixStack, dt: Float) { val ix = lastX + (x - lastX) * dt val iy = lastY + (y - lastY) * dt val iz = lastZ + (z - lastZ) * dt val iYaw = lastYaw + (yaw - lastYaw) * dt - GlStateManager.translate(ix, iy, iz) + stack.translate(ix, iy, iz) if (!isForInventory) { - GlStateManager.rotate(-iYaw, 0, 1, 0) + stack.mulPose(Vector3f.YP.rotationDegrees(-iYaw)) } else { - GlStateManager.rotate(-owner.rotationYaw, 0, 1, 0) + stack.mulPose(Vector3f.YP.rotationDegrees(-owner.yRot)) } - GlStateManager.translate(0.3, -0.1, -0.2) + stack.translate(0.3, -0.1, -0.2) } - private def isForInventory = Minecraft.getMinecraft.currentScreen != null && owner == Minecraft.getMinecraft.player + private def isForInventory = Minecraft.getInstance.screen != null && owner == Minecraft.getInstance.player } @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala index e025e22283..54f44434db 100644 --- a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala +++ b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala @@ -6,14 +6,14 @@ import java.util.concurrent.TimeUnit import com.google.common.cache.CacheBuilder import com.google.common.cache.RemovalListener import com.google.common.cache.RemovalNotification +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.client.renderer.font.TextBufferRenderData import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GLAllocation -import net.minecraft.client.renderer.GlStateManager import net.minecraft.tileentity.TileEntity -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import net.minecraftforge.event.TickEvent.ClientTickEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.opengl.GL11 object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEntity, Int] { @@ -34,12 +34,12 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti // Rendering // ----------------------------------------------------------------------- // - def render(buffer: TextBufferRenderData) { + def render(stack: MatrixStack, buffer: TextBufferRenderData) { currentBuffer = buffer - compileOrDraw(cache.get(currentBuffer, this)) + compileOrDraw(stack, cache.get(currentBuffer, this)) } - private def compileOrDraw(list: Int) = { + private def compileOrDraw(stack: MatrixStack, list: Int) = { if (currentBuffer.dirty) { RenderState.checkError(getClass.getName + ".compileOrDraw: entering (aka: wasntme)") @@ -55,7 +55,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti RenderState.checkError(getClass.getName + ".compileOrDraw: glNewList") } - renderer.drawBuffer(currentBuffer.data, currentBuffer.viewport._1, currentBuffer.viewport._2) + renderer.drawBuffer(stack, currentBuffer.data, currentBuffer.viewport._1, currentBuffer.viewport._2) RenderState.checkError(getClass.getName + ".compileOrDraw: drawString") @@ -71,11 +71,11 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti } else { GL11.glCallList(list) - GlStateManager.enableTexture2D() - GlStateManager.depthMask(true) - GlStateManager.color(1, 1, 1, 1) + RenderSystem.enableTexture() + RenderSystem.depthMask(true) + RenderSystem.color4f(1, 1, 1, 1) - // Because display lists and the GlStateManager don't like each other, apparently. + // Because display lists and the RenderSystem don't like each other, apparently. GL11.glEnable(GL11.GL_TEXTURE_2D) RenderState.bindTexture(0) GL11.glDepthMask(true) @@ -94,7 +94,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti def call = { RenderState.checkError(getClass.getName + ".call: entering (aka: wasntme)") - val list = GLAllocation.generateDisplayLists(1) + val list = GL11.glGenLists(1) currentBuffer.dirty = true // Force compilation. RenderState.checkError(getClass.getName + ".call: leaving") @@ -105,7 +105,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti def onRemoval(e: RemovalNotification[TileEntity, Int]) { RenderState.checkError(getClass.getName + ".onRemoval: entering (aka: wasntme)") - GLAllocation.deleteDisplayLists(e.getValue) + GL11.glDeleteLists(e.getValue, 1) RenderState.checkError(getClass.getName + ".onRemoval: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala index a0968c505e..4b411b080e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala @@ -1,14 +1,16 @@ package li.cil.oc.client.renderer +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.server.network.WirelessNetwork import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.math.vector.Vector4f +import net.minecraft.util.math.vector.Matrix4f import net.minecraft.world.World import net.minecraftforge.client.event.RenderWorldLastEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.common.ObfuscationReflectionHelper -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import org.lwjgl.opengl.GL11 object WirelessNetworkDebugRenderer { @@ -19,63 +21,70 @@ object WirelessNetworkDebugRenderer { if (Settings.rTreeDebugRenderer) { RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: entering (aka: wasntme)") - val world = ObfuscationReflectionHelper.getPrivateValue(classOf[net.minecraft.client.renderer.RenderGlobal], e.getContext, "theWorld", "field_72769_h", "r").asInstanceOf[World] - WirelessNetwork.dimensions.get(world.provider.getDimension) match { + val world = Minecraft.getInstance.level + WirelessNetwork.dimensions.get(world.dimension) match { case Some(tree) => - val mc = Minecraft.getMinecraft - val player = mc.player - val px = player.lastTickPosX + (player.posX - player.lastTickPosX) * e.getPartialTicks - val py = player.lastTickPosY + (player.posY - player.lastTickPosY) * e.getPartialTicks - val pz = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * e.getPartialTicks + val player = Minecraft.getInstance.player + val px = player.xOld + (player.getX - player.xOld) * e.getPartialTicks + val py = player.yOld + (player.getY - player.yOld) * e.getPartialTicks + val pz = player.zOld + (player.getZ - player.zOld) * e.getPartialTicks + val stack = e.getMatrixStack RenderState.pushAttrib() - GlStateManager.pushMatrix() - GL11.glTranslated(-px, -py, -pz) + stack.pushPose() + stack.translate(-px, -py, -pz) RenderState.makeItBlend() GL11.glDisable(GL11.GL_LIGHTING) GL11.glDisable(GL11.GL_TEXTURE_2D) GL11.glDisable(GL11.GL_DEPTH_TEST) GL11.glDisable(GL11.GL_CULL_FACE) - def drawBox(minX: Double, minY: Double, minZ: Double, maxX: Double, maxY: Double, maxZ: Double) { + def glVertex(matrix: Matrix4f, temp: Vector4f, x: Float, y: Float, z: Float) { + temp.set(x, y, z, 1) + temp.transform(matrix) + GL11.glVertex3f(temp.x, temp.y, temp.z) + } + + def drawBox(matrix: Matrix4f, temp: Vector4f, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float) { GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, minY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, minY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, minZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, minZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, maxY, minZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) - GL11.glVertex3d(minX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) + glVertex(matrix, temp, minX, maxY, maxZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(minX, minY, minZ) - GL11.glVertex3d(minX, maxY, minZ) - GL11.glVertex3d(minX, maxY, maxZ) - GL11.glVertex3d(minX, minY, maxZ) + glVertex(matrix, temp, minX, minY, minZ) + glVertex(matrix, temp, minX, maxY, minZ) + glVertex(matrix, temp, minX, maxY, maxZ) + glVertex(matrix, temp, minX, minY, maxZ) GL11.glEnd() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex3d(maxX, minY, minZ) - GL11.glVertex3d(maxX, minY, maxZ) - GL11.glVertex3d(maxX, maxY, maxZ) - GL11.glVertex3d(maxX, maxY, minZ) + glVertex(matrix, temp, maxX, minY, minZ) + glVertex(matrix, temp, maxX, minY, maxZ) + glVertex(matrix, temp, maxX, maxY, maxZ) + glVertex(matrix, temp, maxX, maxY, minZ) GL11.glEnd() } + val temp = new Vector4f() GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE) for (((min, max), level) <- tree.allBounds) { val (minX, minY, minZ) = min @@ -86,13 +95,13 @@ object WirelessNetworkDebugRenderer { ((color >> 8) & 0xFF) / 255f, ((color >> 0) & 0xFF) / 255f, 0.25f) - val size = 0.5 - level * 0.05 - drawBox(minX - size, minY - size, minZ - size, maxX + size, maxY + size, maxZ + size) + val size = 0.5f - level * 0.05f + drawBox(stack.last.pose, temp, minX.toFloat - size, minY.toFloat - size, minZ.toFloat - size, maxX.toFloat + size, maxY.toFloat + size, maxZ.toFloat + size) } GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL) RenderState.popAttrib() - GlStateManager.popMatrix() + stack.popPose() case _ => } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala index a84053bd0d..03a4e9d0c5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala @@ -11,17 +11,17 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ItemColorizer -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase -import net.minecraft.item.EnumDyeColor +import net.minecraft.block.BlockState +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.LivingEntity +import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d -import net.minecraft.world.World -import net.minecraftforge.common.property.IExtendedBlockState +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d +import net.minecraftforge.client.model.data.IModelData import scala.collection.convert.WrapAsJava._ import scala.collection.mutable @@ -31,93 +31,95 @@ object CableModel extends CableModel class CableModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = - state match { - case extended: IExtendedBlockState => - val neighbors = extended.getValue(block.Cable.NeighborsProp) - val color = extended.getValue(block.Cable.ColorProp) - val isCableSide = extended.getValue(block.Cable.IsSideCableProp) - (neighbors,color,isCableSide) match { - case (neighbourMask: Integer, color: Integer, isCableOnSideMask: Integer) => - val faces = mutable.ArrayBuffer.empty[BakedQuad] - - faces ++= bakeQuads(Middle, cableTexture, color) - for (side <- EnumFacing.values) { - val connected = (neighbourMask & (1 << side.getIndex)) != 0 - val isCableOnSide = (isCableOnSideMask & (1 << side.getIndex)) != 0 - val (plug, shortBody, longBody) = Connected(side.getIndex) - if (connected) { - if (isCableOnSide) { - faces ++= bakeQuads(longBody, cableTexture, color) - } - else { - faces ++= bakeQuads(shortBody, cableTexture, color) - faces ++= bakeQuads(plug, cableCapTexture, None) - } - } - else if (((1 << side.getOpposite.getIndex) & neighbourMask) == neighbourMask || neighbourMask == 0) { - faces ++= bakeQuads(Disconnected(side.getIndex), cableCapTexture, None) - } - } + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = + data match { + case cable: tileentity.Cable => + val world = cable.getLevel + val neighbors = block.Cable.neighbors(world, cable.getBlockPos) + val color = cable.getColor + var isCableSide = 0 + for (side <- Direction.values) { + if (world.getBlockEntity(cable.getBlockPos.relative(side)).isInstanceOf[tileentity.Cable]){ + isCableSide = block.Cable.mask(side, isCableSide) + } + } + val faces = mutable.ArrayBuffer.empty[BakedQuad] - bufferAsJavaList(faces) - case _ => super.getQuads(state, side, rand) + faces ++= bakeQuads(Middle, cableTexture, color) + for (side <- Direction.values) { + val connected = (neighbors & (1 << side.get3DDataValue)) != 0 + val isCableOnSide = (isCableSide & (1 << side.get3DDataValue)) != 0 + val (plug, shortBody, longBody) = Connected(side.get3DDataValue) + if (connected) { + if (isCableOnSide) { + faces ++= bakeQuads(longBody, cableTexture, color) + } + else { + faces ++= bakeQuads(shortBody, cableTexture, color) + faces ++= bakeQuads(plug, cableCapTexture, None) + } + } + else if (((1 << side.getOpposite.get3DDataValue) & neighbors) == neighbors || neighbors == 0) { + faces ++= bakeQuads(Disconnected(side.get3DDataValue), cableCapTexture, None) + } } + + bufferAsJavaList(faces) case _ => super.getQuads(state, side, rand) } protected def isCable(pos: BlockPosition) = { pos.world match { case Some(world) => - world.getTileEntity(pos).isInstanceOf[tileentity.Cable] + world.getBlockEntity(pos).isInstanceOf[tileentity.Cable] case _ => false } } - protected final val Middle = makeBox(new Vec3d(6 / 16f, 6 / 16f, 6 / 16f), new Vec3d(10 / 16f, 10 / 16f, 10 / 16f)) + protected final val Middle = makeBox(new Vector3d(6 / 16f, 6 / 16f, 6 / 16f), new Vector3d(10 / 16f, 10 / 16f, 10 / 16f)) // Per side, always plug + short cable + long cable (no plug). protected final val Connected = Array( - (makeBox(new Vec3d(5 / 16f, 0 / 16f, 5 / 16f), new Vec3d(11 / 16f, 1 / 16f, 11 / 16f)), - makeBox(new Vec3d(6 / 16f, 1 / 16f, 6 / 16f), new Vec3d(10 / 16f, 6 / 16f, 10 / 16f)), - makeBox(new Vec3d(6 / 16f, 0 / 16f, 6 / 16f), new Vec3d(10 / 16f, 6 / 16f, 10 / 16f))), - (makeBox(new Vec3d(5 / 16f, 15 / 16f, 5 / 16f), new Vec3d(11 / 16f, 16 / 16f, 11 / 16f)), - makeBox(new Vec3d(6 / 16f, 10 / 16f, 6 / 16f), new Vec3d(10 / 16f, 15 / 16f, 10 / 16f)), - makeBox(new Vec3d(6 / 16f, 10 / 16f, 6 / 16f), new Vec3d(10 / 16f, 16 / 16f, 10 / 16f))), - (makeBox(new Vec3d(5 / 16f, 5 / 16f, 0 / 16f), new Vec3d(11 / 16f, 11 / 16f, 1 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 1 / 16f), new Vec3d(10 / 16f, 10 / 16f, 6 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 0 / 16f), new Vec3d(10 / 16f, 10 / 16f, 6 / 16f))), - (makeBox(new Vec3d(5 / 16f, 5 / 16f, 15 / 16f), new Vec3d(11 / 16f, 11 / 16f, 16 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 10 / 16f), new Vec3d(10 / 16f, 10 / 16f, 15 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 10 / 16f), new Vec3d(10 / 16f, 10 / 16f, 16 / 16f))), - (makeBox(new Vec3d(0 / 16f, 5 / 16f, 5 / 16f), new Vec3d(1 / 16f, 11 / 16f, 11 / 16f)), - makeBox(new Vec3d(1 / 16f, 6 / 16f, 6 / 16f), new Vec3d(6 / 16f, 10 / 16f, 10 / 16f)), - makeBox(new Vec3d(0 / 16f, 6 / 16f, 6 / 16f), new Vec3d(6 / 16f, 10 / 16f, 10 / 16f))), - (makeBox(new Vec3d(15 / 16f, 5 / 16f, 5 / 16f), new Vec3d(16 / 16f, 11 / 16f, 11 / 16f)), - makeBox(new Vec3d(10 / 16f, 6 / 16f, 6 / 16f), new Vec3d(15 / 16f, 10 / 16f, 10 / 16f)), - makeBox(new Vec3d(10 / 16f, 6 / 16f, 6 / 16f), new Vec3d(16 / 16f, 10 / 16f, 10 / 16f))) + (makeBox(new Vector3d(5 / 16f, 0 / 16f, 5 / 16f), new Vector3d(11 / 16f, 1 / 16f, 11 / 16f)), + makeBox(new Vector3d(6 / 16f, 1 / 16f, 6 / 16f), new Vector3d(10 / 16f, 6 / 16f, 10 / 16f)), + makeBox(new Vector3d(6 / 16f, 0 / 16f, 6 / 16f), new Vector3d(10 / 16f, 6 / 16f, 10 / 16f))), + (makeBox(new Vector3d(5 / 16f, 15 / 16f, 5 / 16f), new Vector3d(11 / 16f, 16 / 16f, 11 / 16f)), + makeBox(new Vector3d(6 / 16f, 10 / 16f, 6 / 16f), new Vector3d(10 / 16f, 15 / 16f, 10 / 16f)), + makeBox(new Vector3d(6 / 16f, 10 / 16f, 6 / 16f), new Vector3d(10 / 16f, 16 / 16f, 10 / 16f))), + (makeBox(new Vector3d(5 / 16f, 5 / 16f, 0 / 16f), new Vector3d(11 / 16f, 11 / 16f, 1 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 1 / 16f), new Vector3d(10 / 16f, 10 / 16f, 6 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 0 / 16f), new Vector3d(10 / 16f, 10 / 16f, 6 / 16f))), + (makeBox(new Vector3d(5 / 16f, 5 / 16f, 15 / 16f), new Vector3d(11 / 16f, 11 / 16f, 16 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 10 / 16f), new Vector3d(10 / 16f, 10 / 16f, 15 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 10 / 16f), new Vector3d(10 / 16f, 10 / 16f, 16 / 16f))), + (makeBox(new Vector3d(0 / 16f, 5 / 16f, 5 / 16f), new Vector3d(1 / 16f, 11 / 16f, 11 / 16f)), + makeBox(new Vector3d(1 / 16f, 6 / 16f, 6 / 16f), new Vector3d(6 / 16f, 10 / 16f, 10 / 16f)), + makeBox(new Vector3d(0 / 16f, 6 / 16f, 6 / 16f), new Vector3d(6 / 16f, 10 / 16f, 10 / 16f))), + (makeBox(new Vector3d(15 / 16f, 5 / 16f, 5 / 16f), new Vector3d(16 / 16f, 11 / 16f, 11 / 16f)), + makeBox(new Vector3d(10 / 16f, 6 / 16f, 6 / 16f), new Vector3d(15 / 16f, 10 / 16f, 10 / 16f)), + makeBox(new Vector3d(10 / 16f, 6 / 16f, 6 / 16f), new Vector3d(16 / 16f, 10 / 16f, 10 / 16f))) ) // Per side, cap only. protected final val Disconnected = Array( - makeBox(new Vec3d(6 / 16f, 5 / 16f, 6 / 16f), new Vec3d(10 / 16f, 6 / 16f, 10 / 16f)), - makeBox(new Vec3d(6 / 16f, 10 / 16f, 6 / 16f), new Vec3d(10 / 16f, 11 / 16f, 10 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 5 / 16f), new Vec3d(10 / 16f, 10 / 16f, 6 / 16f)), - makeBox(new Vec3d(6 / 16f, 6 / 16f, 10 / 16f), new Vec3d(10 / 16f, 10 / 16f, 11 / 16f)), - makeBox(new Vec3d(5 / 16f, 6 / 16f, 6 / 16f), new Vec3d(6 / 16f, 10 / 16f, 10 / 16f)), - makeBox(new Vec3d(10 / 16f, 6 / 16f, 6 / 16f), new Vec3d(11 / 16f, 10 / 16f, 10 / 16f)) + makeBox(new Vector3d(6 / 16f, 5 / 16f, 6 / 16f), new Vector3d(10 / 16f, 6 / 16f, 10 / 16f)), + makeBox(new Vector3d(6 / 16f, 10 / 16f, 6 / 16f), new Vector3d(10 / 16f, 11 / 16f, 10 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 5 / 16f), new Vector3d(10 / 16f, 10 / 16f, 6 / 16f)), + makeBox(new Vector3d(6 / 16f, 6 / 16f, 10 / 16f), new Vector3d(10 / 16f, 10 / 16f, 11 / 16f)), + makeBox(new Vector3d(5 / 16f, 6 / 16f, 6 / 16f), new Vector3d(6 / 16f, 10 / 16f, 10 / 16f)), + makeBox(new Vector3d(10 / 16f, 6 / 16f, 6 / 16f), new Vector3d(11 / 16f, 10 / 16f, 10 / 16f)) ) protected def cableTexture = Array.fill(6)(Textures.getSprite(Textures.Block.Cable)) protected def cableCapTexture = Array.fill(6)(Textures.getSprite(Textures.Block.CableCap)) - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { + object ItemOverride extends ItemOverrideList { class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] - val color = if (ItemColorizer.hasColor(stack)) ItemColorizer.getColor(stack) else Color.rgbValues(EnumDyeColor.SILVER) + val color = if (ItemColorizer.hasColor(stack)) ItemColorizer.getColor(stack) else Color.rgbValues(DyeColor.LIGHT_GRAY) faces ++= bakeQuads(Middle, cableTexture, Some(color)) faces ++= bakeQuads(Connected(0)._2, cableTexture, Some(color)) @@ -129,7 +131,7 @@ class CableModel extends SmartBlockModelBase { } } - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = new ItemModel(stack) + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = new ItemModel(stack) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala index 42a7f104ec..a1e12ef33b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala @@ -4,15 +4,15 @@ import java.util import java.util.Collections import li.cil.oc.client.Textures -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase +import net.minecraft.block.BlockState +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d -import net.minecraft.world.World +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d import scala.collection.convert.WrapAsJava.bufferAsJavaList import scala.collection.mutable @@ -20,7 +20,7 @@ import scala.collection.mutable object DroneModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] faces ++= Boxes.flatMap(box => bakeQuads(box, Array.fill(6)(droneTexture), None)) @@ -31,15 +31,15 @@ object DroneModel extends SmartBlockModelBase { protected def droneTexture = Textures.getSprite(Textures.Item.DroneItem) protected def Boxes = Array( - makeBox(new Vec3d(1f / 16f, 7f / 16f, 1f / 16f), new Vec3d(7f / 16f, 8f / 16f, 7f / 16f)), - makeBox(new Vec3d(1f / 16f, 7f / 16f, 9f / 16f), new Vec3d(7f / 16f, 8f / 16f, 15f / 16f)), - makeBox(new Vec3d(9f / 16f, 7f / 16f, 1f / 16f), new Vec3d(15f / 16f, 8f / 16f, 7f / 16f)), - makeBox(new Vec3d(9f / 16f, 7f / 16f, 9f / 16f), new Vec3d(15f / 16f, 8f / 16f, 15f / 16f)), - rotateBox(makeBox(new Vec3d(6f / 16f, 6f / 16f, 6f / 16f), new Vec3d(10f / 16f, 9f / 16f, 10f / 16f)), 45) + makeBox(new Vector3d(1f / 16f, 7f / 16f, 1f / 16f), new Vector3d(7f / 16f, 8f / 16f, 7f / 16f)), + makeBox(new Vector3d(1f / 16f, 7f / 16f, 9f / 16f), new Vector3d(7f / 16f, 8f / 16f, 15f / 16f)), + makeBox(new Vector3d(9f / 16f, 7f / 16f, 1f / 16f), new Vector3d(15f / 16f, 8f / 16f, 7f / 16f)), + makeBox(new Vector3d(9f / 16f, 7f / 16f, 9f / 16f), new Vector3d(15f / 16f, 8f / 16f, 15f / 16f)), + rotateBox(makeBox(new Vector3d(6f / 16f, 6f / 16f, 6f / 16f), new Vector3d(10f / 16f, 9f / 16f, 10f / 16f)), 45) ) - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = DroneModel + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = DroneModel } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index bbfd5ddc42..84ac88e8d1 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -1,27 +1,30 @@ package li.cil.oc.client.renderer.block +import java.util.Random + import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.common.item.CustomModel import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.traits.Delegate -import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.ItemMeshDefinition -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ModelBakery -import net.minecraft.client.renderer.block.model.ModelResourceLocation -import net.minecraft.client.renderer.block.statemap.StateMapperBase +import net.minecraft.client.renderer.BlockModelShapes +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.client.renderer.model.ModelResourceLocation +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.LivingEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.util.Direction +import net.minecraft.util.IItemProvider import net.minecraft.util.ResourceLocation -import net.minecraft.util.registry.RegistrySimple import net.minecraftforge.client.event.{ModelBakeEvent, ModelRegistryEvent} -import net.minecraftforge.client.model.{ModelLoader, ModelLoaderRegistry} +import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -42,6 +45,8 @@ object ModelInitialization { private val meshableItems = mutable.ArrayBuffer.empty[Item] private val itemDelegates = mutable.ArrayBuffer.empty[(String, Delegate)] private val itemDelegatesCustom = mutable.ArrayBuffer.empty[Delegate with CustomModel] + private val delegatorOverrides = mutable.Map.empty[Delegator, ItemStack => ModelResourceLocation] + private val modelRemappings = mutable.Map.empty[ModelResourceLocation, ModelResourceLocation] def preInit(): Unit = { MinecraftForge.EVENT_BUS.register(this) @@ -69,13 +74,8 @@ object ModelInitialization { } } - def registerModel(instance: Item, id: String): Unit = { - meshableItems += instance - } - - def registerModel(instance: Block, id: String): Unit = { - val item = Item.getItemFromBlock(instance) - registerModel(item, id) + def registerModel(instance: IItemProvider, id: String): Unit = { + meshableItems += instance.asItem } // ----------------------------------------------------------------------- // @@ -85,26 +85,22 @@ object ModelInitialization { val block = descriptor.block() val stack = descriptor.createItemStack(1) - ModelLoader.setCustomModelResourceLocation(stack.getItem, stack.getMetadata, itemLocation) - ModelLoader.setCustomStateMapper(block, new StateMapperBase { - override def getModelResourceLocation(state: IBlockState): ModelResourceLocation = blockLocation - }) + val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper + shaper.register(stack.getItem, itemLocation) + block.getStateDefinition.getPossibleStates.foreach { + modelRemappings += BlockModelShapes.stateToModelLocation(_) -> blockLocation + } } private def registerItems(): Unit = { - val meshDefinition = new ItemMeshDefinition { - override def getModelLocation(stack: ItemStack): ModelResourceLocation = { - Option(api.Items.get(stack)) match { - case Some(descriptor) => - val location = Settings.resourceDomain + ":" + descriptor.name() - new ModelResourceLocation(location, "inventory") - case _ => null - } - } - } - + val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper for (item <- meshableItems) { - ModelLoader.setCustomMeshDefinition(item, meshDefinition) + Option(api.Items.get(new ItemStack(item))) match { + case Some(descriptor) => + val location = Settings.resourceDomain + ":" + descriptor.name() + shaper.register(item, new ModelResourceLocation(location, "inventory")) + case _ => + } } meshableItems.clear() } @@ -112,20 +108,35 @@ object ModelInitialization { private def registerSubItems(): Unit = { for ((id, item) <- itemDelegates) { val location = Settings.resourceDomain + ":" + id - ModelLoader.setCustomModelResourceLocation(item.parent, item.itemId, new ModelResourceLocation(location, "inventory")) - ModelBakery.registerItemVariants(item.parent, new ResourceLocation(location)) + delegatorOverrides.get(item.parent) match { + case Some(func) => delegatorOverrides.put(item.parent, stack => { + if (stack.getDamageValue != item.itemId) func(stack) + else new ModelResourceLocation(location, "inventory") + }) + case _ => delegatorOverrides.put(item.parent, stack => { + if (stack.getDamageValue != item.itemId) null + else new ModelResourceLocation(location, "inventory") + }) + } } - itemDelegates.clear() } private def registerSubItemsCustom(): Unit = { for (item <- itemDelegatesCustom) { - ModelLoader.setCustomMeshDefinition(item.parent, new ItemMeshDefinition { - override def getModelLocation(stack: ItemStack): ModelResourceLocation = Delegator.subItem(stack) match { - case Some(subItem: CustomModel) => subItem.getModelLocation(stack) - case _ => null - } - }) + delegatorOverrides.get(item.parent) match { + case Some(func) => delegatorOverrides.put(item.parent, stack => { + Delegator.subItem(stack) match { + case Some(subItem: CustomModel) => subItem.getModelLocation(stack) + case _ => func(stack) + } + }) + case _ => delegatorOverrides.put(item.parent, stack => { + Delegator.subItem(stack) match { + case Some(subItem: CustomModel) => subItem.getModelLocation(stack) + case _ => null + } + }) + } item.registerModelLocations() } } @@ -134,19 +145,23 @@ object ModelInitialization { @SubscribeEvent def onModelBake(e: ModelBakeEvent): Unit = { - val registry = e.getModelRegistry.asInstanceOf[RegistrySimple[ModelResourceLocation, IBakedModel]] - - registry.putObject(CableBlockLocation, CableModel) - registry.putObject(CableItemLocation, CableModel) - registry.putObject(NetSplitterBlockLocation, NetSplitterModel) - registry.putObject(NetSplitterItemLocation, NetSplitterModel) - registry.putObject(PrintBlockLocation, PrintModel) - registry.putObject(PrintItemLocation, PrintModel) - registry.putObject(RobotBlockLocation, RobotModel) - registry.putObject(RobotItemLocation, RobotModel) - registry.putObject(RobotAfterimageBlockLocation, NullModel) - registry.putObject(RobotAfterimageItemLocation, NullModel) + val registry = e.getModelRegistry + + registry.put(CableBlockLocation, CableModel) + registry.put(CableItemLocation, CableModel) + registry.put(NetSplitterBlockLocation, NetSplitterModel) + registry.put(NetSplitterItemLocation, NetSplitterModel) + registry.put(PrintBlockLocation, PrintModel) + registry.put(PrintItemLocation, PrintModel) + registry.put(RobotBlockLocation, RobotModel) + registry.put(RobotItemLocation, RobotModel) + registry.put(RobotAfterimageBlockLocation, NullModel) + registry.put(RobotAfterimageItemLocation, NullModel) + for ((id, item) <- itemDelegates) { + val location = Settings.resourceDomain + ":" + id + ModelLoader.addSpecialModel(new ResourceLocation(location)) + } for (item <- itemDelegatesCustom) { item.bakeModels(e) } @@ -158,16 +173,56 @@ object ModelInitialization { Constants.BlockName.Rack -> (parent => new ServerRackModel(parent)) ) - registry.getKeys.collect { - case location: ModelResourceLocation => registry.getObject(location) match { - case parent: IBakedModel => - for ((name, model) <- modelOverrides) { - val pattern = s"^${Settings.resourceDomain}:$name#.*" - if (location.toString.matches(pattern)) { - registry.putObject(location, model(parent)) - } + registry.keySet.toArray.foreach { + case location: ModelResourceLocation => { + var parent = registry.get(location) + for ((name, model) <- modelOverrides) { + val pattern = s"^${Settings.resourceDomain}:$name#.*" + if (location.toString.matches(pattern)) { + parent = model(parent) + registry.put(location, parent) } - case _ => + } + modelRemappings.get(location) match { + case Some(remapped) => registry.put(remapped, parent) + case _ => + } + } + case _ => + } + + // Temporary hack to apply model overrides from registerModel calls. + for ((delegator, handler) <- delegatorOverrides) { + val originalLocation = new ModelResourceLocation(delegator.getRegistryName, "inventory") + registry.get(originalLocation) match { + case original: IBakedModel => { + val overrides = new ItemOverrideList { + override def resolve(base: IBakedModel, stack: ItemStack, world: ClientWorld, holder: LivingEntity) = + Option(handler(stack)).map(registry).getOrElse(original.getOverrides.resolve(base, stack, world, holder)) + } + val fake = new IBakedModel { + @Deprecated + override def getQuads(state: BlockState, dir: Direction, rand: Random) = original.getQuads(state, dir, rand) + + override def useAmbientOcclusion() = original.useAmbientOcclusion + + override def isGui3d() = original.isGui3d + + override def usesBlockLight() = original.usesBlockLight + + override def isCustomRenderer() = original.isCustomRenderer + + @Deprecated + override def getParticleIcon() = original.getParticleIcon + + @Deprecated + override def getTransforms() = original.getTransforms + + override def getOverrides() = overrides + } + registry.put(originalLocation, fake) + } + case null => } } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala index f446e4895a..4478b90507 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala @@ -7,18 +7,18 @@ import li.cil.oc.client.Textures import li.cil.oc.common.block import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d -import net.minecraft.world.World +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.client.event.TextureStitchEvent -import net.minecraftforge.common.property.IExtendedBlockState -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.client.model.data.IModelData +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsJava.bufferAsJavaList import scala.collection.mutable @@ -26,19 +26,15 @@ import scala.collection.mutable object NetSplitterModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = - state match { - case extended: IExtendedBlockState => - extended.getValue(block.property.PropertyTile.Tile) match { - case t: tileentity.NetSplitter => - val faces = mutable.ArrayBuffer.empty[BakedQuad] + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = + data match { + case t: tileentity.NetSplitter => + val faces = mutable.ArrayBuffer.empty[BakedQuad] - faces ++= BaseModel - addSideQuads(faces, EnumFacing.values().map(t.isSideOpen)) + faces ++= BaseModel + addSideQuads(faces, Direction.values().map(t.isSideOpen)) - bufferAsJavaList(faces) - case _ => super.getQuads(state, side, rand) - } + bufferAsJavaList(faces) case _ => super.getQuads(state, side, rand) } @@ -55,20 +51,20 @@ object NetSplitterModel extends SmartBlockModelBase { val faces = mutable.ArrayBuffer.empty[BakedQuad] // Bottom. - faces ++= bakeQuads(makeBox(new Vec3d(0 / 16f, 0 / 16f, 5 / 16f), new Vec3d(5 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(11 / 16f, 0 / 16f, 5 / 16f), new Vec3d(16 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 0 / 16f, 0 / 16f), new Vec3d(11 / 16f, 5 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 0 / 16f, 11 / 16f), new Vec3d(11 / 16f, 5 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 5 / 16f), new Vector3d(5 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 5 / 16f), new Vector3d(16 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 0 / 16f), new Vector3d(11 / 16f, 5 / 16f, 5 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 11 / 16f), new Vector3d(11 / 16f, 5 / 16f, 16 / 16f)), splitterTexture, None) // Corners. - faces ++= bakeQuads(makeBox(new Vec3d(0 / 16f, 0 / 16f, 0 / 16f), new Vec3d(5 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(11 / 16f, 0 / 16f, 0 / 16f), new Vec3d(16 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(0 / 16f, 0 / 16f, 11 / 16f), new Vec3d(5 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(11 / 16f, 0 / 16f, 11 / 16f), new Vec3d(16 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 0 / 16f), new Vector3d(5 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 0 / 16f), new Vector3d(16 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 11 / 16f), new Vector3d(5 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 11 / 16f), new Vector3d(16 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) // Top. - faces ++= bakeQuads(makeBox(new Vec3d(0 / 16f, 11 / 16f, 5 / 16f), new Vec3d(5 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(11 / 16f, 11 / 16f, 5 / 16f), new Vec3d(16 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 11 / 16f, 0 / 16f), new Vec3d(11 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 11 / 16f, 11 / 16f), new Vec3d(11 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 11 / 16f, 5 / 16f), new Vector3d(5 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 11 / 16f, 5 / 16f), new Vector3d(16 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 0 / 16f), new Vector3d(11 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 11 / 16f), new Vector3d(11 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) faces.toArray } @@ -81,42 +77,42 @@ object NetSplitterModel extends SmartBlockModelBase { } protected def addSideQuads(faces: mutable.ArrayBuffer[BakedQuad], openSides: Array[Boolean]): Unit = { - val down = openSides(EnumFacing.DOWN.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, if (down) 0 / 16f else 2 / 16f, 5 / 16f), new Vec3d(11 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) + val down = openSides(Direction.DOWN.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, if (down) 0 / 16f else 2 / 16f, 5 / 16f), new Vector3d(11 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) - val up = openSides(EnumFacing.UP.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 11 / 16f, 5 / 16f), new Vec3d(11 / 16f, if (up) 16 / 16f else 14f / 16f, 11 / 16f)), splitterTexture, None) + val up = openSides(Direction.UP.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 5 / 16f), new Vector3d(11 / 16f, if (up) 16 / 16f else 14f / 16f, 11 / 16f)), splitterTexture, None) - val north = openSides(EnumFacing.NORTH.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 5 / 16f, if (north) 0 / 16f else 2 / 16f), new Vec3d(11 / 16f, 11 / 16f, 5 / 16f)), splitterTexture, None) + val north = openSides(Direction.NORTH.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, if (north) 0 / 16f else 2 / 16f), new Vector3d(11 / 16f, 11 / 16f, 5 / 16f)), splitterTexture, None) - val south = openSides(EnumFacing.SOUTH.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(5 / 16f, 5 / 16f, 11 / 16f), new Vec3d(11 / 16f, 11 / 16f, if (south) 16 / 16f else 14 / 16f)), splitterTexture, None) + val south = openSides(Direction.SOUTH.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, 11 / 16f), new Vector3d(11 / 16f, 11 / 16f, if (south) 16 / 16f else 14 / 16f)), splitterTexture, None) - val west = openSides(EnumFacing.WEST.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(if (west) 0 / 16f else 2 / 16f, 5 / 16f, 5 / 16f), new Vec3d(5 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) + val west = openSides(Direction.WEST.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(if (west) 0 / 16f else 2 / 16f, 5 / 16f, 5 / 16f), new Vector3d(5 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) - val east = openSides(EnumFacing.EAST.ordinal()) - faces ++= bakeQuads(makeBox(new Vec3d(11 / 16f, 5 / 16f, 5 / 16f), new Vec3d(if (east) 16 / 16f else 14 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) + val east = openSides(Direction.EAST.ordinal()) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 5 / 16f, 5 / 16f), new Vector3d(if (east) 16 / 16f else 14 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) } class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { val data = new PrintData(stack) - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] Textures.Block.bind() faces ++= BaseModel - addSideQuads(faces, EnumFacing.values().map(_ => false)) + addSideQuads(faces, Direction.values().map(_ => false)) bufferAsJavaList(faces) } } - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = new ItemModel(stack) + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = new ItemModel(stack) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala index 7c8159ad1b..471e178a97 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala @@ -13,16 +13,17 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ExtendedAABB import li.cil.oc.util.ExtendedAABB._ -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase -import net.minecraft.item.EnumDyeColor +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.client.renderer.texture.MissingTextureSprite +import net.minecraft.entity.LivingEntity +import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.world.World -import net.minecraftforge.common.property.IExtendedBlockState +import net.minecraft.util.Direction +import net.minecraftforge.client.model.data.IModelData import scala.collection.convert.WrapAsJava.bufferAsJavaList import scala.collection.mutable @@ -30,35 +31,31 @@ import scala.collection.mutable object PrintModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = - state match { - case extended: IExtendedBlockState => - extended.getValue(block.property.PropertyTile.Tile) match { - case t: tileentity.Print => - val faces = mutable.ArrayBuffer.empty[BakedQuad] + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = + data match { + case t: tileentity.Print => + val faces = mutable.ArrayBuffer.empty[BakedQuad] - for (shape <- t.shapes if !Strings.isNullOrEmpty(shape.texture)) { - val bounds = shape.bounds.rotateTowards(t.facing) - val texture = resolveTexture(shape.texture) - faces ++= bakeQuads(makeBox(bounds.min, bounds.max), Array.fill(6)(texture), shape.tint.getOrElse(White)) - } - - bufferAsJavaList(faces) - case _ => super.getQuads(state, side, rand) + for (shape <- t.shapes if !Strings.isNullOrEmpty(shape.texture)) { + val bounds = shape.bounds.rotateTowards(t.facing) + val texture = resolveTexture(shape.texture) + faces ++= bakeQuads(makeBox(bounds.minVec, bounds.maxVec), Array.fill(6)(texture), shape.tint.getOrElse(White)) } + + bufferAsJavaList(faces) case _ => super.getQuads(state, side, rand) } private def resolveTexture(name: String) = { val texture = Textures.getSprite(name) - if (texture.getIconName == "missingno") Textures.getSprite("minecraft:blocks/" + name) + if (texture.equals(MissingTextureSprite.getLocation)) Textures.getSprite("minecraft:blocks/" + name) else texture } class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { val data = new PrintData(stack) - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] val shapes = @@ -69,20 +66,20 @@ object PrintModel extends SmartBlockModelBase { for (shape <- shapes) { val bounds = shape.bounds val texture = resolveTexture(shape.texture) - faces ++= bakeQuads(makeBox(bounds.min, bounds.max), Array.fill(6)(texture), shape.tint.getOrElse(White)) + faces ++= bakeQuads(makeBox(bounds.minVec, bounds.maxVec), Array.fill(6)(texture), shape.tint.getOrElse(White)) } if (shapes.isEmpty) { val bounds = ExtendedAABB.unitBounds val texture = resolveTexture(Settings.resourceDomain + ":blocks/white") - faces ++= bakeQuads(makeBox(bounds.min, bounds.max), Array.fill(6)(texture), Color.rgbValues(EnumDyeColor.LIME)) + faces ++= bakeQuads(makeBox(bounds.minVec, bounds.maxVec), Array.fill(6)(texture), Color.rgbValues(DyeColor.LIME)) } bufferAsJavaList(faces) } } - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = new ItemModel(stack) + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = new ItemModel(stack) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala index 4ec49b7587..9a42294b16 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala @@ -4,15 +4,14 @@ import java.util import java.util.Collections import li.cil.oc.client.Textures -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.EntityLivingBase +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.world.World +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava.bufferAsJavaList import scala.collection.mutable @@ -56,30 +55,30 @@ object RobotModel extends SmartBlockModelBase { (x - 0.5f) * 1.4f + 0.5f, (y - 0.5f) * 1.4f + 0.5f, (z - 0.5f) * 1.4f + 0.5f, - EnumFacing.UP, robotTexture, robotTexture.getInterpolatedU(u * 16), robotTexture.getInterpolatedV(v * 16), + Direction.UP, robotTexture, robotTexture.getU(u * 16), robotTexture.getV(v * 16), White) }.toArray } - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] - faces += new BakedQuad(quad(top, top1, top2), tint, EnumFacing.NORTH, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(top, top2, top3), tint, EnumFacing.EAST, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(top, top3, top4), tint, EnumFacing.SOUTH, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(top, top4, top1), tint, EnumFacing.WEST, robotTexture, true, DefaultVertexFormats.ITEM) + faces += new BakedQuad(quad(top, top1, top2), tint, Direction.NORTH, robotTexture, true) + faces += new BakedQuad(quad(top, top2, top3), tint, Direction.EAST, robotTexture, true) + faces += new BakedQuad(quad(top, top3, top4), tint, Direction.SOUTH, robotTexture, true) + faces += new BakedQuad(quad(top, top4, top1), tint, Direction.WEST, robotTexture, true) - faces += new BakedQuad(quad(bottom, bottom1, bottom2), tint, EnumFacing.NORTH, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(bottom, bottom2, bottom3), tint, EnumFacing.EAST, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(bottom, bottom3, bottom4), tint, EnumFacing.SOUTH, robotTexture, true, DefaultVertexFormats.ITEM) - faces += new BakedQuad(quad(bottom, bottom4, bottom1), tint, EnumFacing.WEST, robotTexture, true, DefaultVertexFormats.ITEM) + faces += new BakedQuad(quad(bottom, bottom1, bottom2), tint, Direction.NORTH, robotTexture, true) + faces += new BakedQuad(quad(bottom, bottom2, bottom3), tint, Direction.EAST, robotTexture, true) + faces += new BakedQuad(quad(bottom, bottom3, bottom4), tint, Direction.SOUTH, robotTexture, true) + faces += new BakedQuad(quad(bottom, bottom4, bottom1), tint, Direction.WEST, robotTexture, true) bufferAsJavaList(faces) } } - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = ItemModel + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = ItemModel } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala index 5069923f19..8414835514 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala @@ -10,71 +10,67 @@ import li.cil.oc.common.Tier import li.cil.oc.common.block import li.cil.oc.common.tileentity import li.cil.oc.util.Color -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.world.World -import net.minecraftforge.common.property.IExtendedBlockState +import net.minecraft.util.Direction +import net.minecraftforge.client.model.data.IModelData import scala.collection.convert.WrapAsJava._ object ScreenModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { - val safeSide = if (side != null) side else EnumFacing.SOUTH - state match { - case extended: IExtendedBlockState => - extended.getValue(block.property.PropertyTile.Tile) match { - case screen: tileentity.Screen => - val facing = screen.toLocal(safeSide) + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = { + val safeSide = if (side != null) side else Direction.SOUTH + data match { + case screen: tileentity.Screen => + val facing = screen.toLocal(safeSide) - val (x, y) = screen.localPosition - var px = xy2part(x, screen.width - 1) - var py = xy2part(y, screen.height - 1) - if ((safeSide == EnumFacing.DOWN || screen.facing == EnumFacing.DOWN) && safeSide != screen.facing) { - px = 2 - px - py = 2 - py - } - val rotation = - if (safeSide == EnumFacing.UP) screen.yaw.getHorizontalIndex - else if (safeSide == EnumFacing.DOWN) -screen.yaw.getHorizontalIndex - else 0 + val (x, y) = screen.localPosition + var px = xy2part(x, screen.width - 1) + var py = xy2part(y, screen.height - 1) + if ((safeSide == Direction.DOWN || screen.facing == Direction.DOWN) && safeSide != screen.facing) { + px = 2 - px + py = 2 - py + } + val rotation = + if (safeSide == Direction.UP) screen.yaw.get2DDataValue + else if (safeSide == Direction.DOWN) -screen.yaw.get2DDataValue + else 0 - def pitch = if (screen.pitch == EnumFacing.NORTH) 0 else 1 - val texture = - if (screen.width == 1 && screen.height == 1) { - if (facing == EnumFacing.SOUTH) - Textures.Block.Screen.SingleFront(pitch) - else - Textures.Block.Screen.Single(safeSide.getIndex) - } - else if (screen.width == 1) { - if (facing == EnumFacing.SOUTH) - Textures.Block.Screen.VerticalFront(pitch)(py) - else - Textures.Block.Screen.Vertical(pitch)(py)(facing.getIndex) - } - else if (screen.height == 1) { - if (facing == EnumFacing.SOUTH) - Textures.Block.Screen.HorizontalFront(pitch)(px) - else - Textures.Block.Screen.Horizontal(pitch)(px)(facing.getIndex) - } - else { - if (facing == EnumFacing.SOUTH) - Textures.Block.Screen.MultiFront(pitch)(py)(px) - else - Textures.Block.Screen.Multi(pitch)(py)(px)(facing.getIndex) - } + def pitch = if (screen.pitch == Direction.NORTH) 0 else 1 + val texture = + if (screen.width == 1 && screen.height == 1) { + if (facing == Direction.SOUTH) + Textures.Block.Screen.SingleFront(pitch) + else + Textures.Block.Screen.Single(safeSide.get3DDataValue) + } + else if (screen.width == 1) { + if (facing == Direction.SOUTH) + Textures.Block.Screen.VerticalFront(pitch)(py) + else + Textures.Block.Screen.Vertical(pitch)(py)(facing.get3DDataValue) + } + else if (screen.height == 1) { + if (facing == Direction.SOUTH) + Textures.Block.Screen.HorizontalFront(pitch)(px) + else + Textures.Block.Screen.Horizontal(pitch)(px)(facing.get3DDataValue) + } + else { + if (facing == Direction.SOUTH) + Textures.Block.Screen.MultiFront(pitch)(py)(px) + else + Textures.Block.Screen.Multi(pitch)(py)(px)(facing.get3DDataValue) + } - seqAsJavaList(Seq(bakeQuad(safeSide, Textures.getSprite(texture), Some(screen.getColor), rotation))) - case _ => super.getQuads(state, safeSide, rand) - } + seqAsJavaList(Seq(bakeQuad(safeSide, Textures.getSprite(texture), Some(screen.getColor), rotation))) case _ => super.getQuads(state, safeSide, rand) } } @@ -88,18 +84,18 @@ object ScreenModel extends SmartBlockModelBase { case _ => Color.byTier(Tier.One) } - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = { + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val result = - if (side == EnumFacing.NORTH || side == null) + if (side == Direction.NORTH || side == null) Textures.Block.Screen.SingleFront(0) else Textures.Block.Screen.Single(side.ordinal()) - seqAsJavaList(Seq(bakeQuad(if (side != null) side else EnumFacing.SOUTH, Textures.getSprite(result), Some(Color.rgbValues(color)), 0))) + seqAsJavaList(Seq(bakeQuad(if (side != null) side else Direction.SOUTH, Textures.getSprite(result), Some(Color.rgbValues(color)), 0))) } } - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = new ItemModel(stack) + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = new ItemModel(stack) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala index 9b9e12740b..5601a3bff2 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala @@ -8,17 +8,17 @@ import li.cil.oc.api.event.RackMountableRenderEvent import li.cil.oc.client.Textures import li.cil.oc.common.block import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.BakedQuad -import net.minecraft.client.renderer.block.model.IBakedModel -import net.minecraft.client.renderer.block.model.ItemOverrideList -import net.minecraft.entity.EntityLivingBase +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.client.renderer.model.BakedQuad +import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d -import net.minecraft.world.World +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.common.property.IExtendedBlockState +import net.minecraftforge.client.model.data.IModelData import scala.collection.convert.WrapAsJava.bufferAsJavaList import scala.collection.mutable @@ -26,38 +26,34 @@ import scala.collection.mutable class ServerRackModel(val parent: IBakedModel) extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = - state match { - case extended: IExtendedBlockState => - extended.getValue(block.property.PropertyTile.Tile) match { - case rack: tileentity.Rack => - val facing = rack.facing - val faces = mutable.ArrayBuffer.empty[BakedQuad] + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = + data match { + case rack: tileentity.Rack => + val facing = rack.facing + val faces = mutable.ArrayBuffer.empty[BakedQuad] - for (side <- EnumFacing.values if side != facing) { - faces ++= bakeQuads(Case(side.getIndex), serverRackTexture, None) - } + for (side <- Direction.values if side != facing) { + faces ++= bakeQuads(Case(side.get3DDataValue), serverRackTexture, None) + } - val textures = serverTexture - val defaultFront = Textures.getSprite(Textures.Block.RackFront) - for (slot <- 0 until 4) rack.getMountable(slot) match { - case mountable: RackMountable => - val event = new RackMountableRenderEvent.Block(rack, slot, rack.lastData(slot), side) - MinecraftForge.EVENT_BUS.post(event) - if (!event.isCanceled) { - if (event.getFrontTextureOverride != null) { - (2 until 6).foreach(textures(_) = event.getFrontTextureOverride) - } else { - (2 until 6).foreach(textures(_) = defaultFront) - } - faces ++= bakeQuads(Servers(slot), textures, None) - } - case _ => + val textures = serverTexture + val defaultFront = Textures.getSprite(Textures.Block.RackFront) + for (slot <- 0 until 4) rack.getMountable(slot) match { + case mountable: RackMountable => + val event = new RackMountableRenderEvent.Block(rack, slot, rack.lastData(slot), side) + MinecraftForge.EVENT_BUS.post(event) + if (!event.isCanceled) { + if (event.getFrontTextureOverride != null) { + (2 until 6).foreach(textures(_) = event.getFrontTextureOverride) + } else { + (2 until 6).foreach(textures(_) = defaultFront) + } + faces ++= bakeQuads(Servers(slot), textures, None) } - - bufferAsJavaList(faces) - case _ => super.getQuads(state, side, rand) + case _ => } + + bufferAsJavaList(faces) case _ => super.getQuads(state, side, rand) } @@ -80,23 +76,23 @@ class ServerRackModel(val parent: IBakedModel) extends SmartBlockModelBase { ) protected final val Case = Array( - makeBox(new Vec3d(0 / 16f, 0 / 16f, 0 / 16f), new Vec3d(16 / 16f, 2 / 16f, 16 / 16f)), - makeBox(new Vec3d(0 / 16f, 14 / 16f, 0 / 16f), new Vec3d(16 / 16f, 16 / 16f, 16 / 16f)), - makeBox(new Vec3d(0 / 16f, 2 / 16f, 0 / 16f), new Vec3d(16 / 16f, 14 / 16f, 0.99f / 16f)), - makeBox(new Vec3d(0 / 16f, 2 / 16f, 15.01f / 16f), new Vec3d(16 / 16f, 14 / 16f, 16 / 16f)), - makeBox(new Vec3d(0 / 16f, 2 / 16f, 0 / 16f), new Vec3d(0.99f / 16f, 14 / 16f, 16 / 16f)), - makeBox(new Vec3d(15.01f / 16f, 2 / 16f, 0 / 16f), new Vec3d(16 / 16f, 14f / 16f, 16 / 16f)) + makeBox(new Vector3d(0 / 16f, 0 / 16f, 0 / 16f), new Vector3d(16 / 16f, 2 / 16f, 16 / 16f)), + makeBox(new Vector3d(0 / 16f, 14 / 16f, 0 / 16f), new Vector3d(16 / 16f, 16 / 16f, 16 / 16f)), + makeBox(new Vector3d(0 / 16f, 2 / 16f, 0 / 16f), new Vector3d(16 / 16f, 14 / 16f, 0.99f / 16f)), + makeBox(new Vector3d(0 / 16f, 2 / 16f, 15.01f / 16f), new Vector3d(16 / 16f, 14 / 16f, 16 / 16f)), + makeBox(new Vector3d(0 / 16f, 2 / 16f, 0 / 16f), new Vector3d(0.99f / 16f, 14 / 16f, 16 / 16f)), + makeBox(new Vector3d(15.01f / 16f, 2 / 16f, 0 / 16f), new Vector3d(16 / 16f, 14f / 16f, 16 / 16f)) ) protected final val Servers = Array( - makeBox(new Vec3d(0.5f / 16f, 11 / 16f, 0.5f / 16f), new Vec3d(15.5f / 16f, 14 / 16f, 15.5f / 16f)), - makeBox(new Vec3d(0.5f / 16f, 8 / 16f, 0.5f / 16f), new Vec3d(15.5f / 16f, 11 / 16f, 15.5f / 16f)), - makeBox(new Vec3d(0.5f / 16f, 5 / 16f, 0.5f / 16f), new Vec3d(15.5f / 16f, 8 / 16f, 15.5f / 16f)), - makeBox(new Vec3d(0.5f / 16f, 2 / 16f, 0.5f / 16f), new Vec3d(15.5f / 16f, 5 / 16f, 15.5f / 16f)) + makeBox(new Vector3d(0.5f / 16f, 11 / 16f, 0.5f / 16f), new Vector3d(15.5f / 16f, 14 / 16f, 15.5f / 16f)), + makeBox(new Vector3d(0.5f / 16f, 8 / 16f, 0.5f / 16f), new Vector3d(15.5f / 16f, 11 / 16f, 15.5f / 16f)), + makeBox(new Vector3d(0.5f / 16f, 5 / 16f, 0.5f / 16f), new Vector3d(15.5f / 16f, 8 / 16f, 15.5f / 16f)), + makeBox(new Vector3d(0.5f / 16f, 2 / 16f, 0.5f / 16f), new Vector3d(15.5f / 16f, 5 / 16f, 15.5f / 16f)) ) - object ItemOverride extends ItemOverrideList(Collections.emptyList()) { - override def handleItemState(originalModel: IBakedModel, stack: ItemStack, world: World, entity: EntityLivingBase): IBakedModel = parent + object ItemOverride extends ItemOverrideList { + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = parent } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala b/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala index 88479d1899..0cc99a04c9 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala @@ -4,32 +4,37 @@ import java.util import java.util.Collections import li.cil.oc.client.Textures -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.block.model._ +import net.minecraft.client.renderer.model._ import net.minecraft.client.renderer.texture.TextureAtlasSprite -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d -import org.lwjgl.util.vector.Vector3f +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.math.vector.Vector3f trait SmartBlockModelBase extends IBakedModel { - override def getOverrides: ItemOverrideList = ItemOverrideList.NONE + override def getOverrides: ItemOverrideList = ItemOverrideList.EMPTY - override def getQuads(state: IBlockState, side: EnumFacing, rand: Long): util.List[BakedQuad] = Collections.emptyList() + @Deprecated + override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = Collections.emptyList() - override def isAmbientOcclusion = true + override def useAmbientOcclusion = true override def isGui3d = true - override def isBuiltInRenderer = false + override def usesBlockLight = true + + override def isCustomRenderer = true // Note: we don't care about the actual texture here, we just need the block // texture atlas. So any of our textures we know is loaded into it will do. - override def getParticleTexture = Textures.getSprite(Textures.Block.GenericTop) + @Deprecated + override def getParticleIcon = Textures.getSprite(Textures.Block.GenericTop) - override def getItemCameraTransforms = DefaultBlockCameraTransforms + @Deprecated + override def getTransforms = DefaultBlockCameraTransforms + @Deprecated protected final val DefaultBlockCameraTransforms = { val gui = new ItemTransformVec3f(new Vector3f(30, 225, 0), new Vector3f(0, 0, 0), new Vector3f(0.625f, 0.625f, 0.625f)) val ground = new ItemTransformVec3f(new Vector3f(0, 0, 0), new Vector3f(0, 3, 0), new Vector3f(0.25f, 0.25f, 0.25f)) @@ -39,46 +44,46 @@ trait SmartBlockModelBase extends IBakedModel { val firstperson_lefthand = new ItemTransformVec3f(new Vector3f(0, 225, 0), new Vector3f(0, 0, 0), new Vector3f(0.40f, 0.40f, 0.40f)) // scale(0.0625f): see ItemTransformVec3f.Deserializer.deserialize. - gui.translation.scale(0.0625f) - ground.translation.scale(0.0625f) - fixed.translation.scale(0.0625f) - thirdperson_righthand.translation.scale(0.0625f) - firstperson_righthand.translation.scale(0.0625f) - firstperson_lefthand.translation.scale(0.0625f) + gui.translation.mul(0.0625f) + ground.translation.mul(0.0625f) + fixed.translation.mul(0.0625f) + thirdperson_righthand.translation.mul(0.0625f) + firstperson_righthand.translation.mul(0.0625f) + firstperson_lefthand.translation.mul(0.0625f) new ItemCameraTransforms( - ItemTransformVec3f.DEFAULT, + ItemTransformVec3f.NO_TRANSFORM, thirdperson_righthand, firstperson_lefthand, firstperson_righthand, - ItemTransformVec3f.DEFAULT, + ItemTransformVec3f.NO_TRANSFORM, gui, ground, fixed) } - protected def missingModel = Minecraft.getMinecraft.getRenderItem.getItemModelMesher.getModelManager.getMissingModel + protected def missingModel = Minecraft.getInstance.getModelManager.getMissingModel // Standard faces for a unit cube. protected final val UnitCube = Array( - Array(new Vec3d(0, 0, 1), new Vec3d(0, 0, 0), new Vec3d(1, 0, 0), new Vec3d(1, 0, 1)), - Array(new Vec3d(0, 1, 0), new Vec3d(0, 1, 1), new Vec3d(1, 1, 1), new Vec3d(1, 1, 0)), - Array(new Vec3d(1, 1, 0), new Vec3d(1, 0, 0), new Vec3d(0, 0, 0), new Vec3d(0, 1, 0)), - Array(new Vec3d(0, 1, 1), new Vec3d(0, 0, 1), new Vec3d(1, 0, 1), new Vec3d(1, 1, 1)), - Array(new Vec3d(0, 1, 0), new Vec3d(0, 0, 0), new Vec3d(0, 0, 1), new Vec3d(0, 1, 1)), - Array(new Vec3d(1, 1, 1), new Vec3d(1, 0, 1), new Vec3d(1, 0, 0), new Vec3d(1, 1, 0)) + Array(new Vector3d(0, 0, 1), new Vector3d(0, 0, 0), new Vector3d(1, 0, 0), new Vector3d(1, 0, 1)), + Array(new Vector3d(0, 1, 0), new Vector3d(0, 1, 1), new Vector3d(1, 1, 1), new Vector3d(1, 1, 0)), + Array(new Vector3d(1, 1, 0), new Vector3d(1, 0, 0), new Vector3d(0, 0, 0), new Vector3d(0, 1, 0)), + Array(new Vector3d(0, 1, 1), new Vector3d(0, 0, 1), new Vector3d(1, 0, 1), new Vector3d(1, 1, 1)), + Array(new Vector3d(0, 1, 0), new Vector3d(0, 0, 0), new Vector3d(0, 0, 1), new Vector3d(0, 1, 1)), + Array(new Vector3d(1, 1, 1), new Vector3d(1, 0, 1), new Vector3d(1, 0, 0), new Vector3d(1, 1, 0)) ) // Planes perpendicular to facings. Negative values mean we mirror along that, // axis which is done to mirror back faces and the y axis (because up is // positive but for our texture coordinates down is positive). protected final val Planes = Array( - (new Vec3d(1, 0, 0), new Vec3d(0, 0, -1)), - (new Vec3d(1, 0, 0), new Vec3d(0, 0, 1)), - (new Vec3d(-1, 0, 0), new Vec3d(0, -1, 0)), - (new Vec3d(1, 0, 0), new Vec3d(0, -1, 0)), - (new Vec3d(0, 0, 1), new Vec3d(0, -1, 0)), - (new Vec3d(0, 0, -1), new Vec3d(0, -1, 0)) + (new Vector3d(1, 0, 0), new Vector3d(0, 0, -1)), + (new Vector3d(1, 0, 0), new Vector3d(0, 0, 1)), + (new Vector3d(-1, 0, 0), new Vector3d(0, -1, 0)), + (new Vector3d(1, 0, 0), new Vector3d(0, -1, 0)), + (new Vector3d(0, 0, 1), new Vector3d(0, -1, 0)), + (new Vector3d(0, 0, -1), new Vector3d(0, -1, 0)) ) protected final val White = 0xFFFFFF @@ -87,34 +92,34 @@ trait SmartBlockModelBase extends IBakedModel { * Generates a list of arrays, each containing the four vertices making up a * face of the box with the specified size. */ - protected def makeBox(from: Vec3d, to: Vec3d) = { + protected def makeBox(from: Vector3d, to: Vector3d) = { val minX = math.min(from.x, to.x) val minY = math.min(from.y, to.y) val minZ = math.min(from.z, to.z) val maxX = math.max(from.x, to.x) val maxY = math.max(from.y, to.y) val maxZ = math.max(from.z, to.z) - UnitCube.map(face => face.map(vertex => new Vec3d( + UnitCube.map(face => face.map(vertex => new Vector3d( math.max(minX, math.min(maxX, vertex.x)), math.max(minY, math.min(maxY, vertex.y)), math.max(minZ, math.min(maxZ, vertex.z))))) } - protected def rotateVector(v: Vec3d, angle: Double, axis: Vec3d) = { + protected def rotateVector(v: Vector3d, angle: Double, axis: Vector3d) = { // vrot = v * cos(angle) + (axis x v) * sin(angle) + axis * (axis dot v)(1 - cos(angle)) - def scale(v: Vec3d, s: Double) = new Vec3d(v.x * s, v.y * s, v.z * s) + def scale(v: Vector3d, s: Double) = v.scale(s) val cosAngle = math.cos(angle) val sinAngle = math.sin(angle) scale(v, cosAngle). - add(scale(axis.crossProduct(v), sinAngle)). - add(scale(axis, axis.dotProduct(v) * (1 - cosAngle))) + add(scale(axis.cross(v), sinAngle)). + add(scale(axis, axis.dot(v) * (1 - cosAngle))) } - protected def rotateFace(face: Array[Vec3d], angle: Double, axis: Vec3d, around: Vec3d = new Vec3d(0.5, 0.5, 0.5)) = { + protected def rotateFace(face: Array[Vector3d], angle: Double, axis: Vector3d, around: Vector3d = new Vector3d(0.5, 0.5, 0.5)) = { face.map(v => rotateVector(v.subtract(around), angle, axis).add(around)) } - protected def rotateBox(box: Array[Array[Vec3d]], angle: Double, axis: Vec3d = new Vec3d(0, 1, 0), around: Vec3d = new Vec3d(0.5, 0.5, 0.5)) = { + protected def rotateBox(box: Array[Array[Vector3d]], angle: Double, axis: Vector3d = new Vector3d(0, 1, 0), around: Vector3d = new Vector3d(0.5, 0.5, 0.5)) = { box.map(face => rotateFace(face, angle, axis, around)) } @@ -123,7 +128,7 @@ trait SmartBlockModelBase extends IBakedModel { *

    * Usually used to generate the quads for a cube previously generated using makeBox(). */ - protected def bakeQuads(box: Array[Array[Vec3d]], texture: Array[TextureAtlasSprite], color: Option[Int]): Array[BakedQuad] = { + protected def bakeQuads(box: Array[Array[Vector3d]], texture: Array[TextureAtlasSprite], color: Option[Int]): Array[BakedQuad] = { val colorRGB = color.getOrElse(White) bakeQuads(box, texture, colorRGB) } @@ -133,33 +138,33 @@ trait SmartBlockModelBase extends IBakedModel { *

    * Usually used to generate the quads for a cube previously generated using makeBox(). */ - protected def bakeQuads(box: Array[Array[Vec3d]], texture: Array[TextureAtlasSprite], colorRGB: Int): Array[BakedQuad] = { - EnumFacing.values.map(side => { - val vertices = box(side.getIndex) - val data = quadData(vertices, side, texture(side.getIndex), colorRGB, 0) - new BakedQuad(data, -1, side, texture(side.getIndex), true, DefaultVertexFormats.ITEM) + protected def bakeQuads(box: Array[Array[Vector3d]], texture: Array[TextureAtlasSprite], colorRGB: Int): Array[BakedQuad] = { + Direction.values.map(side => { + val vertices = box(side.get3DDataValue) + val data = quadData(vertices, side, texture(side.get3DDataValue), colorRGB, 0) + new BakedQuad(data, -1, side, texture(side.get3DDataValue), true) }) } /** * Create a single BakedQuad of a unit cube's specified side. */ - protected def bakeQuad(side: EnumFacing, texture: TextureAtlasSprite, color: Option[Int], rotation: Int) = { + protected def bakeQuad(side: Direction, texture: TextureAtlasSprite, color: Option[Int], rotation: Int) = { val colorRGB = color.getOrElse(White) - val vertices = UnitCube(side.getIndex) + val vertices = UnitCube(side.get3DDataValue) val data = quadData(vertices, side, texture, colorRGB, rotation) - new BakedQuad(data, -1, side, texture, true, DefaultVertexFormats.ITEM) + new BakedQuad(data, -1, side, texture, true) } // Generate raw data used for a BakedQuad based on the specified facing, vertices, texture and rotation. // The UV coordinates are generated from the positions of the vertices, i.e. they are simply cube- // mapped. This is good enough for us. - protected def quadData(vertices: Array[Vec3d], facing: EnumFacing, texture: TextureAtlasSprite, colorRGB: Int, rotation: Int): Array[Int] = { - val (uAxis, vAxis) = Planes(facing.getIndex) + protected def quadData(vertices: Array[Vector3d], facing: Direction, texture: TextureAtlasSprite, colorRGB: Int, rotation: Int): Array[Int] = { + val (uAxis, vAxis) = Planes(facing.get3DDataValue) val rot = (rotation + 4) % 4 vertices.flatMap(vertex => { - var u = vertex.dotProduct(uAxis) - var v = vertex.dotProduct(vAxis) + var u = vertex.dot(uAxis) + var v = vertex.dot(vAxis) if (uAxis.x + uAxis.y + uAxis.z < 0) u = 1 + u if (vAxis.x + vAxis.y + vAxis.z < 0) v = 1 + v for (i <- 0 until rot) { @@ -168,15 +173,15 @@ trait SmartBlockModelBase extends IBakedModel { u = v v = (-(tmp - 0.5)) + 0.5 } - rawData(vertex.x, vertex.y, vertex.z, facing, texture, texture.getInterpolatedU(u * 16), texture.getInterpolatedV(v * 16), colorRGB) + rawData(vertex.x, vertex.y, vertex.z, facing, texture, texture.getU(u * 16), texture.getV(v * 16), colorRGB) }) } // See FaceBakery#storeVertexData. - protected def rawData(x: Double, y: Double, z: Double, face: EnumFacing, texture: TextureAtlasSprite, u: Float, v: Float, colorRGB: Int) = { - val vx = (face.getFrontOffsetX * 127) & 0xFF - val vy = (face.getFrontOffsetY * 127) & 0xFF - val vz = (face.getFrontOffsetZ * 127) & 0xFF + protected def rawData(x: Double, y: Double, z: Double, face: Direction, texture: TextureAtlasSprite, u: Float, v: Float, colorRGB: Int) = { + val vx = (face.getStepX * 127) & 0xFF + val vy = (face.getStepY * 127) & 0xFF + val vz = (face.getStepZ * 127) & 0xFF Array( java.lang.Float.floatToRawIntBits(x.toFloat), @@ -190,7 +195,7 @@ trait SmartBlockModelBase extends IBakedModel { } // See FaceBakery. - protected def getFaceShadeColor(face: EnumFacing, colorRGB: Int): Int = { + protected def getFaceShadeColor(face: Direction, colorRGB: Int): Int = { val brightness = getFaceBrightness(face) val b = (colorRGB >> 16) & 0xFF val g = (colorRGB >> 8) & 0xFF @@ -200,12 +205,12 @@ trait SmartBlockModelBase extends IBakedModel { private def shade(value: Int, brightness: Float) = (brightness * value).toInt max 0 min 255 - protected def getFaceBrightness(face: EnumFacing): Float = { + protected def getFaceBrightness(face: Direction): Float = { face match { - case EnumFacing.DOWN => 0.5f - case EnumFacing.UP => 1.0f - case EnumFacing.NORTH | EnumFacing.SOUTH => 0.8f - case EnumFacing.WEST | EnumFacing.EAST => 0.6f + case Direction.DOWN => 0.5f + case Direction.UP => 1.0f + case Direction.NORTH | Direction.SOUTH => 0.8f + case Direction.WEST | Direction.EAST => 0.6f } } } diff --git a/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala index 3f4d1ab441..f4ad998fd9 100644 --- a/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala @@ -1,27 +1,45 @@ package li.cil.oc.client.renderer.entity +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.entity.Drone import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.entity.Render +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.entity.EntityRenderer +import net.minecraft.client.renderer.entity.EntityRendererManager +import net.minecraft.client.renderer.texture.OverlayTexture +import net.minecraftforge.fml.client.registry.IRenderFactory -object DroneRenderer extends Render[Drone](Minecraft.getMinecraft.getRenderManager) { +object DroneRenderer extends IRenderFactory[Drone] { val model = new ModelQuadcopter() - override def doRender(entity: Drone, x: Double, y: Double, z: Double, yaw: Float, dt: Float) { - bindEntityTexture(entity) - GlStateManager.pushMatrix() - RenderState.pushAttrib() + override def createRenderFor(manager: EntityRendererManager) = new DroneRenderer(manager) +} - GlStateManager.translate(x, y + 2 / 16f, z) +class DroneRenderer(manager: EntityRendererManager) extends EntityRenderer[Drone](manager) { + override def render(entity: Drone, yaw: Float, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int): Unit = { + val renderType = getRenderType(entity) + if (renderType != null) { + stack.pushPose() + stack.translate(0, 2f / 16f, 0) + val builder = buffer.getBuffer(renderType) + DroneRenderer.model.renderToBuffer(stack, builder, light, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1) + stack.popPose() + } + super.render(entity, yaw, dt, stack, buffer, light) + } - model.render(entity, 0, 0, 0, 0, 0, dt) + override def getTextureLocation(entity: Drone) = Textures.Model.Drone - RenderState.popAttrib() - GlStateManager.popMatrix() + def getRenderType(entity: Drone): RenderType = { + val mc = Minecraft.getInstance + val texture = getTextureLocation(entity) + if (!entity.isInvisible) DroneRenderer.model.renderType(texture) + else if (!entity.isInvisibleTo(mc.player)) RenderType.itemEntityTranslucentCull(texture) + else if (mc.shouldEntityAppearGlowing(entity)) RenderType.outline(texture) + else null } - - override def getEntityTexture(entity: Drone) = Textures.Model.Drone } diff --git a/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala b/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala index 6b8e51d110..b65edc11cb 100644 --- a/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala +++ b/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala @@ -1,179 +1,128 @@ package li.cil.oc.client.renderer.entity +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.common.entity.Drone import li.cil.oc.util.RenderState -import net.minecraft.client.model.ModelBase -import net.minecraft.client.model.ModelRenderer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.entity.model.EntityModel +import net.minecraft.client.renderer.model.ModelRenderer import net.minecraft.entity.Entity -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -final class ModelQuadcopter extends ModelBase { - val body = new ModelRenderer(this, "body") - val wing0 = new ModelRenderer(this, "wing0") - val wing1 = new ModelRenderer(this, "wing1") - val wing2 = new ModelRenderer(this, "wing2") - val wing3 = new ModelRenderer(this, "wing3") - val light0 = new ModelRenderer(this, "light0") - val light1 = new ModelRenderer(this, "light1") - val light2 = new ModelRenderer(this, "light2") - val light3 = new ModelRenderer(this, "light3") - - textureWidth = 64 - textureHeight = 32 - - setTextureOffset("body.middle", 0, 23) - setTextureOffset("body.top", 0, 1) - setTextureOffset("body.bottom", 0, 17) - setTextureOffset("wing0.flap0", 0, 9) - setTextureOffset("wing0.pin0", 0, 27) - setTextureOffset("wing1.flap1", 0, 9) - setTextureOffset("wing1.pin1", 0, 27) - setTextureOffset("wing2.flap2", 0, 9) - setTextureOffset("wing2.pin2", 0, 27) - setTextureOffset("wing3.flap3", 0, 9) - setTextureOffset("wing3.pin3", 0, 27) - - setTextureOffset("light0.flap0", 24, 0) - setTextureOffset("light1.flap1", 24, 0) - setTextureOffset("light2.flap2", 24, 0) - setTextureOffset("light3.flap3", 24, 0) - - body.addBox("top", -3, 1, -3, 6, 1, 6).rotateAngleY = math.toRadians(45).toFloat - body.addBox("middle", -1, 0, -1, 2, 1, 2).rotateAngleY = math.toRadians(45).toFloat - body.addBox("bottom", -2, -1, -2, 4, 1, 4).rotateAngleY = math.toRadians(45).toFloat - wing0.addBox("flap0", 1, 0, -7, 6, 1, 6) - wing0.addBox("pin0", 2, -1, -3, 1, 3, 1) - wing1.addBox("flap1", 1, 0, 1, 6, 1, 6) - wing1.addBox("pin1", 2, -1, 2, 1, 3, 1) - wing2.addBox("flap2", -7, 0, 1, 6, 1, 6) - wing2.addBox("pin2", -3, -1, 2, 1, 3, 1) - wing3.addBox("flap3", -7, 0, -7, 6, 1, 6) - wing3.addBox("pin3", -3, -1, -3, 1, 3, 1) - - light0.addBox("flap0", 1, 0, -7, 6, 1, 6) - light1.addBox("flap1", 1, 0, 1, 6, 1, 6) - light2.addBox("flap2", -7, 0, 1, 6, 1, 6) - light3.addBox("flap3", -7, 0, -7, 6, 1, 6) - - private val scale = 1 / 16f - private val up = new Vec3d(0, 1, 0) - - private def doRender(drone: Drone, dt: Float) { +final class ModelQuadcopter extends EntityModel[Drone] { + val body = new ModelRenderer(this) + val wing0 = new ModelRenderer(this) + val wing1 = new ModelRenderer(this) + val wing2 = new ModelRenderer(this) + val wing3 = new ModelRenderer(this) + val light0 = new ModelRenderer(this) + val light1 = new ModelRenderer(this) + val light2 = new ModelRenderer(this) + val light3 = new ModelRenderer(this) + + texWidth = 64 + texHeight = 32 + + body.texOffs(0, 23).addBox(-3, 1, -3, 6, 1, 6).yRot = math.toRadians(45).toFloat // top + body.texOffs(0, 1).addBox(-1, 0, -1, 2, 1, 2).yRot = math.toRadians(45).toFloat // middle + body.texOffs(0, 17).addBox(-2, -1, -2, 4, 1, 4).yRot = math.toRadians(45).toFloat // bottom + wing0.texOffs(0, 9).addBox(1, 0, -7, 6, 1, 6) // flap0 + wing0.texOffs(0, 27).addBox(2, -1, -3, 1, 3, 1) // pin0 + wing1.texOffs(0, 9).addBox(1, 0, 1, 6, 1, 6) // flap1 + wing1.texOffs(0, 27).addBox(2, -1, 2, 1, 3, 1) // pin1 + wing2.texOffs(0, 9).addBox(-7, 0, 1, 6, 1, 6) // flap2 + wing2.texOffs(0, 27).addBox(-3, -1, 2, 1, 3, 1) // pin2 + wing3.texOffs(0, 9).addBox(-7, 0, -7, 6, 1, 6) // flap3 + wing3.texOffs(0, 27).addBox(-3, -1, -3, 1, 3, 1) // pin3 + + light0.texOffs(24, 0).addBox(1, 0, -7, 6, 1, 6) // flap0 + light1.texOffs(24, 0).addBox(1, 0, 1, 6, 1, 6) // flap1 + light2.texOffs(24, 0).addBox(-7, 0, 1, 6, 1, 6) // flap2 + light3.texOffs(24, 0).addBox(-7, 0, -7, 6, 1, 6) // flap3 + + private val up = new Vector3d(0, 1, 0) + + private def doRender(drone: Drone, dt: Float, stack: MatrixStack, builder: IVertexBuilder, light: Int, overlay: Int, r: Float, g: Float, b: Float, a: Float) { + stack.pushPose() if (drone.isRunning) { val timeJitter = drone.hashCode() ^ 0xFF - GlStateManager.translate(0, (math.sin(timeJitter + (drone.getEntityWorld.getTotalWorldTime + dt) / 20.0) * (1 / 16f)).toFloat, 0) + stack.translate(0, (math.sin(timeJitter + (drone.level.getGameTime + dt) / 20.0) * (1 / 16f)).toFloat, 0) } - val velocity = new Vec3d(drone.motionX, drone.motionY, drone.motionZ) - val direction = velocity.normalize() - if (direction.dotProduct(up) < 0.99) { + val direction = drone.getDeltaMovement.normalize() + if (direction.dot(up) < 0.99) { // Flying sideways. - val rotationAxis = direction.crossProduct(up) - val relativeSpeed = velocity.lengthVector().toFloat / drone.maxVelocity - GlStateManager.rotate(relativeSpeed * -20, rotationAxis.x.toFloat, rotationAxis.y.toFloat, rotationAxis.z.toFloat) + val rotationAxis = direction.cross(up) + val relativeSpeed = drone.getDeltaMovement.length().toFloat / drone.maxVelocity + stack.mulPose(new Vector3f(rotationAxis).rotationDegrees(relativeSpeed * -20)) } - GlStateManager.rotate(drone.bodyAngle, 0, 1, 0) + stack.mulPose(Vector3f.YP.rotationDegrees(drone.bodyAngle)) + body.render(stack, builder, light, overlay, r, g, b, a) - body.render(scale) + wing0.xRot = drone.flapAngles(0)(0) + wing0.zRot = drone.flapAngles(0)(1) + wing1.xRot = drone.flapAngles(1)(0) + wing1.zRot = drone.flapAngles(1)(1) + wing2.xRot = drone.flapAngles(2)(0) + wing2.zRot = drone.flapAngles(2)(1) + wing3.xRot = drone.flapAngles(3)(0) + wing3.zRot = drone.flapAngles(3)(1) - wing0.rotateAngleX = drone.flapAngles(0)(0) - wing0.rotateAngleZ = drone.flapAngles(0)(1) - wing1.rotateAngleX = drone.flapAngles(1)(0) - wing1.rotateAngleZ = drone.flapAngles(1)(1) - wing2.rotateAngleX = drone.flapAngles(2)(0) - wing2.rotateAngleZ = drone.flapAngles(2)(1) - wing3.rotateAngleX = drone.flapAngles(3)(0) - wing3.rotateAngleZ = drone.flapAngles(3)(1) - - wing0.render(scale) - wing1.render(scale) - wing2.render(scale) - wing3.render(scale) + wing0.render(stack, builder, light, overlay, r, g, b, a) + wing1.render(stack, builder, light, overlay, r, g, b, a) + wing2.render(stack, builder, light, overlay, r, g, b, a) + wing3.render(stack, builder, light, overlay, r, g, b, a) if (drone.isRunning) { RenderState.disableEntityLighting() - GlStateManager.depthFunc(GL11.GL_LEQUAL) + RenderSystem.depthFunc(GL11.GL_LEQUAL) - light0.rotateAngleX = drone.flapAngles(0)(0) - light0.rotateAngleZ = drone.flapAngles(0)(1) - light1.rotateAngleX = drone.flapAngles(1)(0) - light1.rotateAngleZ = drone.flapAngles(1)(1) - light2.rotateAngleX = drone.flapAngles(2)(0) - light2.rotateAngleZ = drone.flapAngles(2)(1) - light3.rotateAngleX = drone.flapAngles(3)(0) - light3.rotateAngleZ = drone.flapAngles(3)(1) + light0.xRot = drone.flapAngles(0)(0) + light0.zRot = drone.flapAngles(0)(1) + light1.xRot = drone.flapAngles(1)(0) + light1.zRot = drone.flapAngles(1)(1) + light2.xRot = drone.flapAngles(2)(0) + light2.zRot = drone.flapAngles(2)(1) + light3.xRot = drone.flapAngles(3)(0) + light3.zRot = drone.flapAngles(3)(1) // Additive blending for the lights. RenderState.makeItBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) val lightColor = drone.lightColor val r = (lightColor >>> 16) & 0xFF val g = (lightColor >>> 8) & 0xFF val b = (lightColor >>> 0) & 0xFF - GlStateManager.color(r / 255f, g / 255f, b / 255f) + RenderSystem.color3f(r / 255f, g / 255f, b / 255f) - light0.render(scale) - light1.render(scale) - light2.render(scale) - light3.render(scale) + light0.render(stack, builder, light, overlay, r, g, b, a) + light1.render(stack, builder, light, overlay, r, g, b, a) + light2.render(stack, builder, light, overlay, r, g, b, a) + light3.render(stack, builder, light, overlay, r, g, b, a) RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) } + stack.popPose() } - // For inventory rendering. - def render() { - body.render(scale) - - val tilt = math.toRadians(2).toFloat - wing0.rotateAngleX = tilt - wing0.rotateAngleZ = tilt - wing1.rotateAngleX = -tilt - wing1.rotateAngleZ = tilt - wing2.rotateAngleX = -tilt - wing2.rotateAngleZ = -tilt - wing3.rotateAngleX = tilt - wing3.rotateAngleZ = -tilt - - wing0.render(scale) - wing1.render(scale) - wing2.render(scale) - wing3.render(scale) - - RenderState.disableEntityLighting() - GlStateManager.depthFunc(GL11.GL_LEQUAL) - - light0.rotateAngleX = tilt - light0.rotateAngleZ = tilt - light1.rotateAngleX = -tilt - light1.rotateAngleZ = tilt - light2.rotateAngleX = -tilt - light2.rotateAngleZ = -tilt - light3.rotateAngleX = tilt - light3.rotateAngleZ = -tilt - - - RenderState.makeItBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - GlStateManager.color(0x66 / 255f, 0xDD / 255f, 0x55 / 255f) - - light0.render(scale) - light1.render(scale) - light2.render(scale) - light3.render(scale) - - RenderState.disableBlend() - RenderState.enableEntityLighting() - GlStateManager.color(1, 1, 1, 1) + private var cachedEntity: Drone = null + private var cachedDt = 0.0f + + override def setupAnim(drone: Drone, f1: Float, f2: Float, f3: Float, f4: Float, f5: Float): Unit = {} + + override def prepareMobModel(drone: Drone, f1: Float, f2: Float, dt: Float): Unit = { + cachedEntity = drone + cachedDt = dt } - override def render(entity: Entity, f1: Float, f2: Float, f3: Float, f4: Float, f5: Float, f6: Float): Unit = { - doRender(entity.asInstanceOf[Drone], f6) + override def renderToBuffer(stack: MatrixStack, builder: IVertexBuilder, light: Int, overlay: Int, r: Float, g: Float, b: Float, a: Float): Unit = { + doRender(cachedEntity, cachedDt, stack, builder, light: Int, overlay: Int, r: Float, g: Float, b: Float, a: Float) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala index 1e178b7052..6bf0a4cc55 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala @@ -1,14 +1,17 @@ package li.cil.oc.client.renderer.font +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture import li.cil.oc.util.FontUtils import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.resources.IReloadableResourceManager -import net.minecraft.client.resources.IResourceManager -import net.minecraft.client.resources.IResourceManagerReloadListener +import net.minecraft.client.renderer.texture.TextureUtil +import net.minecraft.resources.IReloadableResourceManager +import net.minecraft.resources.IResourceManager +import net.minecraft.resources.IResourceManagerReloadListener +import net.minecraft.util.math.vector.Matrix4f +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.BufferUtils import org.lwjgl.opengl._ @@ -31,7 +34,7 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa initialize() - Minecraft.getMinecraft.getResourceManager match { + Minecraft.getInstance.getResourceManager match { case reloadable: IReloadableResourceManager => reloadable.registerReloadListener(this) case _ => } @@ -68,9 +71,9 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa charMap.getOrElseUpdate(char, createCharIcon(char)) } - override protected def drawChar(tx: Float, ty: Float, char: Char) { + override protected def drawChar(matrix: Matrix4f, tx: Float, ty: Float, char: Char) { charMap.get(char) match { - case Some(icon) if icon.texture == activeTexture => icon.draw(tx, ty) + case Some(icon) if icon.texture == activeTexture => icon.draw(matrix, tx, ty) case _ => } } @@ -94,7 +97,7 @@ object DynamicFontRenderer { private val size = 256 class CharTexture(val owner: DynamicFontRenderer) { - private val id = GlStateManager.generateTexture() + private val id = TextureUtil.generateTextureId() RenderState.bindTexture(id) if (Settings.get.textLinearFiltering) { GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR) @@ -114,13 +117,13 @@ object DynamicFontRenderer { private val rows = size / cellHeight private val uStep = cellWidth / size.toDouble private val vStep = cellHeight / size.toDouble - private val pad = 1.0 / size + private val pad = 1f / size private val capacity = cols * rows private var chars = 0 def delete() { - GlStateManager.deleteTexture(id) + RenderSystem.deleteTexture(id) } def bind() { @@ -150,15 +153,23 @@ object DynamicFontRenderer { } class CharIcon(val texture: CharTexture, val w: Int, val h: Int, val u1: Double, val v1: Double, val u2: Double, val v2: Double) { - def draw(tx: Float, ty: Float) { + def draw(matrix: Matrix4f, tx: Float, ty: Float) { GL11.glTexCoord2d(u1, v2) - GL11.glVertex2f(tx, ty + h) + val vec = new Vector4f(tx, ty + h, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u2, v2) - GL11.glVertex2f(tx + w, ty + h) + vec.set(tx + w, ty + h, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u2, v1) - GL11.glVertex2f(tx + w, ty) + vec.set(tx + w, ty, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u1, v1) - GL11.glVertex2f(tx, ty) + vec.set(tx, ty, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java b/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java index 78eee08440..3a24a6fb69 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java +++ b/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java @@ -25,7 +25,7 @@ public void initialize() { glyphs[i] = null; } try { - final InputStream font = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(Settings.resourceDomain(), "font.hex")).getInputStream(); + final InputStream font = Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(Settings.resourceDomain(), "font.hex")).getInputStream(); try { OpenComputers.log().info("Initializing unicode glyph provider."); final BufferedReader input = new BufferedReader(new InputStreamReader(font)); diff --git a/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala index ff3a55f63d..103bfea405 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala @@ -6,6 +6,8 @@ import li.cil.oc.Settings import li.cil.oc.client.Textures import net.minecraft.client.Minecraft import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.vector.Matrix4f +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 import scala.io.Source @@ -16,7 +18,7 @@ import scala.io.Source */ class StaticFontRenderer extends TextureFontRenderer { protected val (chars, charWidth, charHeight) = try { - val lines = Source.fromInputStream(Minecraft.getMinecraft.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "textures/font/chars.txt")).getInputStream)(Charsets.UTF_8).getLines() + val lines = Source.fromInputStream(Minecraft.getInstance.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "textures/font/chars.txt")).getInputStream)(Charsets.UTF_8).getLines() val chars = lines.next() val (w, h) = if (lines.hasNext) { val size = lines.next().split(" ", 2) @@ -31,11 +33,11 @@ class StaticFontRenderer extends TextureFontRenderer { } private val cols = 256 / charWidth - private val uStep = charWidth / 256.0 + private val uStep = charWidth / 256f private val uSize = uStep - private val vStep = (charHeight + 1) / 256.0 - private val vSize = charHeight / 256.0 - private val s = Settings.get.fontCharScale + private val vStep = (charHeight + 1) / 256f + private val vSize = charHeight / 256f + private val s = Settings.get.fontCharScale.toFloat private val dw = charWidth * s - charWidth private val dh = charHeight * s - charHeight @@ -50,7 +52,7 @@ class StaticFontRenderer extends TextureFontRenderer { } } - override protected def drawChar(tx: Float, ty: Float, char: Char) { + override protected def drawChar(matrix: Matrix4f, tx: Float, ty: Float, char: Char) { val index = 1 + (chars.indexOf(char) match { case -1 => chars.indexOf('?') case i => i @@ -60,13 +62,21 @@ class StaticFontRenderer extends TextureFontRenderer { val u = x * uStep val v = y * vStep GL11.glTexCoord2d(u, v + vSize) - GL11.glVertex3d(tx - dw, ty + charHeight * s, 0) + val vec = new Vector4f(tx - dw, ty + charHeight * s, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u + uSize, v + vSize) - GL11.glVertex3d(tx + charWidth * s, ty + charHeight * s, 0) + vec.set(tx + charWidth * s, ty + charHeight * s, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u + uSize, v) - GL11.glVertex3d(tx + charWidth * s, ty - dh, 0) + vec.set(tx + charWidth * s, ty - dh, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2d(u, v) - GL11.glVertex3d(tx - dw, ty - dh, 0) + vec.set(tx - dw, ty - dh, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) } override protected def generateChar(char: Char) {} diff --git a/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala index d164830208..fbd11495cf 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala @@ -1,10 +1,13 @@ package li.cil.oc.client.renderer.font +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.util.PackedColor import li.cil.oc.util.RenderState import li.cil.oc.util.TextBuffer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.math.vector.Matrix4f +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 /** @@ -26,19 +29,19 @@ abstract class TextureFontRenderer { * be generated inside the draw call. */ def generateChars(chars: Array[Char]) { - GlStateManager.enableTexture2D() + RenderSystem.enableTexture() for (char <- chars) { generateChar(char) } } - def drawBuffer(buffer: TextBuffer, viewportWidth: Int, viewportHeight: Int) { + def drawBuffer(stack: MatrixStack, buffer: TextBuffer, viewportWidth: Int, viewportHeight: Int) { val format = buffer.format - GlStateManager.pushMatrix() + stack.pushPose() RenderState.pushAttrib() - GlStateManager.scale(0.5f, 0.5f, 1) + stack.scale(0.5f, 0.5f, 1) GL11.glDepthMask(false) RenderState.makeItBlend() @@ -56,14 +59,14 @@ abstract class TextureFontRenderer { var width = 0 for (col <- color.map(PackedColor.unpackBackground(_, format)) if x + width < viewportWidth) { if (col != cbg) { - drawQuad(cbg, x, y, width) + drawQuad(stack.last.pose, cbg, x, y, width) cbg = col x += width width = 0 } width = width + 1 } - drawQuad(cbg, x, y, width) + drawQuad(stack.last.pose, cbg, x, y, width) } GL11.glEnd() @@ -99,7 +102,7 @@ abstract class TextureFontRenderer { } // Don't render whitespace. if (ch != ' ') { - drawChar(tx, ty, ch) + drawChar(stack.last.pose, tx, ty, ch) } tx += charWidth } @@ -109,23 +112,23 @@ abstract class TextureFontRenderer { RenderState.checkError(getClass.getName + ".drawBuffer: foreground") - GlStateManager.bindTexture(0) + RenderSystem.bindTexture(0) GL11.glDepthMask(true) GL11.glColor3f(1, 1, 1) RenderState.disableBlend() RenderState.popAttrib() - GlStateManager.popMatrix() + stack.popPose() RenderState.checkError(getClass.getName + ".drawBuffer: leaving") } - def drawString(s: String, x: Int, y: Int): Unit = { - GlStateManager.pushMatrix() + def drawString(stack: MatrixStack, s: String, x: Int, y: Int): Unit = { + stack.pushPose() RenderState.pushAttrib() - GlStateManager.translate(x, y, 0) - GlStateManager.scale(0.5f, 0.5f, 1) - GlStateManager.depthMask(false) + stack.translate(x, y, 0) + stack.scale(0.5f, 0.5f, 1) + RenderSystem.depthMask(false) for (i <- 0 until textureCount) { bindTexture(i) @@ -135,7 +138,7 @@ abstract class TextureFontRenderer { val ch = s.charAt(n) // Don't render whitespace. if (ch != ' ') { - drawChar(tx, 0, ch) + drawChar(stack.last.pose, tx, 0, ch) } tx += charWidth } @@ -143,8 +146,8 @@ abstract class TextureFontRenderer { } RenderState.popAttrib() - GlStateManager.popMatrix() - GlStateManager.color(1, 1, 1) + stack.popPose() + RenderSystem.color3f(1, 1, 1) } protected def charWidth: Int @@ -157,20 +160,28 @@ abstract class TextureFontRenderer { protected def generateChar(char: Char): Unit - protected def drawChar(tx: Float, ty: Float, char: Char): Unit + protected def drawChar(matrix: Matrix4f, tx: Float, ty: Float, char: Char): Unit - private def drawQuad(color: Int, x: Int, y: Int, width: Int) = if (color != 0 && width > 0) { + private def drawQuad(matrix: Matrix4f, color: Int, x: Int, y: Int, width: Int) = if (color != 0 && width > 0) { val x0 = x * charWidth val x1 = (x + width) * charWidth val y0 = y * charHeight val y1 = (y + 1) * charHeight - GlStateManager.color( + RenderSystem.color3f( ((color >> 16) & 0xFF) / 255f, ((color >> 8) & 0xFF) / 255f, (color & 0xFF) / 255f) - GL11.glVertex3d(x0, y1, 0) - GL11.glVertex3d(x1, y1, 0) - GL11.glVertex3d(x1, y0, 0) - GL11.glVertex3d(x0, y0, 0) + val vec = new Vector4f(x0, y1, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(x1, y1, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(x1, y0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(x0, y0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala index 54023d9383..75f5198af6 100644 --- a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala @@ -1,10 +1,10 @@ package li.cil.oc.client.renderer.gui +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.Textures import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GLAllocation -import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.texture.TextureManager import org.lwjgl.opengl.GL11 @@ -21,7 +21,7 @@ object BufferRenderer { RenderState.checkError(getClass.getName + ".displayLists: entering (aka: wasntme)") textureManager = Some(tm) - displayLists = GLAllocation.generateDisplayLists(2) + displayLists = GL11.glGenLists(2) RenderState.checkError(getClass.getName + ".displayLists: leaving") }) @@ -87,12 +87,12 @@ object BufferRenderer { GL11.glCallList(displayLists) } - def drawText(screen: api.internal.TextBuffer) = + def drawText(stack: MatrixStack, screen: api.internal.TextBuffer) = if (textureManager.isDefined) { RenderState.pushAttrib() - GlStateManager.depthMask(false) - val changed = screen.renderText() - GlStateManager.depthMask(true) + RenderSystem.depthMask(false) + val changed = screen.renderText(stack) + RenderSystem.depthMask(true) RenderState.popAttrib() changed } diff --git a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala index 2f5285d164..b52b40e7a1 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala @@ -1,36 +1,39 @@ package li.cil.oc.client.renderer.item +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Settings import li.cil.oc.util.RenderState -import net.minecraft.client.model.ModelBase -import net.minecraft.client.model.ModelBiped -import net.minecraft.client.model.ModelRenderer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.model.Model +import net.minecraft.client.renderer.model.ModelRenderer +import net.minecraft.client.renderer.entity.model.BipedModel import net.minecraft.client.renderer.RenderHelper import net.minecraft.entity.Entity +import net.minecraft.entity.LivingEntity import net.minecraft.util.ResourceLocation import org.lwjgl.opengl.GL11 -object HoverBootRenderer extends ModelBiped { +object HoverBootRenderer extends BipedModel[LivingEntity](0.5f) { val texture = new ResourceLocation(Settings.resourceDomain, "textures/model/drone.png") - val bootLeft = new ModelRenderer(this, "bootLeft") - val bootRight = new ModelRenderer(this, "bootRight") - val body = new ModelRenderer(this, "body") - val wing0 = new ModelRenderer(this, "wing0") - val wing1 = new ModelRenderer(this, "wing1") - val wing2 = new ModelRenderer(this, "wing2") - val wing3 = new ModelRenderer(this, "wing3") - val light0 = new LightModelRenderer(this, "light0") - val light1 = new LightModelRenderer(this, "light1") - val light2 = new LightModelRenderer(this, "light2") - val light3 = new LightModelRenderer(this, "light3") - - bootLeft.addChild(body) + val bootLeft = new ModelRenderer(this) + val bootRight = new ModelRenderer(this) + val droneBody = new ModelRenderer(this) + val wing0 = new ModelRenderer(this) + val wing1 = new ModelRenderer(this) + val wing2 = new ModelRenderer(this) + val wing3 = new ModelRenderer(this) + val light0 = new LightModelRenderer(this) + val light1 = new LightModelRenderer(this) + val light2 = new LightModelRenderer(this) + val light3 = new LightModelRenderer(this) + + bootLeft.addChild(droneBody) bootLeft.addChild(wing0) bootLeft.addChild(wing1) - bootRight.addChild(body) + bootRight.addChild(droneBody) bootRight.addChild(wing2) bootRight.addChild(wing3) @@ -39,88 +42,69 @@ object HoverBootRenderer extends ModelBiped { wing2.addChild(light2) wing3.addChild(light3) - textureWidth = 64 - textureHeight = 32 - - setTextureOffset("body.middle", 0, 23) - setTextureOffset("body.top", 0, 1) - setTextureOffset("body.bottom", 0, 17) - setTextureOffset("wing0.flap0", 0, 9) - setTextureOffset("wing0.pin0", 0, 27) - setTextureOffset("wing1.flap1", 0, 9) - setTextureOffset("wing1.pin1", 0, 27) - setTextureOffset("wing2.flap2", 0, 9) - setTextureOffset("wing2.pin2", 0, 27) - setTextureOffset("wing3.flap3", 0, 9) - setTextureOffset("wing3.pin3", 0, 27) - - setTextureOffset("light0.flap0", 24, 0) - setTextureOffset("light1.flap1", 24, 0) - setTextureOffset("light2.flap2", 24, 0) - setTextureOffset("light3.flap3", 24, 0) - - bootRight.offsetY = 10.1f / 16 - bootLeft.offsetY = 10.11f / 16f - - body.addBox("top", -3, 1, -3, 6, 1, 6).rotateAngleY = math.toRadians(45).toFloat - body.addBox("middle", -1, 0, -1, 2, 1, 2).rotateAngleY = math.toRadians(45).toFloat - body.addBox("bottom", -2, -1, -2, 4, 1, 4).rotateAngleY = math.toRadians(45).toFloat - wing0.addBox("flap0", -1, 0, -7, 6, 1, 6) - wing0.addBox("pin0", 0, -1, -3, 1, 3, 1) - wing1.addBox("flap1", -1, 0, 1, 6, 1, 6) - wing1.addBox("pin1", 0, -1, 2, 1, 3, 1) - wing2.addBox("flap2", -5, 0, 1, 6, 1, 6) - wing2.addBox("pin2", -1, -1, 2, 1, 3, 1) - wing3.addBox("flap3", -5, 0, -7, 6, 1, 6) - wing3.addBox("pin3", -1, -1, -3, 1, 3, 1) - - light0.addBox("flap0", -1, 0, -7, 6, 1, 6) - light1.addBox("flap1", -1, 0, 1, 6, 1, 6) - light2.addBox("flap2", -5, 0, 1, 6, 1, 6) - light3.addBox("flap3", -5, 0, -7, 6, 1, 6) + texWidth = 64 + texHeight = 32 + + bootRight.y = 10.1f / 16 + bootLeft.y = 10.11f / 16f + + droneBody.texOffs(0, 23).addBox(-3, 1, -3, 6, 1, 6).yRot = math.toRadians(45).toFloat // top + droneBody.texOffs(0, 1).addBox(-1, 0, -1, 2, 1, 2).yRot = math.toRadians(45).toFloat // middle + droneBody.texOffs(0, 17).addBox(-2, -1, -2, 4, 1, 4).yRot = math.toRadians(45).toFloat // bottom + wing0.texOffs(0, 9).addBox(-1, 0, -7, 6, 1, 6) // flap0 + wing0.texOffs(0, 27).addBox(0, -1, -3, 1, 3, 1) // pin0 + wing1.texOffs(0, 9).addBox(-1, 0, 1, 6, 1, 6) // flap1 + wing1.texOffs(0, 27).addBox(0, -1, 2, 1, 3, 1) // pin1 + wing2.texOffs(0, 9).addBox(-5, 0, 1, 6, 1, 6) // flap2 + wing2.texOffs(0, 27).addBox(-1, -1, 2, 1, 3, 1) // pin2 + wing3.texOffs(0, 9).addBox(-5, 0, -7, 6, 1, 6) // flap3 + wing3.texOffs(0, 27).addBox(-1, -1, -3, 1, 3, 1) // pin3 + + light0.texOffs(24, 0).addBox(-1, 0, -7, 6, 1, 6) // flap0 + light1.texOffs(24, 0).addBox(-1, 0, 1, 6, 1, 6) // flap1 + light2.texOffs(24, 0).addBox(-5, 0, 1, 6, 1, 6) // flap2 + light3.texOffs(24, 0).addBox(-5, 0, -7, 6, 1, 6) // flap3 // No drone textured legs, thank you very much. - bipedLeftLeg.cubeList.clear() - bipedRightLeg.cubeList.clear() + leftLeg = leftLeg.createShallowCopy() + rightLeg = rightLeg.createShallowCopy() - bipedLeftLeg.addChild(bootLeft) - bipedRightLeg.addChild(bootRight) + leftLeg.addChild(bootLeft) + rightLeg.addChild(bootRight) - bipedHead.isHidden = true - bipedHeadwear.isHidden = true - bipedBody.isHidden = true - bipedRightArm.isHidden = true - bipedLeftArm.isHidden = true + head.visible = false + hat.visible = false + body.visible = false + rightArm.visible = false + leftArm.visible = false var lightColor = 0x66DD55 - override def render(entity: Entity, f0: Float, f1: Float, f2: Float, f3: Float, f4: Float, f5: Float): Unit = { + override def setupAnim(entity: LivingEntity, f1: Float, f2: Float, f3: Float, f4: Float, f5: Float): Unit = { + super.setupAnim(entity, f1, f2, f3, f4, f5) // Because Forge is being a dummy... - isSneak = entity.isSneaking + crouching = entity.isCrouching // Because Forge is being an even bigger dummy... - isChild = false - super.render(entity, f0, f1, f2, f3, f4, f5) + young = false } - class LightModelRenderer(modelBase: ModelBase, name: String) extends ModelRenderer(modelBase, name) { - override def render(dt: Float): Unit = { + class LightModelRenderer(modelBase: Model) extends ModelRenderer(modelBase) { + override def render(stack: MatrixStack, builder: IVertexBuilder, light: Int, overlay: Int, r: Float, g: Float, b: Float, a: Float): Unit = { RenderState.pushAttrib() - GlStateManager.disableLighting() + RenderSystem.disableLighting() RenderState.disableEntityLighting() - GlStateManager.depthFunc(GL11.GL_LEQUAL) + RenderSystem.depthFunc(GL11.GL_LEQUAL) RenderState.makeItBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - val r = ((lightColor >>> 16) & 0xFF) / 255f - val g = ((lightColor >>> 8) & 0xFF) / 255f - val b = ((lightColor >>> 0) & 0xFF) / 255f - GlStateManager.color(r, g, b) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + val rm = ((lightColor >>> 16) & 0xFF) / 255f + val gm = ((lightColor >>> 8) & 0xFF) / 255f + val bm = ((lightColor >>> 0) & 0xFF) / 255f - super.render(dt) + super.render(stack, builder, light, overlay, r * rm, g * gm, b * bm, a) RenderState.disableBlend() - GlStateManager.enableLighting() + RenderSystem.enableLighting() RenderState.enableEntityLighting() - GlStateManager.color(1, 1, 1) RenderState.popAttrib() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala index e4dd1512b8..89d5e23176 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala @@ -1,5 +1,8 @@ package li.cil.oc.client.renderer.item +import com.google.common.collect.ImmutableList +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Constants import li.cil.oc.api import li.cil.oc.api.driver.item.UpgradeRenderer.MountPointName @@ -7,14 +10,21 @@ import li.cil.oc.api.event.RobotRenderEvent.MountPoint import li.cil.oc.client.Textures import li.cil.oc.integration.opencomputers.Item import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats +import net.minecraft.client.renderer.vertex.VertexFormat import net.minecraft.item.ItemStack -import net.minecraft.util.math.AxisAlignedBB +import net.minecraft.util.math.vector.Quaternion import org.lwjgl.opengl.GL11 object UpgradeRenderer { + final val POSITION_TEX_NORMAL = new VertexFormat(ImmutableList.builder() + .add(DefaultVertexFormats.ELEMENT_POSITION) + .add(DefaultVertexFormats.ELEMENT_UV0) + .add(DefaultVertexFormats.ELEMENT_NORMAL) + .add(DefaultVertexFormats.ELEMENT_PADDING) + .build()) + lazy val craftingUpgrade = api.Items.get(Constants.ItemName.CraftingUpgrade) lazy val generatorUpgrade = api.Items.get(Constants.ItemName.GeneratorUpgrade) lazy val inventoryUpgrade = api.Items.get(Constants.ItemName.InventoryUpgrade) @@ -36,71 +46,72 @@ object UpgradeRenderer { descriptor == craftingUpgrade || descriptor == generatorUpgrade || descriptor == inventoryUpgrade } - def render(stack: ItemStack, mountPoint: MountPoint): Unit = { + def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint): Unit = { val descriptor = api.Items.get(stack) if (descriptor == api.Items.get(Constants.ItemName.CraftingUpgrade)) { Textures.bind(Textures.Model.UpgradeCrafting) - drawSimpleBlock(mountPoint) + drawSimpleBlock(matrix, mountPoint) RenderState.checkError(getClass.getName + ".renderItem: crafting upgrade") } else if (descriptor == api.Items.get(Constants.ItemName.GeneratorUpgrade)) { Textures.bind(Textures.Model.UpgradeGenerator) - drawSimpleBlock(mountPoint, if (Item.dataTag(stack).getInteger("remainingTicks") > 0) 0.5f else 0) + drawSimpleBlock(matrix, mountPoint, if (Item.dataTag(stack).getInt("remainingTicks") > 0) 0.5f else 0) RenderState.checkError(getClass.getName + ".renderItem: generator upgrade") } else if (descriptor == api.Items.get(Constants.ItemName.InventoryUpgrade)) { Textures.bind(Textures.Model.UpgradeInventory) - drawSimpleBlock(mountPoint) + drawSimpleBlock(matrix, mountPoint) RenderState.checkError(getClass.getName + ".renderItem: inventory upgrade") } } - private val bounds = new AxisAlignedBB(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1) + private val (minX, minY, minZ) = (-0.1f, -0.1f, -0.1f) + private val (maxX, maxY, maxZ) = (0.1f, 0.1f, 0.1f) - private def drawSimpleBlock(mountPoint: MountPoint, frontOffset: Float = 0) { - GlStateManager.rotate(mountPoint.rotation.getW, mountPoint.rotation.getX, mountPoint.rotation.getY, mountPoint.rotation.getZ) - GlStateManager.translate(mountPoint.offset.getX, mountPoint.offset.getY, mountPoint.offset.getZ) + private def drawSimpleBlock(stack: MatrixStack, mountPoint: MountPoint, frontOffset: Float = 0) { + stack.mulPose(new Quaternion(mountPoint.rotation.w, mountPoint.rotation.x, mountPoint.rotation.y, mountPoint.rotation.z)) + stack.translate(mountPoint.offset.x, mountPoint.offset.y, mountPoint.offset.z) val t = Tessellator.getInstance() - val r = t.getBuffer - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_NORMAL) + val r = t.getBuilder + r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) // Front. - r.pos(bounds.minX, bounds.minY, bounds.maxZ).tex(frontOffset, 0.5f).normal(0, 0, 1).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.maxZ).tex(frontOffset + 0.5f, 0.5f).normal(0, 0, 1).endVertex() - r.pos(bounds.maxX, bounds.maxY, bounds.maxZ).tex(frontOffset + 0.5f, 0).normal(0, 0, 1).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.maxZ).tex(frontOffset, 0).normal(0, 0, 1).endVertex() + r.vertex(stack.last.pose, minX, minY, maxZ).uv(frontOffset, 0.5f).normal(stack.last.normal, 0, 0, 1).endVertex() + r.vertex(stack.last.pose, maxX, minY, maxZ).uv(frontOffset + 0.5f, 0.5f).normal(stack.last.normal, 0, 0, 1).endVertex() + r.vertex(stack.last.pose, maxX, maxY, maxZ).uv(frontOffset + 0.5f, 0).normal(stack.last.normal, 0, 0, 1).endVertex() + r.vertex(stack.last.pose, minX, maxY, maxZ).uv(frontOffset, 0).normal(stack.last.normal, 0, 0, 1).endVertex() // Top. - r.pos(bounds.maxX, bounds.maxY, bounds.maxZ).tex(1, 0.5f).normal(0, 1, 0).endVertex() - r.pos(bounds.maxX, bounds.maxY, bounds.minZ).tex(1, 1).normal(0, 1, 0).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.minZ).tex(0.5f, 1).normal(0, 1, 0).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.maxZ).tex(0.5f, 0.5f).normal(0, 1, 0).endVertex() + r.vertex(stack.last.pose, maxX, maxY, maxZ).uv(1, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, maxX, maxY, minZ).uv(1, 1).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, minX, maxY, minZ).uv(0.5f, 1).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, minX, maxY, maxZ).uv(0.5f, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() // Bottom. - r.pos(bounds.minX, bounds.minY, bounds.maxZ).tex(0.5f, 0.5f).normal(0, -1, 0).endVertex() - r.pos(bounds.minX, bounds.minY, bounds.minZ).tex(0.5f, 1).normal(0, -1, 0).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.minZ).tex(1, 1).normal(0, -1, 0).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.maxZ).tex(1, 0.5f).normal(0, -1, 0).endVertex() + r.vertex(stack.last.pose, minX, minY, maxZ).uv(0.5f, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, minX, minY, minZ).uv(0.5f, 1).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, maxX, minY, minZ).uv(1, 1).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, maxX, minY, maxZ).uv(1, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() // Left. - r.pos(bounds.maxX, bounds.maxY, bounds.maxZ).tex(0, 0.5f).normal(1, 0, 0).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.maxZ).tex(0, 1).normal(1, 0, 0).endVertex() - r.pos(bounds.maxX, bounds.minY, bounds.minZ).tex(0.5f, 1).normal(1, 0, 0).endVertex() - r.pos(bounds.maxX, bounds.maxY, bounds.minZ).tex(0.5f, 0.5f).normal(1, 0, 0).endVertex() + r.vertex(stack.last.pose, maxX, maxY, maxZ).uv(0, 0.5f).normal(stack.last.normal, 1, 0, 0).endVertex() + r.vertex(stack.last.pose, maxX, minY, maxZ).uv(0, 1).normal(stack.last.normal, 1, 0, 0).endVertex() + r.vertex(stack.last.pose, maxX, minY, minZ).uv(0.5f, 1).normal(stack.last.normal, 1, 0, 0).endVertex() + r.vertex(stack.last.pose, maxX, maxY, minZ).uv(0.5f, 0.5f).normal(stack.last.normal, 1, 0, 0).endVertex() // Right. - r.pos(bounds.minX, bounds.minY, bounds.maxZ).tex(0, 1).normal(-1, 0, 0).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.maxZ).tex(0, 0.5f).normal(-1, 0, 0).endVertex() - r.pos(bounds.minX, bounds.maxY, bounds.minZ).tex(0.5f, 0.5f).normal(-1, 0, 0).endVertex() - r.pos(bounds.minX, bounds.minY, bounds.minZ).tex(0.5f, 1).normal(-1, 0, 0).endVertex() + r.vertex(stack.last.pose, minX, minY, maxZ).uv(0, 1).normal(stack.last.normal, -1, 0, 0).endVertex() + r.vertex(stack.last.pose, minX, maxY, maxZ).uv(0, 0.5f).normal(stack.last.normal, -1, 0, 0).endVertex() + r.vertex(stack.last.pose, minX, maxY, minZ).uv(0.5f, 0.5f).normal(stack.last.normal, -1, 0, 0).endVertex() + r.vertex(stack.last.pose, minX, minY, minZ).uv(0.5f, 1).normal(stack.last.normal, -1, 0, 0).endVertex() - t.draw() + t.end() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala index 82ca004ff6..4624f80a6b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala @@ -1,12 +1,14 @@ package li.cil.oc.client.renderer.markdown +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.renderer.markdown.segment.InteractiveSegment import li.cil.oc.client.renderer.markdown.segment.Segment import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 import scala.collection.Iterable @@ -61,14 +63,14 @@ object Document { /** * Line height for a normal line of text. */ - def lineHeight(renderer: FontRenderer): Int = renderer.FONT_HEIGHT + 1 + def lineHeight(renderer: FontRenderer): Int = renderer.lineHeight + 1 /** * Renders a list of segments and tooltips if a segment with a tooltip is hovered. * Returns the hovered interactive segment, if any. */ - def render(document: Segment, x: Int, y: Int, maxWidth: Int, maxHeight: Int, yOffset: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { - val mc = Minecraft.getMinecraft + def render(stack: MatrixStack, document: Segment, x: Int, y: Int, maxWidth: Int, maxHeight: Int, yOffset: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { + val window = Minecraft.getInstance.getWindow RenderState.pushAttrib() @@ -76,30 +78,46 @@ object Document { // depth buffer correctly if alpha test is enabled. Guess how we found out? // By noticing that on those systems it only worked while chat messages // were visible. Yeah. I know. - GlStateManager.disableAlpha() + RenderSystem.disableAlphaTest() // Clear depth mask, then create masks in foreground above and below scroll area. - GlStateManager.color(1, 1, 1, 1) - GlStateManager.clear(GL11.GL_DEPTH_BUFFER_BIT) - GlStateManager.enableDepth() - GlStateManager.depthFunc(GL11.GL_LEQUAL) - GlStateManager.depthMask(true) - GlStateManager.colorMask(false, false, false, false) - - GlStateManager.pushMatrix() - GlStateManager.translate(0, 0, 500) + RenderSystem.color4f(1, 1, 1, 1) + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, false) + RenderSystem.enableDepthTest() + RenderSystem.depthFunc(GL11.GL_LEQUAL) + RenderSystem.depthMask(true) + RenderSystem.colorMask(false, false, false, false) + + stack.pushPose() + stack.translate(0, 0, 500) GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex2f(0, y) - GL11.glVertex2f(mc.displayWidth, y) - GL11.glVertex2f(mc.displayWidth, 0) - GL11.glVertex2f(0, 0) - GL11.glVertex2f(0, mc.displayHeight) - GL11.glVertex2f(mc.displayWidth, mc.displayHeight) - GL11.glVertex2f(mc.displayWidth, y + maxHeight) - GL11.glVertex2f(0, y + maxHeight) + val vec = new Vector4f(0, y, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(window.getGuiScaledWidth, y, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(window.getGuiScaledWidth, 0, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(0, 0, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(0, window.getGuiScaledHeight, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(window.getGuiScaledWidth, window.getGuiScaledHeight, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(window.getGuiScaledWidth, y + maxHeight, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(0, y + maxHeight, 0, 1) + vec.transform(stack.last.pose) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glEnd() - GlStateManager.popMatrix() - GlStateManager.colorMask(true, true, true, true) + stack.popPose() + RenderSystem.colorMask(true, true, true, true) // Actual rendering. var hovered: Option[InteractiveSegment] = None @@ -111,7 +129,7 @@ object Document { while (segment != null) { val segmentHeight = segment.nextY(indent, maxWidth, renderer) if (currentY + segmentHeight >= minY && currentY <= maxY) { - val result = segment.render(x, currentY, indent, maxWidth, renderer, mouseX, mouseY) + val result = segment.render(stack, x, currentY, indent, maxWidth, renderer, mouseX, mouseY) hovered = hovered.orElse(result) } currentY += segmentHeight @@ -122,7 +140,7 @@ object Document { hovered.foreach(_.notifyHover()) RenderState.popAttrib() - GlStateManager.bindTexture(0) + RenderSystem.bindTexture(0) hovered } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala index 365577fe4a..c7a094e00d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala @@ -78,5 +78,5 @@ trait BasicTextSegment extends Segment { pos } - protected def computeWrapIndent(renderer: FontRenderer) = if (lists.contains(rootPrefix)) renderer.getStringWidth(rootPrefix) else 0 + protected def computeWrapIndent(renderer: FontRenderer) = if (lists.contains(rootPrefix)) renderer.width(rootPrefix) else 0 } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala index 692019483f..9b63146654 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala @@ -1,12 +1,13 @@ package li.cil.oc.client.renderer.markdown.segment +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.renderer.GlStateManager private[markdown] class CodeSegment(val parent: Segment, val text: String) extends BasicTextSegment { - override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { + override def render(stack: MatrixStack, x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { TextBufferRenderCache.renderer.generateChars(text.toCharArray) var currentX = x + indent @@ -16,8 +17,8 @@ private[markdown] class CodeSegment(val parent: Segment, val text: String) exten var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer) while (chars.length > 0) { val part = chars.take(numChars) - GlStateManager.color(0.75f, 0.8f, 1, 1) - TextBufferRenderCache.renderer.drawString(part, currentX, currentY) + RenderSystem.color4f(0.75f, 0.8f, 1, 1) + TextBufferRenderCache.renderer.drawString(stack, part, currentX, currentY) currentX = x + wrapIndent currentY += lineHeight(renderer) chars = chars.drop(numChars).dropWhile(_.isWhitespace) diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala index 84af300415..c105849cc3 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala @@ -8,6 +8,7 @@ import li.cil.oc.api import li.cil.oc.client.Manual import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.Minecraft +import net.minecraft.util.Util private[markdown] class LinkSegment(parent: Segment, text: String, val url: String) extends TextSegment(parent, text) with InteractiveSegment { private final val normalColor = 0x66FF66 @@ -52,7 +53,7 @@ private[markdown] class LinkSegment(parent: Segment, text: String, val url: Stri desktop.getMethod("browse", classOf[URI]).invoke(instance, new URI(url)) } catch { - case t: Throwable => Minecraft.getMinecraft.player.sendMessage(Localization.Chat.WarningLink(t.toString)) + case t: Throwable => Minecraft.getInstance.player.sendMessage(Localization.Chat.WarningLink(t.toString), Util.NIL_UUID) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala index 4f484c3548..a106ed6726 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala @@ -1,11 +1,14 @@ package li.cil.oc.client.renderer.markdown.segment +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer import li.cil.oc.client.renderer.markdown.Document import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.math.vector.Matrix4f +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 private[markdown] class RenderSegment(val parent: Segment, val title: String, val imageRenderer: ImageRenderer) extends InteractiveSegment { @@ -32,7 +35,7 @@ private[markdown] class RenderSegment(val parent: Segment, val title: String, va override def nextX(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = 0 - override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { + override def render(stack: MatrixStack, x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { val width = imageWidth(maxWidth) val height = imageHeight(maxWidth) val xOffset = (maxWidth - width) / 2 @@ -44,34 +47,43 @@ private[markdown] class RenderSegment(val parent: Segment, val title: String, va val hovered = checkHovered(mouseX, mouseY, x + xOffset, y + yOffset, width, height) - GlStateManager.pushMatrix() - GlStateManager.translate(x + xOffset, y + yOffset, 0) - GlStateManager.scale(s, s, s) + stack.pushPose() + stack.translate(x + xOffset, y + yOffset, 0) + stack.scale(s, s, s) - GlStateManager.enableBlend() - GlStateManager.enableAlpha() + RenderSystem.enableBlend() + RenderSystem.enableAlphaTest() if (hovered.isDefined) { - GlStateManager.color(1, 1, 1, 0.15f) - GlStateManager.disableTexture2D() + RenderSystem.color4f(1, 1, 1, 0.15f) + RenderSystem.disableTexture() GL11.glBegin(GL11.GL_QUADS) - GL11.glVertex2f(0, 0) - GL11.glVertex2f(0, imageRenderer.getHeight) - GL11.glVertex2f(imageRenderer.getWidth, imageRenderer.getHeight) - GL11.glVertex2f(imageRenderer.getWidth, 0) + val matrix = stack.last.pose + val vec = new Vector4f(0, 0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(0, imageRenderer.getHeight, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(imageRenderer.getWidth, imageRenderer.getHeight, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) + vec.set(imageRenderer.getWidth, 0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glEnd() - GlStateManager.enableTexture2D() + RenderSystem.enableTexture() } - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - imageRenderer.render(mouseX - x, mouseY - y) + imageRenderer.render(stack, mouseX - x, mouseY - y) - GlStateManager.disableBlend() - GlStateManager.disableAlpha() - GlStateManager.disableLighting() + RenderSystem.disableBlend() + RenderSystem.disableAlphaTest() + RenderSystem.disableLighting() - GlStateManager.popMatrix() + stack.popPose() hovered } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala index 645dd1a90a..13a4b59052 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala @@ -1,5 +1,6 @@ package li.cil.oc.client.renderer.markdown.segment +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer @@ -46,7 +47,7 @@ trait Segment { * Render the segment at the specified coordinates with the specified * properties. */ - def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = None + def render(stack: MatrixStack, x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = None def renderAsText(format: MarkupFormat.Value): Iterable[String] = { var segment = this diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala index f89032ba5e..b47ad92679 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala @@ -1,15 +1,16 @@ package li.cil.oc.client.renderer.markdown.segment +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.renderer.markdown.Document import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.renderer.GlStateManager import org.lwjgl.opengl.GL11 import scala.collection.mutable import scala.util.matching.Regex private[markdown] class TextSegment(val parent: Segment, val text: String) extends BasicTextSegment { - override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { + override def render(stack: MatrixStack, x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { var currentX = x + indent var currentY = y var chars = text @@ -20,12 +21,12 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten while (chars.length > 0) { val part = chars.take(numChars) hovered = hovered.orElse(resolvedInteractive.fold(None: Option[InteractiveSegment])(_.checkHovered(mouseX, mouseY, currentX, currentY, stringWidth(part, renderer), (Document.lineHeight(renderer) * resolvedScale).toInt))) - GlStateManager.pushMatrix() - GlStateManager.translate(currentX, currentY, 0) - GlStateManager.scale(resolvedScale, resolvedScale, resolvedScale) - GlStateManager.translate(-currentX, -currentY, 0) - renderer.drawString(resolvedFormat + part, currentX, currentY, resolvedColor) - GlStateManager.popMatrix() + stack.pushPose() + stack.translate(currentX, currentY, 0) + stack.scale(resolvedScale, resolvedScale, resolvedScale) + stack.translate(-currentX, -currentY, 0) + renderer.draw(stack, resolvedFormat + part, currentX, currentY, resolvedColor) + stack.popPose() currentX = x + wrapIndent currentY += lineHeight(renderer) chars = chars.drop(numChars).dropWhile(_.isWhitespace) @@ -65,7 +66,7 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten override protected def lineHeight(renderer: FontRenderer): Int = (super.lineHeight(renderer) * resolvedScale).toInt - override protected def stringWidth(s: String, renderer: FontRenderer): Int = (renderer.getStringWidth(resolvedFormat + s) * resolvedScale).toInt + override protected def stringWidth(s: String, renderer: FontRenderer): Int = (renderer.width(resolvedFormat + s) * resolvedScale).toInt // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala index 8314464fc9..59eb8554fa 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala @@ -9,14 +9,19 @@ import net.minecraft.block.Block import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation +import net.minecraftforge.registries.ForgeRegistries object BlockImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { val splitIndex = data.lastIndexOf('@') val (name, optMeta) = if (splitIndex > 0) data.splitAt(splitIndex) else (data, "") val meta = if (Strings.isNullOrEmpty(optMeta)) 0 else Integer.parseInt(optMeta.drop(1)) - Block.REGISTRY.getObject(new ResourceLocation(name)) match { - case block: Block if Item.getItemFromBlock(block) != null => new ItemStackImageRenderer(Array(new ItemStack(block, 1, meta))) + ForgeRegistries.BLOCKS.getValue(new ResourceLocation(name)) match { + case block: Block if block.asItem() != null => { + val stack = new ItemStack(block, 1) + if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) + new ItemStackImageRenderer(Array(stack)) + } case _ => new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.BlockMissing" diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala index f0ae718de0..611028639c 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala @@ -8,14 +8,18 @@ import li.cil.oc.client.Textures import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation +import net.minecraftforge.registries.ForgeRegistries object ItemImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { val splitIndex = data.lastIndexOf('@') val (name, optMeta) = if (splitIndex > 0) data.splitAt(splitIndex) else (data, "") - val meta = if (Strings.isNullOrEmpty(optMeta)) 0 else Integer.parseInt(optMeta.drop(1)) - Item.REGISTRY.getObject(new ResourceLocation(name)) match { - case item: Item => new ItemStackImageRenderer(Array(new ItemStack(item, 1, meta))) + ForgeRegistries.ITEMS.getValue(new ResourceLocation(name)) match { + case item: Item => { + val stack = new ItemStack(item, 1) + if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) + new ItemStackImageRenderer(Array(stack)) + } case _ => new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.ItemMissing" diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala index 277591b0ff..c73a967d55 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala @@ -1,13 +1,14 @@ package li.cil.oc.client.renderer.markdown.segment.render +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api.manual.ImageRenderer import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.OpenGlHelper import net.minecraft.client.renderer.RenderHelper import net.minecraft.item.ItemStack import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL12 +import org.lwjgl.opengl.GL13 private[markdown] class ItemStackImageRenderer(val stacks: Array[ItemStack]) extends ImageRenderer { // How long to show individual stacks, in milliseconds, before switching to the next. @@ -17,16 +18,14 @@ private[markdown] class ItemStackImageRenderer(val stacks: Array[ItemStack]) ext override def getHeight = 32 - override def render(mouseX: Int, mouseY: Int): Unit = { - val mc = Minecraft.getMinecraft + override def render(matrix: MatrixStack, mouseX: Int, mouseY: Int): Unit = { + val mc = Minecraft.getInstance val index = (System.currentTimeMillis() % (cycleSpeed * stacks.length)).toInt / cycleSpeed val stack = stacks(index) - GlStateManager.scale(getWidth / 16, getHeight / 16, getWidth / 16) - GlStateManager.enableRescaleNormal() - RenderHelper.enableGUIStandardItemLighting() - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240) - mc.getRenderItem.renderItemAndEffectIntoGUI(stack, 0, 0) - RenderHelper.disableStandardItemLighting() + matrix.scale(getWidth / 16, getHeight / 16, getWidth / 16) + RenderSystem.enableRescaleNormal() + RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240, 240) + mc.getItemRenderer.renderAndDecorateItem(stack, 0, 0) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala index 0bad3914f9..8433df34e3 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala @@ -4,14 +4,24 @@ import li.cil.oc.api.manual.ImageProvider import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer import li.cil.oc.client.Textures -import net.minecraftforge.oredict.OreDictionary +import net.minecraft.item.ItemStack +import net.minecraft.tags._ +import net.minecraft.util.ResourceLocation +import scala.collection.mutable import scala.collection.convert.WrapAsScala._ object OreDictImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { - val stacks = OreDictionary.getOres(data).filter(stack => !stack.isEmpty && stack.getItem != null) - if (stacks != null && stacks.nonEmpty) new ItemStackImageRenderer(stacks.toArray) + val desired = new ResourceLocation(data) + val stacks = mutable.ArrayBuffer.empty[ItemStack] + ItemTags.getWrappers.find(t => desired.equals(t.getName)).foreach { + stacks ++= _.getValues.map(new ItemStack(_)) + } + ItemTags.getWrappers.find(t => desired.equals(t.getName)).foreach { + stacks ++= _.getValues.map(new ItemStack(_)) + } + if (stacks.nonEmpty) new ItemStackImageRenderer(stacks.toArray) else new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.OreDictMissing" diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala index 4af762c091..7c32bac6e0 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala @@ -3,27 +3,29 @@ package li.cil.oc.client.renderer.markdown.segment.render import java.io.InputStream import javax.imageio.ImageIO +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.client.Textures import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.texture.AbstractTexture +import net.minecraft.client.renderer.texture.Texture import net.minecraft.client.renderer.texture.TextureUtil -import net.minecraft.client.resources.IResourceManager +import net.minecraft.resources.IResourceManager import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.vector.Matrix4f +import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 +import org.lwjgl.system.MemoryUtil class TextureImageRenderer(val location: ResourceLocation) extends ImageRenderer { private val texture = { - val manager = Minecraft.getMinecraft.getTextureManager + val manager = Minecraft.getInstance.getTextureManager manager.getTexture(location) match { case image: ImageTexture => image case other => - if (other != null && other.getGlTextureId != -1) { - TextureUtil.deleteTexture(other.getGlTextureId) - } + other.releaseId() val image = new ImageTexture(location) - manager.loadTexture(location, image) + manager.register(location, image) image } } @@ -32,34 +34,51 @@ class TextureImageRenderer(val location: ResourceLocation) extends ImageRenderer override def getHeight: Int = texture.height - override def render(mouseX: Int, mouseY: Int): Unit = { + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int): Unit = { Textures.bind(location) - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) GL11.glBegin(GL11.GL_QUADS) GL11.glTexCoord2f(0, 0) - GL11.glVertex2f(0, 0) + val matrix = stack.last.pose + val vec = new Vector4f(0, 0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2f(0, 1) - GL11.glVertex2f(0, texture.height) + vec.set(0, texture.height, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2f(1, 1) - GL11.glVertex2f(texture.width, texture.height) + vec.set(texture.width, texture.height, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glTexCoord2f(1, 0) - GL11.glVertex2f(texture.width, 0) + vec.set(texture.width, 0, 0, 1) + vec.transform(matrix) + GL11.glVertex3f(vec.x, vec.y, vec.z) GL11.glEnd() } - private class ImageTexture(val location: ResourceLocation) extends AbstractTexture { + private class ImageTexture(val location: ResourceLocation) extends Texture { var width = 0 var height = 0 - override def loadTexture(manager: IResourceManager): Unit = { - deleteGlTexture() + override def load(manager: IResourceManager): Unit = { + releaseId() var is: InputStream = null try { val resource = manager.getResource(location) is = resource.getInputStream val bi = ImageIO.read(is) - TextureUtil.uploadTextureImageAllocate(getGlTextureId, bi, false, false) + bind() + val data = MemoryUtil.memAllocInt(bi.getWidth * bi.getHeight) + val tempArr = Array.ofDim[Int]((1024 * 1024) min data.capacity) + val dy = tempArr.length / bi.getWidth + for (y0 <- 0 until data.capacity by dy) { + bi.getRGB(0, y0, bi.getWidth, dy max (bi.getHeight - y0), tempArr, 0, bi.getWidth) + data.put(tempArr, 0, bi.getWidth * dy) + } + TextureUtil.initTexture(data, bi.getWidth, bi.getHeight) width = bi.getWidth height = bi.getHeight } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala index 9b5417e132..03e7bf1c5d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala @@ -1,18 +1,28 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.texture.TextureMap -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.texture.AtlasTexture +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import org.lwjgl.opengl.GL11 -object AdapterRenderer extends TileEntitySpecialRenderer[tileentity.Adapter] { - override def render(adapter: tileentity.Adapter, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object AdapterRenderer extends Function[TileEntityRendererDispatcher, AdapterRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new AdapterRenderer(dispatch) +} + +class AdapterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.Adapter](dispatch) { + override def render(adapter: tileentity.Adapter, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (adapter.openSides.contains(true)) { @@ -20,70 +30,70 @@ object AdapterRenderer extends TileEntitySpecialRenderer[tileentity.Adapter] { RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) - bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE) + Minecraft.getInstance().getModelManager().getAtlas(AtlasTexture.LOCATION_BLOCKS).bind() val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val sideActivity = Textures.getSprite(Textures.Block.AdapterOn) - if (adapter.isSideOpen(EnumFacing.DOWN)) { - r.pos(0, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 1, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() - r.pos(1, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() + if (adapter.isSideOpen(Direction.DOWN)) { + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() } - if (adapter.isSideOpen(EnumFacing.UP)) { - r.pos(0, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() + if (adapter.isSideOpen(Direction.UP)) { + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() } - if (adapter.isSideOpen(EnumFacing.NORTH)) { - r.pos(1, 1, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (adapter.isSideOpen(Direction.NORTH)) { + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (adapter.isSideOpen(EnumFacing.SOUTH)) { - r.pos(0, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(1, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (adapter.isSideOpen(Direction.SOUTH)) { + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (adapter.isSideOpen(EnumFacing.WEST)) { - r.pos(0, 1, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(0, 0, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (adapter.isSideOpen(Direction.WEST)) { + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (adapter.isSideOpen(EnumFacing.EAST)) { - r.pos(1, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(1, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (adapter.isSideOpen(Direction.EAST)) { + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala index c8dd054827..fe1180bc70 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala @@ -1,17 +1,26 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Assembler import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -object AssemblerRenderer extends TileEntitySpecialRenderer[Assembler] { +object AssemblerRenderer extends Function[TileEntityRendererDispatcher, AssemblerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new AssemblerRenderer(dispatch) +} - override def render(assembler: Assembler, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +class AssemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Assembler](dispatch) { + override def render(assembler: Assembler, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() @@ -20,55 +29,56 @@ object AssemblerRenderer extends TileEntitySpecialRenderer[Assembler] { RenderState.makeItBlend() RenderState.setBlendAlpha(1) - GlStateManager.pushMatrix() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.pushPose() + + stack.translate(0.5, 0.5, 0.5) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) { val icon = Textures.getSprite(Textures.Block.AssemblerTopOn) - r.pos(-0.5, 0.55, 0.5).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0.5, 0.55, 0.5).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0.5, 0.55, -0.5).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(-0.5, 0.55, -0.5).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, -0.5f, 0.55f, 0.5f).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.55f, 0.5f).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.55f, -0.5f).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, -0.5f, 0.55f, -0.5f).uv(icon.getU0, icon.getV0).endVertex() } - t.draw() + t.end() // TODO Unroll loop to draw all at once? - val indent = 6 / 16f + 0.005 + val indent = 6 / 16f + 0.005f for (i <- 0 until 4) { r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) if (assembler.isAssembling) { val icon = Textures.getSprite(Textures.Block.AssemblerSideAssembling) - r.pos(indent, 0.5, -indent).tex(icon.getInterpolatedU((0.5 - indent) * 16), icon.getMaxV).endVertex() - r.pos(indent, 0.5, indent).tex(icon.getInterpolatedU((0.5 + indent) * 16), icon.getMaxV).endVertex() - r.pos(indent, -0.5, indent).tex(icon.getInterpolatedU((0.5 + indent) * 16), icon.getMinV).endVertex() - r.pos(indent, -0.5, -indent).tex(icon.getInterpolatedU((0.5 - indent) * 16), icon.getMinV).endVertex() + r.vertex(stack.last.pose, indent, 0.5f, -indent).uv(icon.getU((0.5f - indent) * 16), icon.getV1).endVertex() + r.vertex(stack.last.pose, indent, 0.5f, indent).uv(icon.getU((0.5f + indent) * 16), icon.getV1).endVertex() + r.vertex(stack.last.pose, indent, -0.5f, indent).uv(icon.getU((0.5f + indent) * 16), icon.getV0).endVertex() + r.vertex(stack.last.pose, indent, -0.5f, -indent).uv(icon.getU((0.5f - indent) * 16), icon.getV0).endVertex() } { val icon = Textures.getSprite(Textures.Block.AssemblerSideOn) - r.pos(0.5005, 0.5, -0.5).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0.5005, 0.5, 0.5).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0.5005, -0.5, 0.5).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0.5005, -0.5, -0.5).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0.5005f, 0.5f, -0.5f).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0.5005f, 0.5f, 0.5f).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0.5005f, -0.5f, 0.5f).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0.5005f, -0.5f, -0.5f).uv(icon.getU0, icon.getV0).endVertex() } - t.draw() + t.end() - GlStateManager.rotate(90, 0, 1, 0) + stack.mulPose(Vector3f.YP.rotationDegrees(90)) } RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala index 24af1347ae..6a5624ca06 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala @@ -1,19 +1,29 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Case import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.RenderHelper import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -object CaseRenderer extends TileEntitySpecialRenderer[Case] { - override def render(computer: Case, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object CaseRenderer extends Function[TileEntityRendererDispatcher, CaseRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new CaseRenderer(dispatch) +} + +class CaseRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Case](dispatch) { + override def render(computer: Case, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() @@ -22,52 +32,52 @@ object CaseRenderer extends TileEntitySpecialRenderer[Case] { RenderState.makeItBlend() RenderState.setBlendAlpha(1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) computer.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5, 0.5, 0.505) - GlStateManager.scale(1, -1, 1) + stack.translate(-0.5, 0.5, 0.505) + stack.scale(1, -1, 1) if (computer.isRunning) { - renderFrontOverlay(Textures.Block.CaseFrontOn) - if (System.currentTimeMillis() - computer.lastFileSystemAccess < 400 && computer.world.rand.nextDouble() > 0.1) { - renderFrontOverlay(Textures.Block.CaseFrontActivity) + renderFrontOverlay(stack, Textures.Block.CaseFrontOn) + if (System.currentTimeMillis() - computer.lastFileSystemAccess < 400 && computer.world.random.nextDouble() > 0.1) { + renderFrontOverlay(stack, Textures.Block.CaseFrontActivity) } } else if (computer.hasErrored && RenderUtil.shouldShowErrorLight(computer.hashCode)) { - renderFrontOverlay(Textures.Block.CaseFrontError) + renderFrontOverlay(stack, Textures.Block.CaseFrontError) } RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } - private def renderFrontOverlay(texture: ResourceLocation): Unit = { + private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation): Unit = { val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(texture) - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.draw() + t.end() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala index acd81901a8..8fa3299018 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala @@ -1,17 +1,27 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Charger import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -object ChargerRenderer extends TileEntitySpecialRenderer[Charger] { - override def render(charger: Charger, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object ChargerRenderer extends Function[TileEntityRendererDispatcher, ChargerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new ChargerRenderer(dispatch) +} + +class ChargerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Charger](dispatch) { + override def render(charger: Charger, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (charger.chargeSpeed > 0) { @@ -20,62 +30,62 @@ object ChargerRenderer extends TileEntitySpecialRenderer[Charger] { RenderState.disableEntityLighting() RenderState.makeItBlend() RenderState.setBlendAlpha(1) - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) charger.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5f, 0.5f, 0.5f) - GlStateManager.scale(1, -1, 1) + stack.translate(-0.5f, 0.5f, 0.5f) + stack.scale(1, -1, 1) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) { - val inverse = 1 - charger.chargeSpeed + val inverse = 1 - charger.chargeSpeed.toFloat val icon = Textures.getSprite(Textures.Block.ChargerFrontOn) - r.pos(0, 1, 0.005).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0.005).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, inverse, 0.005).tex(icon.getMaxU, icon.getInterpolatedV(inverse * 16)).endVertex() - r.pos(0, inverse, 0.005).tex(icon.getMinU, icon.getInterpolatedV(inverse * 16)).endVertex() + r.vertex(stack.last.pose, 0, 1, 0.005f).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0.005f).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, inverse, 0.005f).uv(icon.getU1, icon.getV(inverse * 16)).endVertex() + r.vertex(stack.last.pose, 0, inverse, 0.005f).uv(icon.getU0, icon.getV(inverse * 16)).endVertex() } if (charger.hasPower) { val icon = Textures.getSprite(Textures.Block.ChargerSideOn) - r.pos(-0.005, 1, -1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(-0.005, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(-0.005, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(-0.005, 0, -1).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, -0.005f, 1, -1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, -0.005f, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, -0.005f, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, -0.005f, 0, -1).uv(icon.getU0, icon.getV0).endVertex() - r.pos(1, 1, -1.005).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, -1.005).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, -1.005).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, -1.005).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1, 1, -1.005f).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, -1.005f).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, -1.005f).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, -1.005f).uv(icon.getU0, icon.getV0).endVertex() - r.pos(1.005, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1.005, 1, -1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1.005, 0, -1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1.005, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1.005f, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1.005f, 1, -1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1.005f, 0, -1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1.005f, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala index b57b468cdb..0134e4f5aa 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala @@ -1,16 +1,25 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import org.lwjgl.opengl.GL11 -object DisassemblerRenderer extends TileEntitySpecialRenderer[tileentity.Disassembler] { - override def render(disassembler: tileentity.Disassembler, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object DisassemblerRenderer extends Function[TileEntityRendererDispatcher, DisassemblerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new DisassemblerRenderer(dispatch) +} + +class DisassemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.Disassembler](dispatch) { + override def render(disassembler: tileentity.Disassembler, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (disassembler.isActive) { @@ -18,56 +27,56 @@ object DisassemblerRenderer extends TileEntitySpecialRenderer[tileentity.Disasse RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) { val icon = Textures.getSprite(Textures.Block.DisassemblerTopOn) - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } { val icon = Textures.getSprite(Textures.Block.DisassemblerSideOn) - r.pos(1, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(1, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala index a559eb1366..e4148fbf29 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala @@ -1,83 +1,86 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.DiskDrive import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.block.model.ItemCameraTransforms -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.model.ItemCameraTransforms +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.item.EntityItem -import net.minecraft.util.EnumFacing +import net.minecraft.entity.item.ItemEntity +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL13 + +object DiskDriveRenderer extends Function[TileEntityRendererDispatcher, DiskDriveRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new DiskDriveRenderer(dispatch) +} -object DiskDriveRenderer extends TileEntitySpecialRenderer[DiskDrive] { - override def render(drive: DiskDrive, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +class DiskDriveRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[DiskDrive](dispatch) { + override def render(drive: DiskDrive, dt: Float, matrix: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + matrix.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + matrix.translate(0.5, 0.5, 0.5) drive.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => matrix.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => matrix.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => matrix.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } drive.items(0) match { case stack if !stack.isEmpty => - GlStateManager.pushMatrix() - GlStateManager.translate(0, 3.5f / 16, 6 / 16f) - GlStateManager.rotate(90, -1, 0, 0) - GlStateManager.scale(0.5f, 0.5f, 0.5f) - - val brightness = drive.world.getCombinedLight(drive.getPos.offset(drive.facing), 0) - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness % 65536, brightness / 65536) - - // This is very 'meh', but item frames do it like this, too! - val entity = new EntityItem(drive.world, 0, 0, 0, stack) - entity.hoverStart = 0 - Textures.Block.bind() - Minecraft.getMinecraft.getRenderItem.renderItem(entity.getItem, ItemCameraTransforms.TransformType.FIXED) - GlStateManager.popMatrix() + matrix.pushPose() + matrix.translate(0, 3.5f / 16, 6 / 16f) + matrix.mulPose(Vector3f.XN.rotationDegrees(90)) + matrix.scale(0.5f, 0.5f, 0.5f) + + Minecraft.getInstance.getItemRenderer.renderStatic(stack, ItemCameraTransforms.TransformType.FIXED, light, overlay, matrix, buffer) + matrix.popPose() case _ => } - if (System.currentTimeMillis() - drive.lastAccess < 400 && drive.world.rand.nextDouble() > 0.1) { - GlStateManager.translate(-0.5, 0.5, 0.505) - GlStateManager.scale(1, -1, 1) + if (System.currentTimeMillis() - drive.lastAccess < 400 && drive.world.random.nextDouble() > 0.1) { + matrix.translate(-0.5, 0.5, 0.505) + matrix.scale(1, -1, 1) RenderState.disableEntityLighting() RenderState.makeItBlend() RenderState.setBlendAlpha(1) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(Textures.Block.DiskDriveFrontActivity) - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(matrix.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(matrix.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(matrix.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(matrix.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() } - GlStateManager.popMatrix() + matrix.pushPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala index 16eeabdbf9..e6d0058d86 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala @@ -1,16 +1,25 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Geolyzer import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import org.lwjgl.opengl.GL11 -object GeolyzerRenderer extends TileEntitySpecialRenderer[Geolyzer] { - override def render(geolyzer: Geolyzer, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object GeolyzerRenderer extends Function[TileEntityRendererDispatcher, GeolyzerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new GeolyzerRenderer(dispatch) +} + +class GeolyzerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Geolyzer](dispatch) { + override def render(geolyzer: Geolyzer, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() @@ -18,32 +27,32 @@ object GeolyzerRenderer extends TileEntitySpecialRenderer[Geolyzer] { RenderState.disableEntityLighting() RenderState.makeItBlend() RenderState.setBlendAlpha(1) - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(Textures.Block.GeolyzerTopOn) - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala index 39515d101b..952ee94fa0 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala @@ -1,30 +1,41 @@ package li.cil.oc.client.renderer.tileentity import java.nio.IntBuffer +import java.util.function.Function import java.util.concurrent.Callable import java.util.concurrent.TimeUnit import com.google.common.cache.CacheBuilder import com.google.common.cache.RemovalListener import com.google.common.cache.RemovalNotification +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Hologram import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.GlStateManager.CullFace -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3f +import net.minecraftforge.event.TickEvent.ClientTickEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.BufferUtils import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL15 import scala.util.Random -object HologramRenderer extends TileEntitySpecialRenderer[Hologram] with Callable[Int] with RemovalListener[TileEntity, Int] { +object HologramRenderer extends Function[TileEntityRendererDispatcher, HologramRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new HologramRenderer(dispatch) +} + +class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Hologram](dispatch) + with Callable[Int] with RemovalListener[TileEntity, Int] { + private val random = new Random() /** We cache the VBOs for the projectors we render for performance. */ @@ -65,9 +76,9 @@ object HologramRenderer extends TileEntitySpecialRenderer[Hologram] with Callabl */ private var failed = false - override def render(hologram: Hologram, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { + override def render(hologram: Hologram, f: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { if (failed) { - HologramRendererFallback.render(hologram, x, y, z, f, damage, alpha) + HologramRendererFallback.render(hologram, f, stack, buffer, light, overlay) return } @@ -76,65 +87,65 @@ object HologramRenderer extends TileEntitySpecialRenderer[Hologram] with Callabl if (!hologram.hasPower) return - GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS) + GL11.glPushClientAttrib(GL11.GL_CLIENT_ALL_ATTRIB_BITS) RenderState.pushAttrib() RenderState.makeItBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - val playerDistSq = x * x + y * y + z * z - val maxDistSq = hologram.getMaxRenderDistanceSquared + val pos = hologram.getBlockPos + val playerDistSq = Minecraft.getInstance.player.getEyePosition(f) + .distanceToSqr(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) + val maxDistSq = hologram.getViewDistance * hologram.getViewDistance val fadeDistSq = hologram.getFadeStartDistanceSquared RenderState.setBlendAlpha(0.75f * (if (playerDistSq > fadeDistSq) math.max(0, 1 - ((playerDistSq - fadeDistSq) / (maxDistSq - fadeDistSq)).toFloat) else 1)) - GlStateManager.pushMatrix() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.pushPose() + + stack.translate(0.5, 0.5, 0.5) hologram.yaw match { - case EnumFacing.WEST => GL11.glRotatef(-90, 0, 1, 0) - case EnumFacing.NORTH => GL11.glRotatef(180, 0, 1, 0) - case EnumFacing.EAST => GL11.glRotatef(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } hologram.pitch match { - case EnumFacing.DOWN => GL11.glRotatef(90, 1, 0, 0) - case EnumFacing.UP => GL11.glRotatef(-90, 1, 0, 0) + case Direction.DOWN => stack.mulPose(Vector3f.XP.rotationDegrees(90)) + case Direction.UP => stack.mulPose(Vector3f.XP.rotationDegrees(-90)) case _ => // No pitch. } - GlStateManager.rotate(hologram.rotationAngle, hologram.rotationX, hologram.rotationY, hologram.rotationZ) - GlStateManager.rotate(hologram.rotationSpeed * (hologram.getWorld.getTotalWorldTime % (360 * 20 - 1) + f) / 20f, hologram.rotationSpeedX, hologram.rotationSpeedY, hologram.rotationSpeedZ) + stack.mulPose(new Vector3f(hologram.rotationX, hologram.rotationY, hologram.rotationZ).rotationDegrees(hologram.rotationAngle)) + stack.mulPose(new Vector3f(hologram.rotationSpeedX, hologram.rotationSpeedY, hologram.rotationSpeedZ) + .rotationDegrees(hologram.rotationSpeed * (hologram.getLevel.getGameTime % (360 * 20 - 1) + f) / 20f)) - GlStateManager.scale(1.001, 1.001, 1.001) // Avoid z-fighting with other blocks. - GlStateManager.translate( + stack.scale(1.001f, 1.001f, 1.001f) // Avoid z-fighting with other blocks. + stack.translate( (hologram.translation.x * hologram.width / 16 - 1.5) * hologram.scale, hologram.translation.y * hologram.height / 16 * hologram.scale, (hologram.translation.z * hologram.width / 16 - 1.5) * hologram.scale) // Do a bit of flickering, because that's what holograms do! if (Settings.get.hologramFlickerFrequency > 0 && random.nextDouble() < Settings.get.hologramFlickerFrequency) { - GlStateManager.scale(1 + random.nextGaussian() * 0.01, 1 + random.nextGaussian() * 0.001, 1 + random.nextGaussian() * 0.01) - GlStateManager.translate(random.nextGaussian() * 0.01, random.nextGaussian() * 0.01, random.nextGaussian() * 0.01) + stack.scale(1 + random.nextGaussian().toFloat * 0.01f, 1 + random.nextGaussian().toFloat * 0.001f, 1 + random.nextGaussian().toFloat * 0.01f) + stack.translate(random.nextGaussian() * 0.01, random.nextGaussian() * 0.01, random.nextGaussian() * 0.01) } // After the below scaling, hologram is drawn inside a [0..48]x[0..32]x[0..48] box - GlStateManager.scale(hologram.scale / 16f, hologram.scale / 16f, hologram.scale / 16f) - - bindTexture(Textures.Model.HologramEffect) + stack.scale(hologram.scale.toFloat / 16f, hologram.scale.toFloat / 16f, hologram.scale.toFloat / 16f) - // Normalize normals (yes, glScale scales them too). - GL11.glEnable(GL11.GL_NORMALIZE) + Textures.bind(Textures.Model.HologramEffect) - val sx = (x + 0.5) * hologram.scale - val sy = -(y + 0.5) * hologram.scale - val sz = (z + 0.5) * hologram.scale + val sx = (pos.getX + 0.5) * hologram.scale + val sy = -(pos.getY + 0.5) * hologram.scale + val sz = (pos.getZ + 0.5) * hologram.scale if (sx >= -1.5 && sx <= 1.5 && sz >= -1.5 && sz <= 1.5 && sy >= 0 && sy <= 2) { // Camera is inside the hologram. - GlStateManager.disableCull() + RenderSystem.disableCull() } else { // Camera is outside the hologram. - GlStateManager.enableCull() - GlStateManager.cullFace(CullFace.BACK) + RenderSystem.enableCull() } // We do two passes here to avoid weird transparency effects: in the first @@ -143,15 +154,15 @@ object HologramRenderer extends TileEntitySpecialRenderer[Hologram] with Callabl // angles (because some faces will shine through sometimes and sometimes // they won't), so a more... consistent look is desirable. val glBuffer = cache.get(hologram, this) - GlStateManager.colorMask(false, false, false, false) - GlStateManager.depthMask(true) + RenderSystem.colorMask(false, false, false, false) + RenderSystem.depthMask(true) draw(glBuffer) - GlStateManager.colorMask(true, true, true, true) - GlStateManager.depthFunc(GL11.GL_EQUAL) + RenderSystem.colorMask(true, true, true, true) + RenderSystem.depthFunc(GL11.GL_EQUAL) draw(glBuffer) - GlStateManager.depthFunc(GL11.GL_LEQUAL) + RenderSystem.depthFunc(GL11.GL_LEQUAL) - GlStateManager.popMatrix() + stack.popPose() RenderState.disableBlend() RenderState.popAttrib() diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala index 850fbaeaf3..ce6049eb00 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala @@ -1,27 +1,29 @@ package li.cil.oc.client.renderer.tileentity +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.common.tileentity.Hologram import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.IRenderTypeBuffer -object HologramRendererFallback extends TileEntitySpecialRenderer[Hologram] { +object HologramRendererFallback { var text = "Requires OpenGL 1.5" - override def render(hologram: Hologram, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { + def render(hologram: Hologram, f: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - val fontRenderer = Minecraft.getMinecraft.fontRenderer + val fontRenderer = Minecraft.getInstance.font - GlStateManager.pushMatrix() - GlStateManager.translate(x + 0.5, y + 0.75, z + 0.5) + stack.pushPose() + val pos = hologram.getBlockPos + stack.translate(pos.getX + 0.5, pos.getY + 0.75, pos.getZ + 0.5) - GlStateManager.scale(1 / 128f, -1 / 128f, 1 / 128f) - GlStateManager.disableCull() - fontRenderer.drawString(text, -fontRenderer.getStringWidth(text) / 2, 0, 0xFFFFFFFF) + stack.scale(1 / 128f, -1 / 128f, 1 / 128f) + RenderSystem.disableCull() + fontRenderer.draw(stack, text, -fontRenderer.width(text) / 2, 0, 0xFFFFFFFF) - GlStateManager.popMatrix() + stack.popPose() RenderState.checkError(getClass.getName + ".render: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala index 26a0d4d82e..45b734c738 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala @@ -1,19 +1,29 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Microcontroller import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.BufferBuilder -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -object MicrocontrollerRenderer extends TileEntitySpecialRenderer[Microcontroller] { - override def render(mcu: Microcontroller, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object MicrocontrollerRenderer extends Function[TileEntityRendererDispatcher, MicrocontrollerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new MicrocontrollerRenderer(dispatch) +} + +class MicrocontrollerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Microcontroller](dispatch) { + override def render(mcu: Microcontroller, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() @@ -21,53 +31,53 @@ object MicrocontrollerRenderer extends TileEntitySpecialRenderer[Microcontroller RenderState.disableEntityLighting() RenderState.makeItBlend() RenderState.setBlendAlpha(1) - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) mcu.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5, 0.5, 0.505) - GlStateManager.scale(1, -1, 1) + stack.translate(-0.5, 0.5, 0.505) + stack.scale(1, -1, 1) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - renderFrontOverlay(Textures.Block.MicrocontrollerFrontLight, r) + renderFrontOverlay(stack, Textures.Block.MicrocontrollerFrontLight, r) if (mcu.isRunning) { - renderFrontOverlay(Textures.Block.MicrocontrollerFrontOn, r) + renderFrontOverlay(stack, Textures.Block.MicrocontrollerFrontOn, r) } else if (mcu.hasErrored && RenderUtil.shouldShowErrorLight(mcu.hashCode)) { - renderFrontOverlay(Textures.Block.MicrocontrollerFrontError, r) + renderFrontOverlay(stack, Textures.Block.MicrocontrollerFrontError, r) } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } - private def renderFrontOverlay(texture: ResourceLocation, r: BufferBuilder): Unit = { + private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation, r: BufferBuilder): Unit = { val icon = Textures.getSprite(texture) - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala index 847c653140..865759ae0b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala @@ -1,18 +1,28 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.texture.TextureMap -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.texture.AtlasTexture +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import org.lwjgl.opengl.GL11 -object NetSplitterRenderer extends TileEntitySpecialRenderer[tileentity.NetSplitter] { - override def render(splitter: tileentity.NetSplitter, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object NetSplitterRenderer extends Function[TileEntityRendererDispatcher, NetSplitterRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new NetSplitterRenderer(dispatch) +} + +class NetSplitterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.NetSplitter](dispatch) { + override def render(splitter: tileentity.NetSplitter, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (splitter.openSides.contains(!splitter.isInverted)) { @@ -20,70 +30,70 @@ object NetSplitterRenderer extends TileEntitySpecialRenderer[tileentity.NetSplit RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) - bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE) + Minecraft.getInstance().getModelManager().getAtlas(AtlasTexture.LOCATION_BLOCKS).bind() val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val sideActivity = Textures.getSprite(Textures.Block.NetSplitterOn) - if (splitter.isSideOpen(EnumFacing.DOWN)) { - r.pos(0, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 1, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() - r.pos(1, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() + if (splitter.isSideOpen(Direction.DOWN)) { + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() } - if (splitter.isSideOpen(EnumFacing.UP)) { - r.pos(0, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() + if (splitter.isSideOpen(Direction.UP)) { + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() } - if (splitter.isSideOpen(EnumFacing.NORTH)) { - r.pos(1, 1, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (splitter.isSideOpen(Direction.NORTH)) { + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (splitter.isSideOpen(EnumFacing.SOUTH)) { - r.pos(0, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(1, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (splitter.isSideOpen(Direction.SOUTH)) { + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (splitter.isSideOpen(EnumFacing.WEST)) { - r.pos(0, 1, 0).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(0, 1, 1).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(0, 0, 1).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(0, 0, 0).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (splitter.isSideOpen(Direction.WEST)) { + r.vertex(stack.last.pose, 0, 1, 0).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - if (splitter.isSideOpen(EnumFacing.EAST)) { - r.pos(1, 1, 1).tex(sideActivity.getMinU, sideActivity.getMaxV).endVertex() - r.pos(1, 1, 0).tex(sideActivity.getMaxU, sideActivity.getMaxV).endVertex() - r.pos(1, 0, 0).tex(sideActivity.getMaxU, sideActivity.getMinV).endVertex() - r.pos(1, 0, 1).tex(sideActivity.getMinU, sideActivity.getMinV).endVertex() + if (splitter.isSideOpen(Direction.EAST)) { + r.vertex(stack.last.pose, 1, 1, 1).uv(sideActivity.getU0, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(sideActivity.getU1, sideActivity.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(sideActivity.getU1, sideActivity.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala index bd41d973b0..73baff5172 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala @@ -1,16 +1,25 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import org.lwjgl.opengl.GL11 -object PowerDistributorRenderer extends TileEntitySpecialRenderer[tileentity.PowerDistributor] { - override def render(distributor: tileentity.PowerDistributor, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object PowerDistributorRenderer extends Function[TileEntityRendererDispatcher, PowerDistributorRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new PowerDistributorRenderer(dispatch) +} + +class PowerDistributorRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.PowerDistributor](dispatch) { + override def render(distributor: tileentity.PowerDistributor, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (distributor.globalBuffer > 0) { @@ -20,55 +29,55 @@ object PowerDistributorRenderer extends TileEntitySpecialRenderer[tileentity.Pow RenderState.makeItBlend() RenderState.setBlendAlpha((distributor.globalBuffer / distributor.globalBufferSize).toFloat) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) { val icon = Textures.getSprite(Textures.Block.PowerDistributorTopOn) - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } { val icon = Textures.getSprite(Textures.Block.PowerDistributorSideOn) - r.pos(1, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(1, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala index fd562d3063..32b48a0867 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala @@ -1,37 +1,44 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Printer import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.RenderHelper -import net.minecraft.client.renderer.block.model.ItemCameraTransforms -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.model.ItemCameraTransforms +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher +import net.minecraft.util.math.vector.Vector3f +import org.lwjgl.opengl.GL13 + +object PrinterRenderer extends Function[TileEntityRendererDispatcher, PrinterRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new PrinterRenderer(dispatch) +} -object PrinterRenderer extends TileEntitySpecialRenderer[Printer] { - override def render(printer: Printer, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +class PrinterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Printer](dispatch) { + override def render(printer: Printer, dt: Float, matrix: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") if (printer.data.stateOff.nonEmpty) { val stack = printer.data.createItemStack() RenderState.pushAttrib() - GlStateManager.pushMatrix() - - GlStateManager.translate(x + 0.5, y + 0.5 + 0.3, z + 0.5) - - GlStateManager.rotate((System.currentTimeMillis() % 20000) / 20000f * 360, 0, 1, 0) - GlStateManager.scale(0.75, 0.75, 0.75) + matrix.pushPose() + val pos = printer.getBlockPos + matrix.translate(pos.getX + 0.5, pos.getY + 0.5 + 0.3, pos.getZ + 0.5) - val brightness = printer.world.getCombinedLight(printer.getPos, 0) - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness % 65536, brightness / 65536) + matrix.mulPose(Vector3f.YP.rotationDegrees((System.currentTimeMillis() % 20000) / 20000f * 360)) + matrix.scale(0.75f, 0.75f, 0.75f) Textures.Block.bind() - Minecraft.getMinecraft.getRenderItem.renderItem(stack, ItemCameraTransforms.TransformType.FIXED) + Minecraft.getInstance.getItemRenderer.renderStatic(stack, ItemCameraTransforms.TransformType.FIXED, light, overlay, matrix, buffer) - GlStateManager.popMatrix() + matrix.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala index e916ae1a2a..16da338c0a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala @@ -1,54 +1,64 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api.event.RackMountableRenderEvent import li.cil.oc.common.tileentity.Rack import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer -import net.minecraft.util.EnumFacing +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3f import net.minecraftforge.common.MinecraftForge import org.lwjgl.opengl.GL11 -object RackRenderer extends TileEntitySpecialRenderer[Rack] { +object RackRenderer extends Function[TileEntityRendererDispatcher, RackRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new RackRenderer(dispatch) +} + +class RackRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Rack](dispatch) { private final val vOffset = 2 / 16f private final val vSize = 3 / 16f - override def render(rack: Rack, x: Double, y: Double, z: Double, partialTicks: Float, destroyStage: Int, alpha: Float): Unit = { + override def render(rack: Rack, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) rack.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5, 0.5, 0.505 - 0.5f / 16f) - GlStateManager.scale(1, -1, 1) + stack.translate(-0.5, 0.5, 0.505 - 0.5f / 16f) + stack.scale(1, -1, 1) // Note: we manually sync the rack inventory for this to work. - for (i <- 0 until rack.getSizeInventory) { - if (!rack.getStackInSlot(i).isEmpty) { - GlStateManager.pushMatrix() + for (i <- 0 until rack.getContainerSize) { + if (!rack.getItem(i).isEmpty) { + stack.pushPose() RenderState.pushAttrib() val v0 = vOffset + i * vSize val v1 = vOffset + (i + 1) * vSize - val event = new RackMountableRenderEvent.TileEntity(rack, i, rack.lastData(i), v0, v1) + val event = new RackMountableRenderEvent.TileEntity(rack, i, rack.lastData(i), stack, buffer, light, overlay, v0, v1) MinecraftForge.EVENT_BUS.post(event) RenderState.popAttrib() - GlStateManager.popMatrix() + stack.popPose() } } - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala index 83b3bbb33e..3090082504 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala @@ -1,71 +1,81 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity.Raid import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.BufferBuilder +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.texture.TextureAtlasSprite -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 -object RaidRenderer extends TileEntitySpecialRenderer[Raid] { - override def render(raid: Raid, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object RaidRenderer extends Function[TileEntityRendererDispatcher, RaidRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new RaidRenderer(dispatch) +} + +class RaidRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Raid](dispatch) { + override def render(raid: Raid, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") RenderState.pushAttrib() RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) raid.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5, 0.5, 0.505) - GlStateManager.scale(1, -1, 1) + stack.translate(-0.5, 0.5, 0.505) + stack.scale(1, -1, 1) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) { val icon = Textures.getSprite(Textures.Block.RaidFrontError) - for (slot <- 0 until raid.getSizeInventory) { + for (slot <- 0 until raid.getContainerSize) { if (!raid.presence(slot)) { - renderSlot(r, slot, icon) + renderSlot(stack, r, slot, icon) } } } { val icon = Textures.getSprite(Textures.Block.RaidFrontActivity) - for (slot <- 0 until raid.getSizeInventory) { - if (System.currentTimeMillis() - raid.lastAccess < 400 && raid.world.rand.nextDouble() > 0.1 && slot == raid.lastAccess % raid.getSizeInventory) { - renderSlot(r, slot, icon) + for (slot <- 0 until raid.getContainerSize) { + if (System.currentTimeMillis() - raid.lastAccess < 400 && raid.world.random.nextDouble() > 0.1 && slot == raid.lastAccess % raid.getContainerSize) { + renderSlot(stack, r, slot, icon) } } } - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") @@ -74,12 +84,12 @@ object RaidRenderer extends TileEntitySpecialRenderer[Raid] { private val u1 = 2 / 16f private val fs = 4 / 16f - private def renderSlot(r: BufferBuilder, slot: Int, icon: TextureAtlasSprite) { + private def renderSlot(stack: MatrixStack, r: BufferBuilder, slot: Int, icon: TextureAtlasSprite) { val l = u1 + slot * fs val h = u1 + (slot + 1) * fs - r.pos(l, 1, 0).tex(icon.getInterpolatedU(l * 16), icon.getMaxV).endVertex() - r.pos(h, 1, 0).tex(icon.getInterpolatedU(h * 16), icon.getMaxV).endVertex() - r.pos(h, 0, 0).tex(icon.getInterpolatedU(h * 16), icon.getMinV).endVertex() - r.pos(l, 0, 0).tex(icon.getInterpolatedU(l * 16), icon.getMinV).endVertex() + r.vertex(stack.last.pose, l, 1, 0).uv(icon.getU(l * 16), icon.getV1).endVertex() + r.vertex(stack.last.pose, h, 1, 0).uv(icon.getU(h * 16), icon.getV1).endVertex() + r.vertex(stack.last.pose, h, 0, 0).uv(icon.getU(h * 16), icon.getV0).endVertex() + r.vertex(stack.last.pose, l, 0, 0).uv(icon.getU(l * 16), icon.getV0).endVertex() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala index ca447b2afb..85a84baa2f 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala @@ -1,16 +1,25 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import org.lwjgl.opengl.GL11 -object RelayRenderer extends TileEntitySpecialRenderer[tileentity.Relay] { - override def render(switch: tileentity.Relay, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object RelayRenderer extends Function[TileEntityRendererDispatcher, RelayRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new RelayRenderer(dispatch) +} + +class RelayRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.Relay](dispatch) { + override def render(switch: tileentity.Relay, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") val activity = math.max(0, 1 - (System.currentTimeMillis() - switch.lastMessage) / 1000.0) @@ -21,45 +30,45 @@ object RelayRenderer extends TileEntitySpecialRenderer[tileentity.Relay] { RenderState.makeItBlend() RenderState.setBlendAlpha(activity.toFloat) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(Textures.Block.SwitchSideOn) - r.pos(1, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - r.pos(0, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV0).endVertex() - r.pos(1, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.draw() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index 3af604a955..fee25b868e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -1,6 +1,12 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + import com.google.common.base.Strings +import com.google.common.collect.ImmutableList +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api.driver.item.UpgradeRenderer @@ -14,19 +20,21 @@ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.client.Minecraft import net.minecraft.client.renderer._ -import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType -import net.minecraft.client.renderer.entity.RenderLivingBase -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.client.renderer.vertex.VertexFormat import net.minecraft.client.renderer.vertex.VertexFormatElement -import net.minecraft.init.Items -import net.minecraft.item.ItemBlock +import net.minecraft.item.Items +import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.math.vector.Vector3f +import net.minecraft.util.math.vector.Matrix3f import net.minecraft.util.text.TextFormatting -import net.minecraftforge.client.MinecraftForgeClient +import net.minecraftforge.client.ForgeHooksClient import net.minecraftforge.common.MinecraftForge import org.lwjgl.opengl.GL11 @@ -34,9 +42,16 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.mutable import scala.language.implicitConversions -object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { - private val displayList = GLAllocation.generateDisplayLists(2) +object RobotRenderer extends Function[TileEntityRendererDispatcher, RobotRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new RobotRenderer(dispatch) + + private val instance = new RobotRenderer(null) + + def renderChassis(stack: MatrixStack, offset: Double = 0, isRunningOverride: Boolean = false) = + instance.renderChassis(stack, null, offset, isRunningOverride) +} +class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.RobotProxy](dispatch) { private val mountPoints = new Array[RobotRenderEvent.MountPoint](7) private val slotNameMapping = Map( @@ -61,87 +76,72 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { private val gb = 0.5f - gap // https://github.com/MinecraftForge/MinecraftForge/issues/2321 - val POSITION_TEX_NORMALF = new VertexFormat() - val NORMAL_3F = new VertexFormatElement(0, VertexFormatElement.EnumType.FLOAT, VertexFormatElement.EnumUsage.NORMAL, 3) - POSITION_TEX_NORMALF.addElement(DefaultVertexFormats.POSITION_3F) - POSITION_TEX_NORMALF.addElement(DefaultVertexFormats.TEX_2F) - POSITION_TEX_NORMALF.addElement(NORMAL_3F) + val NORMAL_3F = new VertexFormatElement(0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.NORMAL, 3) + val POSITION_TEX_NORMALF = new VertexFormat(ImmutableList.builder() + .add(DefaultVertexFormats.ELEMENT_POSITION) + .add(DefaultVertexFormats.ELEMENT_UV0) + .add(NORMAL_3F) + .build()) - private implicit def extendWorldRenderer(self: BufferBuilder): ExtendedWorldRenderer = new ExtendedWorldRenderer(self) + private implicit def extendWorldRenderer(self: IVertexBuilder): ExtendedWorldRenderer = new ExtendedWorldRenderer(self) - private class ExtendedWorldRenderer(val buffer: BufferBuilder) { - def normal(normal: Vec3d): BufferBuilder = { + private class ExtendedWorldRenderer(val buffer: IVertexBuilder) { + def normal(matrix: Matrix3f, normal: Vector3d): IVertexBuilder = { val normalized = normal.normalize() - buffer.normal(normalized.x.toFloat, normalized.y.toFloat, normalized.z.toFloat) + buffer.normal(matrix, normalized.x.toFloat, normalized.y.toFloat, normalized.z.toFloat) } } - private def drawTop(): Unit = { + private def drawTop(stack: MatrixStack): Unit = { val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMALF) - r.pos(0.5f, 1, 0.5f).tex(0.25f, 0.25f).normal(new Vec3d(0, 0.2, 1)).endVertex() - r.pos(l, gt, h).tex(0, 0.5f).normal(new Vec3d(0, 0.2, 1)).endVertex() - r.pos(h, gt, h).tex(0.5f, 0.5f).normal(new Vec3d(0, 0.2, 1)).endVertex() - r.pos(h, gt, l).tex(0.5f, 0).normal(new Vec3d(1, 0.2, 0)).endVertex() - r.pos(l, gt, l).tex(0, 0).normal(new Vec3d(0, 0.2, -1)).endVertex() - r.pos(l, gt, h).tex(0, 0.5f).normal(new Vec3d(-1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(0, 0).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).normal(stack.last.normal, new Vector3d(-1, 0.2, 0)).endVertex() - t.draw() + t.end() r.begin(GL11.GL_QUADS, POSITION_TEX_NORMALF) - r.pos(l, gt, h).tex(0, 1).normal(0, -1, 0).endVertex() - r.pos(l, gt, l).tex(0, 0.5).normal(0, -1, 0).endVertex() - r.pos(h, gt, l).tex(0.5, 0.5).normal(0, -1, 0).endVertex() - r.pos(h, gt, h).tex(0.5, 1).normal(0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 1).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(0, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 1).normal(stack.last.normal, 0, -1, 0).endVertex() - t.draw() + t.end() } - private def drawBottom(): Unit = { + private def drawBottom(stack: MatrixStack): Unit = { val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMALF) - r.pos(0.5f, 0.03f, 0.5f).tex(0.75f, 0.25f).normal(new Vec3d(0, -0.2, 1)).endVertex() - r.pos(l, gb, l).tex(0.5f, 0).normal(new Vec3d(0, -0.2, 1)).endVertex() - r.pos(h, gb, l).tex(1, 0).normal(new Vec3d(0, -0.2, 1)).endVertex() - r.pos(h, gb, h).tex(1, 0.5f).normal(new Vec3d(1, -0.2, 0)).endVertex() - r.pos(l, gb, h).tex(0.5f, 0.5f).normal(new Vec3d(0, -0.2, -1)).endVertex() - r.pos(l, gb, l).tex(0.5f, 0).normal(new Vec3d(-1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(1, 0).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(-1, -0.2, 0)).endVertex() - t.draw() + t.end() r.begin(GL11.GL_QUADS, POSITION_TEX_NORMALF) - r.pos(l, gb, l).tex(0, 0.5).normal(0, 1, 0).endVertex() - r.pos(l, gb, h).tex(0, 1).normal(0, 1, 0).endVertex() - r.pos(h, gb, h).tex(0.5, 1).normal(0, 1, 0).endVertex() - r.pos(h, gb, l).tex(0.5, 0.5).normal(0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(0, 1).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(0.5f, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() - t.draw() + t.end() } - def compileList() { - GL11.glNewList(displayList, GL11.GL_COMPILE) - - drawTop() - - GL11.glEndList() - - GL11.glNewList(displayList + 1, GL11.GL_COMPILE) - - drawBottom() - - GL11.glEndList() - } - - compileList() - def resetMountPoints(running: Boolean) { val offset = if (running) 0 else -0.06f @@ -209,7 +209,7 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { mountPoints(6).rotation.setW(0) } - def renderChassis(robot: tileentity.Robot = null, offset: Double = 0, isRunningOverride: Boolean = false) { + def renderChassis(stack: MatrixStack, robot: tileentity.Robot = null, offset: Double = 0, isRunningOverride: Boolean = false) { val isRunning = if (robot == null) isRunningOverride else robot.isRunning val size = 0.3f @@ -229,21 +229,19 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { val event = new RobotRenderEvent(robot, mountPoints) MinecraftForge.EVENT_BUS.post(event) if (!event.isCanceled) { - bindTexture(Textures.Model.Robot) + Textures.bind(Textures.Model.Robot) if (!isRunning) { - GlStateManager.translate(0, -2 * gap, 0) + stack.translate(0, -2 * gap, 0) } - //GlStateManager.callList(displayList + 1) - drawBottom() + drawBottom(stack) if (!isRunning) { - GlStateManager.translate(0, -2 * gap, 0) + stack.translate(0, -2 * gap, 0) } - if (MinecraftForgeClient.getRenderPass > 0) return + if (ForgeHooksClient.getWorldRenderPass > 0) return - //GlStateManager.callList(displayList) - drawTop() - GlStateManager.color(1, 1, 1) + drawTop(stack) + RenderSystem.color3f(1, 1, 1) if (isRunning) { RenderState.disableEntityLighting() @@ -251,177 +249,168 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { { // Additive blending for the light. RenderState.makeItBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) // Light color. val lightColor = if (robot != null && robot.info != null) robot.info.lightColor else 0xF23030 val r = (lightColor >>> 16) & 0xFF val g = (lightColor >>> 8) & 0xFF val b = (lightColor >>> 0) & 0xFF - GlStateManager.color(r / 255f, g / 255f, b / 255f) + RenderSystem.color3f(r / 255f, g / 255f, b / 255f) } val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(l, gt, l).tex(u0, v0).endVertex() - r.pos(l, gb, l).tex(u0, v1).endVertex() - r.pos(l, gb, h).tex(u1, v1).endVertex() - r.pos(l, gt, h).tex(u1, v0).endVertex() - - r.pos(l, gt, h).tex(u0, v0).endVertex() - r.pos(l, gb, h).tex(u0, v1).endVertex() - r.pos(h, gb, h).tex(u1, v1).endVertex() - r.pos(h, gt, h).tex(u1, v0).endVertex() - - r.pos(h, gt, h).tex(u0, v0).endVertex() - r.pos(h, gb, h).tex(u0, v1).endVertex() - r.pos(h, gb, l).tex(u1, v1).endVertex() - r.pos(h, gt, l).tex(u1, v0).endVertex() - - r.pos(h, gt, l).tex(u0, v0).endVertex() - r.pos(h, gb, l).tex(u0, v1).endVertex() - r.pos(l, gb, l).tex(u1, v1).endVertex() - r.pos(l, gt, l).tex(u1, v0).endVertex() - t.draw() + r.vertex(stack.last.pose, l, gt, l).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, l, gt, h).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, h, gt, h).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, h, gt, l).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(u1, v0).endVertex() + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() } - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) } } - override def render(proxy: tileentity.RobotProxy, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { + override def render(proxy: tileentity.RobotProxy, f: Float, matrix: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") val robot = proxy.robot - val worldTime = robot.getWorld.getTotalWorldTime + f + val worldTime = proxy.getLevel.getGameTime + f - GlStateManager.pushMatrix() + matrix.pushPose() RenderState.pushAttrib() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + + matrix.translate(0.5, 0.5, 0.5) // If the move started while we were rendering and we have a reference to // the *old* proxy the robot would be rendered at the wrong position, so we // correct for the offset. if (robot.proxy != proxy) { - GlStateManager.translate(robot.proxy.x - proxy.x, robot.proxy.y - proxy.y, robot.proxy.z - proxy.z) + matrix.translate(robot.proxy.x - proxy.x, robot.proxy.y - proxy.y, robot.proxy.z - proxy.z) } if (robot.isAnimatingMove) { val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble - val delta = robot.moveFrom.get.subtract(robot.getPos) - GlStateManager.translate(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) + val delta = robot.moveFrom.get.subtract(robot.getBlockPos) + matrix.translate(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) } val timeJitter = robot.hashCode ^ 0xFF val hover = if (robot.isRunning) (Math.sin(timeJitter + worldTime / 20.0) * 0.03).toFloat else -0.03f - GlStateManager.translate(0, hover, 0) + matrix.translate(0, hover, 0) - GlStateManager.pushMatrix() + matrix.pushPose() - GlStateManager.depthMask(true) + RenderSystem.depthMask(true) RenderState.enableEntityLighting() - GlStateManager.disableBlend() + RenderSystem.disableBlend() if (robot.isAnimatingTurn) { val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toFloat - GlStateManager.rotate(90 * remaining, 0, robot.turnAxis, 0) + val axis = if (robot.turnAxis < 0) Vector3f.YN else Vector3f.YP + matrix.mulPose(axis.rotationDegrees(90 * remaining)) } robot.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => matrix.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => matrix.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => matrix.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + matrix.translate(-0.5f, -0.5f, -0.5f) val offset = timeJitter + worldTime / 20.0 - renderChassis(robot, offset) + renderChassis(matrix, robot, offset) - if (MinecraftForgeClient.getRenderPass == 0 && !robot.renderingErrored && x * x + y * y + z * z < 24 * 24) { - val itemRenderer = Minecraft.getMinecraft.getItemRenderer - StackOption(robot.getStackInSlot(0)) match { + val pos = proxy.getBlockPos + val dist = Minecraft.getInstance.player.position.distanceToSqr(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) + if (ForgeHooksClient.getWorldRenderPass == 0 && !robot.renderingErrored && dist < 24 * 24) { + val itemRenderer = Minecraft.getInstance.getItemRenderer + StackOption(robot.getItem(0)) match { case SomeStack(stack) => RenderState.pushAttrib() - GlStateManager.pushMatrix() + matrix.pushPose() try { // Copy-paste from player render code, with minor adjustments for // robot scale. - GlStateManager.disableCull() - GlStateManager.enableRescaleNormal() + RenderSystem.disableCull() + RenderSystem.enableRescaleNormal() - GlStateManager.scale(1, -1, -1) - GlStateManager.translate(0, -8 * 0.0625F - 0.0078125F, -0.5F) + matrix.scale(1, -1, -1) + matrix.translate(0, -8 * 0.0625F - 0.0078125F, -0.5F) if (robot.isAnimatingSwing) { val wantedTicksPerCycle = 10 val cycles = math.max(robot.animationTicksTotal / wantedTicksPerCycle, 1) val ticksPerCycle = robot.animationTicksTotal / cycles val remaining = (robot.animationTicksLeft - f) / ticksPerCycle.toDouble - GlStateManager.rotate((Math.sin((remaining - remaining.toInt) * Math.PI) * 45).toFloat, 1, 0, 0) + matrix.mulPose(Vector3f.XP.rotationDegrees((Math.sin((remaining - remaining.toInt) * Math.PI) * 45).toFloat)) } val item = stack.getItem - if (item.isInstanceOf[ItemBlock]) { - GlStateManager.rotate(-90.0F, 1.0F, 0.0F, 0.0F) - GlStateManager.rotate(180.0F, 0.0F, 1.0F, 0.0F) + if (item.isInstanceOf[BlockItem]) { + matrix.mulPose(Vector3f.XP.rotationDegrees(-90.0F)) + matrix.mulPose(Vector3f.YP.rotationDegrees(180.0F)) val scale = 0.625F - GlStateManager.scale(scale, scale, scale) + matrix.scale(scale, scale, scale) } else if (item == Items.BOW) { - GlStateManager.translate(1.5f/16f, -0.125F, -0.125F) - GlStateManager.rotate(10.0F, 0.0F, 0.0F, 1.0F) - val scale = 0.625F - GlStateManager.scale(scale, -scale, scale) - } - else if (item.isFull3D) { - if (item.shouldRotateAroundWhenRendering) { - GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F) - GlStateManager.translate(0.0F, -0.0625F, 0.0F) - } - - GlStateManager.translate(0.0F, 0.1875F, 0.0F) - GlStateManager.translate(0.0625F, -0.125F, -2/16F) + matrix.translate(1.5f/16f, -0.125F, -0.125F) + matrix.mulPose(Vector3f.ZP.rotationDegrees(10.0F)) val scale = 0.625F - GlStateManager.scale(scale, -scale, scale) - GlStateManager.rotate(0.0F, 1.0F, 0.0F, 0.0F) - GlStateManager.rotate(0.0F, 0.0F, 1.0F, 0.0F) + matrix.scale(scale, -scale, scale) } else { - GlStateManager.translate(0, 2f/16f, 0) + matrix.translate(0, 2f/16f, 0) val scale = 0.875F - GlStateManager.scale(scale, scale, scale) - GlStateManager.rotate(90.0F, 0.0F, 1.0F, 0.0F) - GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F) + matrix.scale(scale, scale, scale) + matrix.mulPose(Vector3f.YP.rotationDegrees(90.0F)) + matrix.mulPose(Vector3f.ZP.rotationDegrees(180.0F)) } - itemRenderer.renderItem(Minecraft.getMinecraft.player, stack, TransformType.THIRD_PERSON_RIGHT_HAND) + itemRenderer.renderStatic(Minecraft.getInstance.player, stack, TransformType.THIRD_PERSON_RIGHT_HAND, false, matrix, buffer, proxy.getLevel, light, overlay) } catch { case e: Throwable => OpenComputers.log.warn("Failed rendering equipped item.", e) robot.renderingErrored = true } - GlStateManager.enableCull() - GlStateManager.disableRescaleNormal() - GlStateManager.popMatrix() + RenderSystem.enableCull() + RenderSystem.disableRescaleNormal() + matrix.popPose() RenderState.popAttrib() case _ => } - if (MinecraftForgeClient.getRenderPass == 0) { + if (ForgeHooksClient.getWorldRenderPass == 0) { lazy val availableSlots = slotNameMapping.keys.to[mutable.Set] lazy val wildcardRenderers = mutable.Buffer.empty[(ItemStack, UpgradeRenderer)] lazy val slotMapping = Array.fill(mountPoints.length)(null: (ItemStack, UpgradeRenderer)) - val renderers = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot). + val renderers = (robot.componentSlots ++ robot.containerSlots).map(robot.getItem). collect { case stack if !stack.isEmpty && stack.getItem.isInstanceOf[UpgradeRenderer] => (stack, stack.getItem.asInstanceOf[UpgradeRenderer]) } for ((stack, renderer) <- renderers) { @@ -442,10 +431,10 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { for ((info, mountPoint) <- (slotMapping, mountPoints).zipped if info != null) try { val (stack, renderer) = info - GlStateManager.pushMatrix() - GlStateManager.translate(0.5f, 0.5f, 0.5f) - renderer.render(stack, mountPoint, robot, f) - GlStateManager.popMatrix() + matrix.pushPose() + matrix.translate(0.5f, 0.5f, 0.5f) + renderer.render(matrix, stack, mountPoint, robot, f) + matrix.popPose() } catch { case e: Throwable => @@ -454,51 +443,51 @@ object RobotRenderer extends TileEntitySpecialRenderer[tileentity.RobotProxy] { } } } - GlStateManager.popMatrix() + matrix.popPose() val name = robot.name - if (Settings.get.robotLabels && MinecraftForgeClient.getRenderPass == 1 && !Strings.isNullOrEmpty(name) && x * x + y * y + z * z < RenderLivingBase.NAME_TAG_RANGE) { - GlStateManager.pushMatrix() + if (Settings.get.robotLabels && ForgeHooksClient.getWorldRenderPass == 1 && !Strings.isNullOrEmpty(name) && ForgeHooksClient.isNameplateInRenderDistance(null, dist)) { + matrix.pushPose() // This is pretty much copy-pasta from the entity's label renderer. val t = Tessellator.getInstance - val r = t.getBuffer - val f = getFontRenderer + val r = t.getBuilder + val f = Minecraft.getInstance.font val scale = 1.6f / 60f - val width = f.getStringWidth(name) + val width = f.width(name) val halfWidth = width / 2 - GlStateManager.translate(0, 0.8, 0) + matrix.translate(0, 0.8, 0) GL11.glNormal3f(0, 1, 0) - GlStateManager.color(1, 1, 1) + RenderSystem.color3f(1, 1, 1) - GlStateManager.rotate(-rendererDispatcher.entityYaw, 0, 1, 0) - GlStateManager.rotate(rendererDispatcher.entityPitch, 1, 0, 0) - GlStateManager.scale(-scale, -scale, scale) + matrix.mulPose(Vector3f.YP.rotationDegrees(-renderer.camera.getYRot)) + matrix.mulPose(Vector3f.XP.rotationDegrees(renderer.camera.getXRot)) + matrix.scale(-scale, -scale, scale) RenderState.makeItBlend() - GlStateManager.depthMask(false) - GlStateManager.disableLighting() - GlStateManager.disableTexture2D() + RenderSystem.depthMask(false) + RenderSystem.disableLighting() + RenderSystem.disableTexture() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR) - r.pos(-halfWidth - 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() - r.pos(-halfWidth - 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() - r.pos(halfWidth + 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() - r.pos(halfWidth + 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() - t.draw() + r.vertex(matrix.last.pose, -halfWidth - 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() + r.vertex(matrix.last.pose, -halfWidth - 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() + r.vertex(matrix.last.pose, halfWidth + 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() + r.vertex(matrix.last.pose, halfWidth + 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() + t.end() - GlStateManager.enableTexture2D() // For the font. - f.drawString((if (EventHandler.isItTime) TextFormatting.OBFUSCATED.toString else "") + name, -halfWidth, 0, 0xFFFFFFFF) + RenderSystem.enableTexture() // For the font. + f.draw(matrix, (if (EventHandler.isItTime) TextFormatting.OBFUSCATED.toString else "") + name, -halfWidth, 0, 0xFFFFFFFF) - GlStateManager.depthMask(true) - GlStateManager.enableLighting() + RenderSystem.depthMask(true) + RenderSystem.enableLighting() RenderState.disableBlend() - GlStateManager.popMatrix() + matrix.popPose() } - GlStateManager.popMatrix() + matrix.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala index 0beb9af08b..e490356847 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala @@ -1,5 +1,9 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api @@ -8,17 +12,23 @@ import li.cil.oc.common.tileentity.Screen import li.cil.oc.integration.util.Wrench import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL13 import org.lwjgl.opengl.GL14 -import org.lwjgl.opengl.GLContext -object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { +object ScreenRenderer extends Function[TileEntityRendererDispatcher, ScreenRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new ScreenRenderer(dispatch) +} + +class ScreenRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Screen](dispatch) { private val maxRenderDistanceSq = Settings.get.maxScreenTextRenderDistance * Settings.get.maxScreenTextRenderDistance private val fadeDistanceSq = Settings.get.screenTextFadeStartDistance * Settings.get.screenTextFadeStartDistance @@ -32,13 +42,13 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { api.Items.get(Constants.BlockName.ScreenTier2), api.Items.get(Constants.BlockName.ScreenTier3)) - private val canUseBlendColor = GLContext.getCapabilities.OpenGL14 + private val canUseBlendColor = true // Minecraft 1.16 already requires OpenGL 2.0 or above. // ----------------------------------------------------------------------- // // Rendering // ----------------------------------------------------------------------- // - override def render(screen: Screen, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { + override def render(screen: Screen, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") this.screen = screen @@ -51,14 +61,15 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { return } - // y = block.bottom - player.feet - // eye is higher, so the y delta should be more negative - val eye_delta: Double = y - Minecraft.getMinecraft.player.getEyeHeight + val eye_pos = Minecraft.getInstance.player.getEyePosition(dt) + val eye_delta: Double = screen.getBlockPos.getY - eye_pos.y // Crude check whether screen text can be seen by the local player based // on the player's position -> angle relative to screen. val screenFacing = screen.facing.getOpposite - if (screenFacing.getFrontOffsetX * (x + 0.5) + screenFacing.getFrontOffsetY * (eye_delta + 0.5) + screenFacing.getFrontOffsetZ * (z + 0.5) < 0) { + val x = screen.getBlockPos.getX - eye_pos.x + val z = screen.getBlockPos.getZ - eye_pos.z + if (screenFacing.getStepX * (x + 0.5) + screenFacing.getStepY * (eye_delta + 0.5) + screenFacing.getStepZ * (z + 0.5) < 0) { return } @@ -66,18 +77,18 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { RenderState.pushAttrib() - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 0xFF, 0xFF) + RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 0xFF, 0xFF) RenderState.disableEntityLighting() RenderState.makeItBlend() - GlStateManager.color(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) + stack.translate(0.5, 0.5, 0.5) RenderState.checkError(getClass.getName + ".render: setup") - drawOverlay() + drawOverlay(stack) RenderState.checkError(getClass.getName + ".render: overlay") @@ -85,77 +96,77 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { val alpha = math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat) if (canUseBlendColor) { GL14.glBlendColor(0, 0, 0, alpha) - GlStateManager.blendFunc(GL11.GL_CONSTANT_ALPHA, GL11.GL_ONE) + RenderSystem.blendFunc(GL14.GL_CONSTANT_ALPHA, GL11.GL_ONE) } } RenderState.checkError(getClass.getName + ".render: fade") if (screen.buffer.isRenderingEnabled) { - draw() + draw(stack) } RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } - private def transform() { + private def transform(stack: MatrixStack) { screen.yaw match { - case EnumFacing.WEST => GlStateManager.rotate(-90, 0, 1, 0) - case EnumFacing.NORTH => GlStateManager.rotate(180, 0, 1, 0) - case EnumFacing.EAST => GlStateManager.rotate(90, 0, 1, 0) + case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) + case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) + case Direction.EAST => stack.mulPose(Vector3f.YP.rotationDegrees(90)) case _ => // No yaw. } screen.pitch match { - case EnumFacing.DOWN => GlStateManager.rotate(90, 1, 0, 0) - case EnumFacing.UP => GlStateManager.rotate(-90, 1, 0, 0) + case Direction.DOWN => stack.mulPose(Vector3f.XP.rotationDegrees(90)) + case Direction.UP => stack.mulPose(Vector3f.XP.rotationDegrees(-90)) case _ => // No pitch. } // Fit area to screen (bottom left = bottom left). - GlStateManager.translate(-0.5f, -0.5f, 0.5f) - GlStateManager.translate(0, screen.height, 0) + stack.translate(-0.5f, -0.5f, 0.5f) + stack.translate(0, screen.height, 0) // Flip text upside down. - GlStateManager.scale(1, -1, 1) + stack.scale(1, -1, 1) } - private def drawOverlay() = if (screen.facing == EnumFacing.UP || screen.facing == EnumFacing.DOWN) { + private def drawOverlay(matrix: MatrixStack) = if (screen.facing == Direction.UP || screen.facing == Direction.DOWN) { // Show up vector overlay when holding same screen block. - val stack = Minecraft.getMinecraft.player.getHeldItemMainhand + val stack = Minecraft.getInstance.player.getItemInHand(Hand.MAIN_HAND) if (!stack.isEmpty) { - if (Wrench.holdsApplicableWrench(Minecraft.getMinecraft.player, screen.getPos) || screens.contains(api.Items.get(stack))) { - GlStateManager.pushMatrix() - transform() - GlStateManager.depthMask(false) - GlStateManager.translate(screen.width / 2f - 0.5f, screen.height / 2f - 0.5f, 0.05f) + if (Wrench.holdsApplicableWrench(Minecraft.getInstance.player, screen.getBlockPos) || screens.contains(api.Items.get(stack))) { + matrix.pushPose() + transform(matrix) + RenderSystem.depthMask(false) + matrix.translate(screen.width / 2f - 0.5f, screen.height / 2f - 0.5f, 0.05f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(Textures.Block.ScreenUpIndicator) - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() + r.vertex(matrix.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(matrix.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(matrix.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(matrix.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.draw() + t.end() - GlStateManager.depthMask(true) - GlStateManager.popMatrix() + RenderSystem.depthMask(true) + matrix.popPose() } } } - private def draw() { + private def draw(stack: MatrixStack) { RenderState.checkError(getClass.getName + ".draw: entering (aka: wasntme)") val sx = screen.width @@ -163,10 +174,10 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { val tw = sx * 16f val th = sy * 16f - transform() + transform(stack) // Offset from border. - GlStateManager.translate(sx * 2.25f / tw, sy * 2.25f / th, 0) + stack.translate(sx * 2.25f / tw, sy * 2.25f / th, 0) // Inner size (minus borders). val isx = sx - (4.5f / 16) @@ -179,37 +190,37 @@ object ScreenRenderer extends TileEntitySpecialRenderer[Screen] { val scaleY = isy / sizeY if (true) { if (scaleX > scaleY) { - GlStateManager.translate(sizeX * 0.5f * (scaleX - scaleY), 0, 0) - GlStateManager.scale(scaleY, scaleY, 1) + stack.translate(sizeX * 0.5f * (scaleX - scaleY), 0, 0) + stack.scale(scaleY, scaleY, 1) } else { - GlStateManager.translate(0, sizeY * 0.5f * (scaleY - scaleX), 0) - GlStateManager.scale(scaleX, scaleX, 1) + stack.translate(0, sizeY * 0.5f * (scaleY - scaleX), 0) + stack.scale(scaleX, scaleX, 1) } } else { // Stretch to fit. - GlStateManager.scale(scaleX, scaleY, 1) + stack.scale(scaleX, scaleY, 1) } // Slightly offset the text so it doesn't clip into the screen. - GlStateManager.translate(0, 0, 0.01) + stack.translate(0, 0, 0.01) RenderState.checkError(getClass.getName + ".draw: setup") // Render the actual text. - screen.buffer.renderText() + screen.buffer.renderText(stack) RenderState.checkError(getClass.getName + ".draw: text") } private def playerDistanceSq() = { - val player = Minecraft.getMinecraft.player + val player = Minecraft.getInstance.player val bounds = screen.getRenderBoundingBox - val px = player.posX - val py = player.posY - val pz = player.posZ + val px = player.getX + val py = player.getY + val pz = player.getZ val ex = bounds.maxX - bounds.minX val ey = bounds.maxY - bounds.minY diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala index 095d0d678c..139b09b63b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala @@ -1,16 +1,25 @@ package li.cil.oc.client.renderer.tileentity +import java.util.function.Function + +import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRenderer +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats import org.lwjgl.opengl.GL11 -object TransposerRenderer extends TileEntitySpecialRenderer[tileentity.Transposer] { - override def render(transposer: tileentity.Transposer, x: Double, y: Double, z: Double, f: Float, damage: Int, alpha: Float) { +object TransposerRenderer extends Function[TileEntityRendererDispatcher, TransposerRenderer] { + override def apply(dispatch: TileEntityRendererDispatcher) = new TransposerRenderer(dispatch) +} + +class TransposerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.Transposer](dispatch) { + override def render(transposer: tileentity.Transposer, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") val activity = math.max(0, 1 - (System.currentTimeMillis() - transposer.lastOperation) / 1000.0) @@ -21,55 +30,55 @@ object TransposerRenderer extends TileEntitySpecialRenderer[tileentity.Transpose RenderState.makeItBlend() RenderState.setBlendAlpha(activity.toFloat) - GlStateManager.pushMatrix() + stack.pushPose() - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5) - GlStateManager.scale(1.0025, -1.0025, 1.0025) - GlStateManager.translate(-0.5f, -0.5f, -0.5f) + stack.translate(0.5, 0.5, 0.5) + stack.scale(1.0025f, -1.0025f, 1.0025f) + stack.translate(-0.5f, -0.5f, -0.5f) val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder Textures.Block.bind() r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) val icon = Textures.getSprite(Textures.Block.TransposerOn) - r.pos(0, 1, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 1, 0).tex(icon.getMinU, icon.getMinV).endVertex() - r.pos(1, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - - r.pos(0, 0, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - r.pos(1, 0, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - - r.pos(1, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(0, 1, 0).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(0, 1, 1).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(0, 0, 1).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(0, 0, 0).tex(icon.getMinU, icon.getMinV).endVertex() - - r.pos(1, 1, 1).tex(icon.getMinU, icon.getMaxV).endVertex() - r.pos(1, 1, 0).tex(icon.getMaxU, icon.getMaxV).endVertex() - r.pos(1, 0, 0).tex(icon.getMaxU, icon.getMinV).endVertex() - r.pos(1, 0, 1).tex(icon.getMinU, icon.getMinV).endVertex() - - t.draw() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV1).endVertex() + + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() + + t.end() RenderState.disableBlend() RenderState.enableEntityLighting() - GlStateManager.popMatrix() + stack.popPose() RenderState.popAttrib() } diff --git a/src/main/scala/li/cil/oc/common/Achievement.scala b/src/main/scala/li/cil/oc/common/Achievement.scala index a7fed0a4ee..ae08527c3e 100644 --- a/src/main/scala/li/cil/oc/common/Achievement.scala +++ b/src/main/scala/li/cil/oc/common/Achievement.scala @@ -1,6 +1,6 @@ package li.cil.oc.common -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack //placeholder @@ -9,11 +9,11 @@ object Achievement { def init() { } - def onAssemble(stack: ItemStack, player: EntityPlayer): Unit = { + def onAssemble(stack: ItemStack, player: PlayerEntity): Unit = { } - def onCraft(stack: ItemStack, player: EntityPlayer): Unit = { + def onCraft(stack: ItemStack, player: PlayerEntity): Unit = { } } @@ -25,7 +25,7 @@ import li.cil.oc.OpenComputers import li.cil.oc.api.detail.ItemInfo import li.cil.oc.common.init.Items import li.cil.oc.util.StackOption -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.stats.StatBase import net.minecraft.stats.{Achievement => MCAchievement} @@ -249,13 +249,13 @@ object Achievement { AchievementPage.registerAchievementPage(new AchievementPage(OpenComputers.Name, All: _*)) } - def onAssemble(stack: ItemStack, player: EntityPlayer): Unit = { + def onAssemble(stack: ItemStack, player: PlayerEntity): Unit = { AssemblingMap.get(Items.get(stack)).foreach(player.addStat(_, 1)) } - def onCraft(stack: ItemStack, player: EntityPlayer): Unit = { + def onCraft(stack: ItemStack, player: PlayerEntity): Unit = { CraftingMap.get(Items.get(stack)).foreach(player.addStat(_, 1)) - CustomCraftingMap.find(entry => ItemStack.areItemStacksEqual(stack, entry._1)).foreach(entry => player.addStat(entry._2, 1)) + CustomCraftingMap.find(entry => ItemStack.matches(stack, entry._1)).foreach(entry => player.addStat(entry._2, 1)) } private def newAchievement(name: String) = new AchievementBuilder(name) diff --git a/src/main/scala/li/cil/oc/common/ComponentTracker.scala b/src/main/scala/li/cil/oc/common/ComponentTracker.scala index 84e01e2860..9fbc02b039 100644 --- a/src/main/scala/li/cil/oc/common/ComponentTracker.scala +++ b/src/main/scala/li/cil/oc/common/ComponentTracker.scala @@ -3,9 +3,10 @@ package li.cil.oc.common import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import li.cil.oc.api.network.ManagedEnvironment +import net.minecraft.util.RegistryKey import net.minecraft.world.World import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -17,10 +18,10 @@ import scala.collection.mutable * containers. For now this is only used for screens / text buffer components. */ abstract class ComponentTracker { - private val worlds = mutable.Map.empty[Int, Cache[String, ManagedEnvironment]] + private val worlds = mutable.Map.empty[RegistryKey[World], Cache[String, ManagedEnvironment]] private def components(world: World) = { - worlds.getOrElseUpdate(world.provider.getDimension, + worlds.getOrElseUpdate(world.dimension, com.google.common.cache.CacheBuilder.newBuilder(). weakValues(). asInstanceOf[CacheBuilder[String, ManagedEnvironment]]. @@ -46,7 +47,9 @@ abstract class ComponentTracker { } @SubscribeEvent - def onWorldUnload(e: WorldEvent.Unload): Unit = clear(e.getWorld) + def onWorldUnload(e: WorldEvent.Unload): Unit = e.getWorld match { + case world: World => clear(world) + } protected def clear(world: World): Unit = this.synchronized { components(world).invalidateAll() diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 1775b799e2..b4e36be026 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -15,7 +15,6 @@ import li.cil.oc.api.network.Environment import li.cil.oc.api.network.SidedComponent import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.client.renderer.PetRenderer -import li.cil.oc.common.asm.ClassTransformer import li.cil.oc.common.capabilities.CapabilityColored import li.cil.oc.common.capabilities.CapabilityEnvironment import li.cil.oc.common.capabilities.CapabilitySidedComponent @@ -25,7 +24,6 @@ import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.item.data.TabletData import li.cil.oc.common.item.traits -import li.cil.oc.common.recipe.Recipes import li.cil.oc.common.tileentity.Robot import li.cil.oc.common.tileentity.traits.power import li.cil.oc.integration.Mods @@ -38,27 +36,31 @@ import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.StackOption._ import li.cil.oc.util._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.init.SoundEvents +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.util.SoundEvents import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity import net.minecraft.util.SoundCategory +import net.minecraft.util.Util +import net.minecraft.world.chunk.Chunk +import net.minecraft.world.server.ChunkHolder +import net.minecraft.world.server.ChunkManager +import net.minecraft.world.server.ServerWorld +import net.minecraftforge.client.event.ClientPlayerNetworkEvent import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.AttachCapabilitiesEvent +import net.minecraftforge.event.TickEvent +import net.minecraftforge.event.TickEvent.ClientTickEvent +import net.minecraftforge.event.TickEvent.ServerTickEvent import net.minecraftforge.event.entity.EntityJoinWorldEvent +import net.minecraftforge.event.entity.player.PlayerEvent._ import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.FMLCommonHandler -import net.minecraftforge.fml.common.Optional -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.PlayerEvent._ -import net.minecraftforge.fml.common.gameevent.TickEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent -import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -113,12 +115,11 @@ object EventHandler { } } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) def scheduleAE2Add(tileEntity: power.AppliedEnergistics2): Unit = { if (SideTracker.isServer) pendingServer.synchronized { tileEntity match { case tile: IGridBlock => - pendingServer += (() => if (!tileEntity.isInvalid) { + pendingServer += (() => if (!tileEntity.isRemoved) { tileEntity.getGridNode(AEPartLocation.INTERNAL).updateState() }) case _ => @@ -140,10 +141,22 @@ object EventHandler { if (!event.getCapabilities.containsKey(traits.Chargeable.KEY)) { event.getObject match { case stack: ItemStack => stack.getItem match { - case chargeable: traits.Chargeable => event.addCapability(traits.Chargeable.KEY, new traits.Chargeable.Provider(stack, chargeable)) + case chargeable: traits.Chargeable => { + val provider = new traits.Chargeable.Provider(stack, chargeable) + event.addCapability(traits.Chargeable.KEY, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } case _: li.cil.oc.api.driver.item.Chargeable => li.cil.oc.common.item.Delegator.subItem(stack) match { - case Some(subItem: traits.Chargeable) => event.addCapability(traits.Chargeable.KEY, new traits.Chargeable.Provider(stack, subItem)) + case Some(subItem: traits.Chargeable) => { + val provider = new traits.Chargeable.Provider(stack, subItem) + event.addCapability(traits.Chargeable.KEY, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } case _ => } case _ => @@ -156,22 +169,42 @@ object EventHandler { @SubscribeEvent def onAttachCapabilities(event: AttachCapabilitiesEvent[TileEntity]): Unit = { event.getObject match { - case tileEntity: TileEntity with Environment => - event.addCapability(CapabilityEnvironment.ProviderEnvironment, new CapabilityEnvironment.Provider(tileEntity)) + case tileEntity: TileEntity with Environment => { + val provider = new CapabilityEnvironment.Provider(tileEntity) + event.addCapability(CapabilityEnvironment.ProviderEnvironment, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } case _ => } event.getObject match { - case tileEntity: TileEntity with Environment with SidedComponent => - event.addCapability(CapabilitySidedComponent.SidedComponent, new CapabilitySidedComponent.Provider(tileEntity)) - case tileEntity: TileEntity with SidedEnvironment => - event.addCapability(CapabilitySidedEnvironment.ProviderSidedEnvironment, new CapabilitySidedEnvironment.Provider(tileEntity)) + case tileEntity: TileEntity with Environment with SidedComponent => { + val provider = new CapabilitySidedComponent.Provider(tileEntity) + event.addCapability(CapabilitySidedComponent.SidedComponent, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } + case tileEntity: TileEntity with SidedEnvironment => { + val provider = new CapabilitySidedEnvironment.Provider(tileEntity) + event.addCapability(CapabilitySidedEnvironment.ProviderSidedEnvironment, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } case _ => } event.getObject match { - case tileEntity: TileEntity with Colored => - event.addCapability(CapabilityColored.ProviderColored, new CapabilityColored.Provider(tileEntity)) + case tileEntity: TileEntity with Colored => { + val provider = new CapabilityColored.Provider(tileEntity) + event.addCapability(CapabilityColored.ProviderColored, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) + } case _ => } } @@ -198,7 +231,7 @@ object EventHandler { val invalid = mutable.ArrayBuffer.empty[Robot] runningRobots.foreach(robot => { - if (robot.isInvalid) invalid += robot + if (robot.isRemoved) invalid += robot else if (robot.world != null) robot.machine.update() }) runningRobots --= invalid @@ -230,20 +263,11 @@ object EventHandler { @SubscribeEvent def playerLoggedIn(e: PlayerLoggedInEvent) { - if (SideTracker.isServer) e.player match { + if (SideTracker.isServer) e.getPlayer match { case _: FakePlayer => // Nope - case player: EntityPlayerMP => + case player: ServerPlayerEntity => if (!LuaStateFactory.isAvailable && !LuaStateFactory.luajRequested) { - player.sendMessage(Localization.Chat.WarningLuaFallback) - } - if (Recipes.hadErrors) { - player.sendMessage(Localization.Chat.WarningRecipes) - } - if (ClassTransformer.hadErrors) { - player.sendMessage(Localization.Chat.WarningClassTransformer) - } - if (ClassTransformer.hadSimpleComponentErrors) { - player.sendMessage(Localization.Chat.WarningSimpleComponent) + player.sendMessage(Localization.Chat.WarningLuaFallback, Util.NIL_UUID) } // Gaaah, MC 1.8 y u do this to me? Sending the packets here directly can lead to them // arriving on the client before it has a world and player instance, which causes all @@ -253,11 +277,11 @@ object EventHandler { ServerPacketSender.sendLootDisks(player) }) // Do update check in local games and for OPs. - val server = FMLCommonHandler.instance.getMinecraftServerInstance - if (!server.isDedicatedServer || server.getPlayerList.canSendCommands(player.getGameProfile)) { + val server = ServerLifecycleHooks.getCurrentServer + if (server.getPlayerList.isOp(player.getGameProfile)) { Future { UpdateCheck.info onSuccess { - case Some(release) => player.sendMessage(Localization.Chat.InfoNewVersion(release.tag_name)) + case Some(release) => player.sendMessage(Localization.Chat.InfoNewVersion(release.tag_name), Util.NIL_UUID) } } } @@ -266,7 +290,7 @@ object EventHandler { } @SubscribeEvent - def clientLoggedIn(e: ClientConnectedToServerEvent) { + def clientLoggedIn(e: ClientPlayerNetworkEvent.LoggedInEvent) { PetRenderer.isInitialized = false PetRenderer.hidden.clear() Loot.disksForClient.clear() @@ -278,14 +302,14 @@ object EventHandler { @SubscribeEvent def onBlockBreak(e: BlockEvent.BreakEvent): Unit = { - e.getWorld.getTileEntity(e.getPos) match { + e.getWorld.getBlockEntity(e.getPos) match { case c: tileentity.Case => - if (c.isCreative && (!e.getPlayer.capabilities.isCreativeMode || !c.canInteract(e.getPlayer.getName))) { + if (c.isCreative && (!e.getPlayer.isCreative || !c.canInteract(e.getPlayer.getName.getString))) { e.setCanceled(true) } case r: tileentity.RobotProxy => val robot = r.robot - if (robot.isCreative && (!e.getPlayer.capabilities.isCreativeMode || !robot.canInteract(e.getPlayer.getName))) { + if (robot.isCreative && (!e.getPlayer.isCreative || !robot.canInteract(e.getPlayer.getName.getString))) { e.setCanceled(true) } case _ => @@ -294,27 +318,27 @@ object EventHandler { @SubscribeEvent def onPlayerRespawn(e: PlayerRespawnEvent) { - keyboards.foreach(_.releasePressedKeys(e.player)) + keyboards.foreach(_.releasePressedKeys(e.getPlayer)) } @SubscribeEvent def onPlayerChangedDimension(e: PlayerChangedDimensionEvent) { - keyboards.foreach(_.releasePressedKeys(e.player)) + keyboards.foreach(_.releasePressedKeys(e.getPlayer)) } @SubscribeEvent def onPlayerLogout(e: PlayerLoggedOutEvent) { - keyboards.foreach(_.releasePressedKeys(e.player)) + keyboards.foreach(_.releasePressedKeys(e.getPlayer)) } @SubscribeEvent def onEntityJoinWorld(e: EntityJoinWorldEvent): Unit = { - if (Settings.get.giveManualToNewPlayers && !e.getWorld.isRemote) e.getEntity match { - case player: EntityPlayer if !player.isInstanceOf[FakePlayer] => + if (Settings.get.giveManualToNewPlayers && !e.getWorld.isClientSide) e.getEntity match { + case player: PlayerEntity if !player.isInstanceOf[FakePlayer] => val persistedData = PlayerUtils.persistedData(player) if (!persistedData.getBoolean(Settings.namespace + "receivedManual")) { - persistedData.setBoolean(Settings.namespace + "receivedManual", true) - player.inventory.addItemStackToInventory(api.Items.get(Constants.ItemName.Manual).createItemStack(1)) + persistedData.putBoolean(Settings.namespace + "receivedManual", true) + player.inventory.add(api.Items.get(Constants.ItemName.Manual).createItemStack(1)) } case _ => } @@ -333,8 +357,8 @@ object EventHandler { didRecraft = recraft(e, navigationUpgrade, stack => { // Restore the map currently used in the upgrade. - Option(api.Driver.driverFor(e.crafting)) match { - case Some(driver) => StackOption(new ItemStack(driver.dataTag(stack).getCompoundTag(Settings.namespace + "map"))) + Option(api.Driver.driverFor(e.getCrafting)) match { + case Some(driver) => StackOption(ItemStack.of(driver.dataTag(stack).getCompound(Settings.namespace + "map"))) case _ => EmptyStack } }) || didRecraft @@ -360,21 +384,21 @@ object EventHandler { }) || didRecraft // Presents? - e.player match { + e.getPlayer match { case _: FakePlayer => // No presents for you, automaton. Such discrimination. Much bad conscience. - case player: EntityPlayerMP if player.getEntityWorld != null && !player.getEntityWorld.isRemote => + case player: ServerPlayerEntity if player.level != null && !player.level.isClientSide => // Presents!? If we didn't recraft, it's an OC item, and the time is right... - if (Settings.get.presentChance > 0 && !didRecraft && api.Items.get(e.crafting) != null && - e.player.getRNG.nextFloat() < Settings.get.presentChance && timeForPresents) { + if (Settings.get.presentChance > 0 && !didRecraft && api.Items.get(e.getCrafting) != null && + e.getPlayer.getRandom.nextFloat() < Settings.get.presentChance && timeForPresents) { // Presents! val present = api.Items.get(Constants.ItemName.Present).createItemStack(1) - e.player.world.playSound(e.player, e.player.posX, e.player.posY, e.player.posZ, SoundEvents.BLOCK_NOTE_PLING, SoundCategory.MASTER, 0.2f, 1f) - InventoryUtils.addToPlayerInventory(present, e.player) + e.getPlayer.level.playSound(e.getPlayer, e.getPlayer.getX, e.getPlayer.getY, e.getPlayer.getZ, SoundEvents.NOTE_BLOCK_PLING, SoundCategory.MASTER, 0.2f, 1f) + InventoryUtils.addToPlayerInventory(present, e.getPlayer) } case _ => // Nope. } - Achievement.onCraft(e.crafting, e.player) + Achievement.onCraft(e.getCrafting, e.getPlayer) } @SubscribeEvent @@ -382,8 +406,8 @@ object EventHandler { val entity = e.getOriginalEntity Option(entity).flatMap(e => Option(e.getItem)) match { case Some(stack) => - Achievement.onAssemble(stack, e.player) - Achievement.onCraft(stack, e.player) + Achievement.onAssemble(stack, e.getPlayer) + Achievement.onCraft(stack, e.getPlayer) case _ => // Huh. } } @@ -409,12 +433,12 @@ object EventHandler { } private def recraft(e: ItemCraftedEvent, item: ItemInfo, callback: ItemStack => StackOption): Boolean = { - if (api.Items.get(e.crafting) == item) { - for (slot <- 0 until e.craftMatrix.getSizeInventory) { - val stack = e.craftMatrix.getStackInSlot(slot) + if (api.Items.get(e.getCrafting) == item) { + for (slot <- 0 until e.getInventory.getContainerSize) { + val stack = e.getInventory.getItem(slot) if (api.Items.get(stack) == item) { callback(stack).foreach(extra => - InventoryUtils.addToPlayerInventory(extra, e.player)) + InventoryUtils.addToPlayerInventory(extra, e.getPlayer)) } } true @@ -422,19 +446,44 @@ object EventHandler { else false } + private lazy val getChunks = try { + val m = classOf[ChunkManager].getDeclaredMethod("getChunks") // getChunks + m.setAccessible(true) + m + } + catch { + case e: Throwable => + throw new Error("Could not access server chunk list", e) + } + + private def getChunks(world: ServerWorld): Iterable[ChunkHolder] = try { + getChunks.invoke(world.getChunkSource.chunkMap).asInstanceOf[Iterable[ChunkHolder]] + } + catch { + case e: Throwable => + throw new Error("Could not access server chunk list", e) + } + // This is called from the ServerThread *and* the ClientShutdownThread, which // can potentially happen at the same time... for whatever reason. So let's // synchronize what we're doing here to avoid race conditions (e.g. when // disposing networks, where this actually triggered an assert). @SubscribeEvent def onWorldUnload(e: WorldEvent.Unload): Unit = this.synchronized { - if (!e.getWorld.isRemote) { - e.getWorld.loadedTileEntityList.collect { + if (!e.getWorld.isClientSide) { + val world = e.getWorld.asInstanceOf[ServerWorld] + world.blockEntityList.collect { case te: tileentity.traits.TileEntity => te.dispose() } - e.getWorld.loadedEntityList.collect { - case host: MachineHost => host.machine.stop() - } + + getChunks(world).foreach(holder => { + val chunk = holder.getTickingChunk + if (chunk != null) chunk.getEntitySections.foreach { + _.iterator.collect { + case host: MachineHost => host.machine.stop() + } + } + }) Callbacks.clear() } @@ -444,18 +493,20 @@ object EventHandler { } @SubscribeEvent - def onChunkUnload(e: ChunkEvent.Unload): Unit = { - if (!e.getWorld.isRemote) { - e.getChunk.getEntityLists.foreach(_.collect { - case host: MachineHost => host.machine match { - case machine: Machine => scheduleClose(machine) - case _ => // Dafuq? - } - case rack: Rack => - (0 until rack.getSizeInventory). - map(rack.getMountable). - collect { case server: Server if server.machine != null => server.machine.stop() } - }) + def onChunkUnloaded(e: ChunkEvent.Unload): Unit = { + if (!e.getWorld.isClientSide) e.getChunk match { + case chunk: Chunk => { + chunk.getEntitySections.foreach(_.collect { + case host: MachineHost => host.machine match { + case machine: Machine => scheduleClose(machine) + case _ => // Dafuq? + } + case rack: Rack => + (0 until rack.getContainerSize). + map(rack.getMountable). + collect { case server: Server if server.machine != null => server.machine.stop() } + }) + } } } } diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index 59e6f8b758..bcc793fc45 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -5,80 +5,81 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.server.component.{DiskDriveMountable, Server} -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.Hand import net.minecraft.world.World -import net.minecraftforge.fml.common.network.IGuiHandler -abstract class GuiHandler extends IGuiHandler { - override def getServerGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int): AnyRef = { +@Deprecated +abstract class GuiHandler { + def getServerGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = { GuiType.Categories.get(id) match { case Some(GuiType.Category.Block) => - world.getTileEntity(BlockPosition(x, GuiType.extractY(y), z)) match { + world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { case t: tileentity.Adapter if id == GuiType.Adapter.id => - new container.Adapter(player.inventory, t) + new container.Adapter(containerId, player.inventory, t) case t: tileentity.Assembler if id == GuiType.Assembler.id => - new container.Assembler(player.inventory, t) + new container.Assembler(containerId, player.inventory, t) case t: tileentity.Charger if id == GuiType.Charger.id => - new container.Charger(player.inventory, t) + new container.Charger(containerId, player.inventory, t) case t: tileentity.Case if id == GuiType.Case.id => - new container.Case(player.inventory, t) + new container.Case(containerId, player.inventory, t) case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - new container.Disassembler(player.inventory, t) + new container.Disassembler(containerId, player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - new container.DiskDrive(player.inventory, t) + new container.DiskDrive(containerId, player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => - new container.Printer(player.inventory, t) + new container.Printer(containerId, player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => - new container.Raid(player.inventory, t) + new container.Raid(containerId, player.inventory, t) case t: tileentity.Relay if id == GuiType.Relay.id => - new container.Relay(player.inventory, t) + new container.Relay(containerId, player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new container.Robot(player.inventory, t.robot) + new container.Robot(containerId, player.inventory, t.robot) case t: tileentity.Rack if id == GuiType.Rack.id => - new container.Rack(player.inventory, t) + new container.Rack(containerId, player.inventory, t) case t: tileentity.Rack if id == GuiType.ServerInRack.id => val slot = GuiType.extractSlot(y) val server = t.getMountable(slot).asInstanceOf[Server] - new container.Server(player.inventory, server, Option(server)) + new container.Server(containerId, player.inventory, server, Option(server)) case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => val slot = GuiType.extractSlot(y) val drive = t.getMountable(slot).asInstanceOf[DiskDriveMountable] - new container.DiskDrive(player.inventory, drive) + new container.DiskDrive(containerId, player.inventory, drive) case _ => null } case Some(GuiType.Category.Entity) => - world.getEntityByID(x) match { + world.getEntity(x) match { case drone: entity.Drone if id == GuiType.Drone.id => - new container.Drone(player.inventory, drone) + new container.Drone(containerId, player.inventory, drone) case _ => null } case Some(GuiType.Category.Item) => { val itemStackInUse = getItemStackInUse(id, player) Delegator.subItem(itemStackInUse) match { case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => - new container.Database(player.inventory, new DatabaseInventory { + new container.Database(containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse - override def isUsableByPlayer(player: EntityPlayer) = player == player + override def stillValid(player: PlayerEntity) = player == player }) case Some(server: item.Server) if id == GuiType.Server.id => - new container.Server(player.inventory, new ServerInventory { + new container.Server(containerId, player.inventory, new ServerInventory { override def container = itemStackInUse - override def isUsableByPlayer(player: EntityPlayer) = player == player + override def stillValid(player: PlayerEntity) = player == player }) case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => val stack = itemStackInUse - if (stack.hasTagCompound) - new container.Tablet(player.inventory, item.Tablet.get(stack, player)) + if (stack.hasTag) + new container.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) else null case Some(drive: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => - new container.DiskDrive(player.inventory, new DiskDriveMountableInventory { + new container.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { override def container: ItemStack = itemStackInUse - override def isUsableByPlayer(player: EntityPlayer) = player == player + override def stillValid(player: PlayerEntity) = player == player }) case _ => null } @@ -87,8 +88,10 @@ abstract class GuiHandler extends IGuiHandler { } } - def getItemStackInUse(id: Int, player: EntityPlayer): ItemStack = { - val mainItem: ItemStack = player.getHeldItemMainhand + def getClientGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = null + + def getItemStackInUse(id: Int, player: PlayerEntity): ItemStack = { + val mainItem: ItemStack = player.getItemInHand(Hand.MAIN_HAND) Delegator.subItem(mainItem) match { case Some(drive: item.traits.FileSystemLike) if id == GuiType.Drive.id => mainItem case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => mainItem @@ -97,7 +100,7 @@ abstract class GuiHandler extends IGuiHandler { case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => mainItem case Some(terminal: item.Terminal) if id == GuiType.Terminal.id => mainItem case Some(drive: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => mainItem - case _ => player.inventory.offHandInventory.get(0) + case _ => player.getItemInHand(Hand.OFF_HAND) } } } diff --git a/src/main/scala/li/cil/oc/common/IMC.scala b/src/main/scala/li/cil/oc/common/IMC.scala index 3401977ba1..264a0507e9 100644 --- a/src/main/scala/li/cil/oc/common/IMC.scala +++ b/src/main/scala/li/cil/oc/common/IMC.scala @@ -14,94 +14,93 @@ import li.cil.oc.integration.util.Wrench import li.cil.oc.server.driver.Registry import li.cil.oc.server.machine.ProgramLocations import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagString +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.StringNBT import net.minecraft.util.math.BlockPos import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.common.event.FMLInterModComms.IMCEvent +import net.minecraftforge.fml.InterModComms.IMCMessage import scala.collection.convert.WrapAsScala._ object IMC { - def handleEvent(e: IMCEvent): Unit = { - for (message <- e.getMessages) { - if (message.key == api.IMC.REGISTER_ASSEMBLER_TEMPLATE && message.isNBTMessage) { - if (message.getNBTValue.hasKey("name", NBT.TAG_STRING)) - OpenComputers.log.debug(s"Registering new assembler template '${message.getNBTValue.getString("name")}' from mod ${message.getSender}.") + def handleMessage(message: IMCMessage): Unit = { + message.getMessageSupplier.get.asInstanceOf[AnyRef] match { + case template: CompoundNBT if message.getMethod == api.IMC.REGISTER_ASSEMBLER_TEMPLATE => { + if (template.contains("name", NBT.TAG_STRING)) + OpenComputers.log.debug(s"Registering new assembler template '${template.getString("name")}' from mod ${message.getSenderModId}.") else - OpenComputers.log.debug(s"Registering new, unnamed assembler template from mod ${message.getSender}.") - try AssemblerTemplates.add(message.getNBTValue) catch { + OpenComputers.log.debug(s"Registering new, unnamed assembler template from mod ${message.getSenderModId}.") + try AssemblerTemplates.add(template) catch { case t: Throwable => OpenComputers.log.warn("Failed registering assembler template.", t) } } - else if (message.key == api.IMC.REGISTER_DISASSEMBLER_TEMPLATE && message.isNBTMessage) { - if (message.getNBTValue.hasKey("name", NBT.TAG_STRING)) - OpenComputers.log.debug(s"Registering new disassembler template '${message.getNBTValue.getString("name")}' from mod ${message.getSender}.") + case template: CompoundNBT if message.getMethod == api.IMC.REGISTER_DISASSEMBLER_TEMPLATE => { + if (template.contains("name", NBT.TAG_STRING)) + OpenComputers.log.debug(s"Registering new disassembler template '${template.getString("name")}' from mod ${message.getSenderModId}.") else - OpenComputers.log.debug(s"Registering new, unnamed disassembler template from mod ${message.getSender}.") - try DisassemblerTemplates.add(message.getNBTValue) catch { + OpenComputers.log.debug(s"Registering new, unnamed disassembler template from mod ${message.getSenderModId}.") + try DisassemblerTemplates.add(template) catch { case t: Throwable => OpenComputers.log.warn("Failed registering disassembler template.", t) } } - else if (message.key == api.IMC.REGISTER_TOOL_DURABILITY_PROVIDER && message.isStringMessage) { - OpenComputers.log.debug(s"Registering new tool durability provider '${message.getStringValue}' from mod ${message.getSender}.") - try ToolDurabilityProviders.add(getStaticMethod(message.getStringValue, classOf[ItemStack])) catch { + case name: String if message.getMethod == api.IMC.REGISTER_TOOL_DURABILITY_PROVIDER => { + OpenComputers.log.debug(s"Registering new tool durability provider '${name}' from mod ${message.getSenderModId}.") + try ToolDurabilityProviders.add(getStaticMethod(name, classOf[ItemStack])) catch { case t: Throwable => OpenComputers.log.warn("Failed registering tool durability provider.", t) } } - else if (message.key == api.IMC.REGISTER_WRENCH_TOOL && message.isStringMessage) { - OpenComputers.log.debug(s"Registering new wrench usage '${message.getStringValue}' from mod ${message.getSender}.") - try Wrench.addUsage(getStaticMethod(message.getStringValue, classOf[EntityPlayer], classOf[BlockPos], classOf[Boolean])) catch { + case name: String if message.getMethod == api.IMC.REGISTER_WRENCH_TOOL => { + OpenComputers.log.debug(s"Registering new wrench usage '${name}' from mod ${message.getSenderModId}.") + try Wrench.addUsage(getStaticMethod(name, classOf[PlayerEntity], classOf[BlockPos], classOf[Boolean])) catch { case t: Throwable => OpenComputers.log.warn("Failed registering wrench usage.", t) } } - else if (message.key == api.IMC.REGISTER_WRENCH_TOOL_CHECK && message.isStringMessage) { - OpenComputers.log.debug(s"Registering new wrench tool check '${message.getStringValue}' from mod ${message.getSender}.") - try Wrench.addCheck(getStaticMethod(message.getStringValue, classOf[ItemStack])) catch { + case name: String if message.getMethod == api.IMC.REGISTER_WRENCH_TOOL_CHECK => { + OpenComputers.log.debug(s"Registering new wrench tool check '${name}' from mod ${message.getSenderModId}.") + try Wrench.addCheck(getStaticMethod(name, classOf[ItemStack])) catch { case t: Throwable => OpenComputers.log.warn("Failed registering wrench check.", t) } } - else if (message.key == api.IMC.REGISTER_ITEM_CHARGE && message.isNBTMessage) { - OpenComputers.log.debug(s"Registering new item charge implementation '${message.getNBTValue.getString("name")}' from mod ${message.getSender}.") + case implInfo: CompoundNBT if message.getMethod == api.IMC.REGISTER_ITEM_CHARGE => { + OpenComputers.log.debug(s"Registering new item charge implementation '${implInfo.getString("name")}' from mod ${message.getSenderModId}.") try ItemCharge.add( - getStaticMethod(message.getNBTValue.getString("canCharge"), classOf[ItemStack]), - getStaticMethod(message.getNBTValue.getString("charge"), classOf[ItemStack], classOf[Double], classOf[Boolean]) + getStaticMethod(implInfo.getString("canCharge"), classOf[ItemStack]), + getStaticMethod(implInfo.getString("charge"), classOf[ItemStack], classOf[Double], classOf[Boolean]) ) catch { case t: Throwable => OpenComputers.log.warn("Failed registering item charge implementation.", t) } } - else if (message.key == api.IMC.BLACKLIST_PERIPHERAL && message.isStringMessage) { - OpenComputers.log.debug(s"Blacklisting CC peripheral '${message.getStringValue}' as requested by mod ${message.getSender}.") - if (!Settings.get.peripheralBlacklist.contains(message.getStringValue)) { - Settings.get.peripheralBlacklist.add(message.getStringValue) + case name: String if message.getMethod == api.IMC.BLACKLIST_PERIPHERAL => { + OpenComputers.log.debug(s"Blacklisting CC peripheral '${name}' as requested by mod ${message.getSenderModId}.") + if (!Settings.get.peripheralBlacklist.contains(name)) { + Settings.get.peripheralBlacklist.add(name) } } - else if (message.key == api.IMC.BLACKLIST_HOST && message.isNBTMessage) { - OpenComputers.log.debug(s"Blacklisting component '${message.getNBTValue.getString("name")}' for host '${message.getNBTValue.getString("host")}' as requested by mod ${message.getSender}.") - try Registry.blacklistHost(new ItemStack(message.getNBTValue.getCompoundTag("item")), Class.forName(message.getNBTValue.getString("host"))) catch { + case compInfo: CompoundNBT if message.getMethod == api.IMC.BLACKLIST_HOST => { + OpenComputers.log.debug(s"Blacklisting component '${compInfo.getString("name")}' for host '${compInfo.getString("host")}' as requested by mod ${message.getSenderModId}.") + try Registry.blacklistHost(ItemStack.of(compInfo.getCompound("item")), Class.forName(compInfo.getString("host"))) catch { case t: Throwable => OpenComputers.log.warn("Failed blacklisting component.", t) } } - else if (message.key == api.IMC.REGISTER_ASSEMBLER_FILTER && message.isStringMessage) { - OpenComputers.log.debug(s"Registering new assembler template filter '${message.getStringValue}' from mod ${message.getSender}.") - try AssemblerTemplates.addFilter(message.getStringValue) catch { + case name: String if message.getMethod == api.IMC.REGISTER_ASSEMBLER_FILTER => { + OpenComputers.log.debug(s"Registering new assembler template filter '${name}' from mod ${message.getSenderModId}.") + try AssemblerTemplates.addFilter(name) catch { case t: Throwable => OpenComputers.log.warn("Failed registering assembler template filter.", t) } } - else if (message.key == api.IMC.REGISTER_INK_PROVIDER && message.isStringMessage) { - OpenComputers.log.debug(s"Registering new ink provider '${message.getStringValue}' from mod ${message.getSender}.") - try PrintData.addInkProvider(getStaticMethod(message.getStringValue, classOf[ItemStack])) catch { + case name: String if message.getMethod == api.IMC.REGISTER_INK_PROVIDER => { + OpenComputers.log.debug(s"Registering new ink provider '${name}' from mod ${message.getSenderModId}.") + try PrintData.addInkProvider(getStaticMethod(name, classOf[ItemStack])) catch { case t: Throwable => OpenComputers.log.warn("Failed registering ink provider.", t) } } - else if (message.key == api.IMC.REGISTER_PROGRAM_DISK_LABEL && message.isNBTMessage) { - OpenComputers.log.debug(s"Registering new program location mapping for program '${message.getNBTValue.getString("program")}' being on disk '${message.getNBTValue.getString("label")}' from mod ${message.getSender}.") - ProgramLocations.addMapping(message.getNBTValue.getString("program"), message.getNBTValue.getString("label"), message.getNBTValue.getTagList("architectures", NBT.TAG_STRING).map((tag: NBTTagString) => tag.getString()).toArray: _*) - } - else { - OpenComputers.log.warn(s"Got an unrecognized or invalid IMC message '${message.key}' from mod ${message.getSender}.") + case diskInfo: CompoundNBT if message.getMethod == api.IMC.REGISTER_PROGRAM_DISK_LABEL => { + OpenComputers.log.debug(s"Registering new program location mapping for program '${diskInfo.getString("program")}' being on disk '${diskInfo.getString("label")}' from mod ${message.getSenderModId}.") + ProgramLocations.addMapping(diskInfo.getString("program"), diskInfo.getString("label"), diskInfo.getList("architectures", NBT.TAG_STRING).map((tag: StringNBT) => tag.getAsString()).toArray: _*) } + case _ => OpenComputers.log.warn(s"Got an unrecognized or invalid IMC message '${message.getMethod}' from mod ${message.getSenderModId}.") } } diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index 511e34ac17..303edf3c49 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -11,19 +11,23 @@ import li.cil.oc.api import li.cil.oc.api.fs.FileSystem import li.cil.oc.common.init.Items import li.cil.oc.util.Color -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.common.DimensionManager +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld +import net.minecraft.world.storage.FolderName import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.ModLoadingContext +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -//class Loot extends WeightedRandomChestContent(api.Items.get(Constants.ItemName.Floppy).item(), api.Items.get(Constants.ItemName.Floppy).createItemStack(1).getItemDamage, 1, 1, Settings.get.lootProbability) { +//class Loot extends WeightedRandomChestContent(api.Items.get(Constants.ItemName.Floppy).item(), api.Items.get(Constants.ItemName.Floppy).createItemStack(1).getDamageValue, 1, 1, Settings.get.lootProbability) { // override def generateChestContent(random: Random, newInventory: IInventory) = // Loot.randomDisk(random) match { // case Some(disk) => @@ -55,31 +59,29 @@ object Loot { val disksForClient = mutable.ArrayBuffer.empty[ItemStack] - def isLootDisk(stack: ItemStack): Boolean = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy) && stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "lootFactory", NBT.TAG_STRING) + def isLootDisk(stack: ItemStack): Boolean = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy) && stack.hasTag && stack.getTag.contains(Settings.namespace + "lootFactory", NBT.TAG_STRING) def randomDisk(rng: Random) = if (disksForSampling.nonEmpty) Some(disksForSampling(rng.nextInt(disksForSampling.length))) else None - def registerLootDisk(name: String, color: EnumDyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { - val mod = Loader.instance.activeModContainer.getModId + def registerLootDisk(name: String, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { + val mod = ModLoadingContext.get.getActiveContainer.getModId OpenComputers.log.debug(s"Registering loot disk '$name' from mod $mod.") val modSpecificName = mod + ":" + name - val data = new NBTTagCompound() - data.setString(Settings.namespace + "fs.label", name) + val data = new CompoundNBT() + data.putString(Settings.namespace + "fs.label", name) - val nbt = new NBTTagCompound() - nbt.setTag(Settings.namespace + "data", data) + val stack = Items.get(Constants.ItemName.Floppy).createItemStack(1) + val nbt = stack.getOrCreateTag + nbt.put(Settings.namespace + "data", data) // Store this top level, so it won't get wiped on save. - nbt.setString(Settings.namespace + "lootFactory", modSpecificName) - nbt.setInteger(Settings.namespace + "color", color.getDyeDamage) - - val stack = Items.get(Constants.ItemName.Floppy).createItemStack(1) - stack.setTagCompound(nbt) + nbt.putString(Settings.namespace + "lootFactory", modSpecificName) + nbt.putInt(Settings.namespace + "color", color.getId) Loot.factories += modSpecificName -> factory @@ -103,13 +105,13 @@ object Loot { } @SubscribeEvent - def initForWorld(e: WorldEvent.Load): Unit = if (!e.getWorld.isRemote && e.getWorld.provider.getDimension == 0) { - worldDisks.clear() - disksForSampling.clear() - if (!e.getWorld.isRemote) { - val path = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + "loot/") + def initForWorld(e: WorldEvent.Load): Unit = e.getWorld match { + case world: ServerWorld if world.dimension == World.OVERWORLD => { + worldDisks.clear() + disksForSampling.clear() + val path = world.getServer.getWorldPath(new FolderName(Settings.savePath)).toFile if (path.exists && path.isDirectory) { - val listFile = new io.File(path, "loot.properties") + val listFile = new io.File(path, "loot/loot.properties") if (listFile.exists && listFile.isFile) { try { val listStream = new io.FileInputStream(listFile) @@ -123,13 +125,13 @@ object Loot { } } } - } - for (entry <- globalDisks if !worldDisks.contains(entry)) { - worldDisks += entry - } - for ((stack, count) <- worldDisks) { - for (i <- 0 until count) { - disksForSampling += stack + for (entry <- globalDisks if !worldDisks.contains(entry)) { + worldDisks += entry + } + for ((stack, count) <- worldDisks) { + for (i <- 0 until count) { + disksForSampling += stack + } } } } @@ -151,14 +153,14 @@ object Loot { } } - def createLootDisk(name: String, path: String, external: Boolean, color: Option[EnumDyeColor] = None) = { + def createLootDisk(name: String, path: String, external: Boolean, color: Option[DyeColor] = None) = { val callable = if (external) new Callable[FileSystem] { override def call(): FileSystem = api.FileSystem.asReadOnly(api.FileSystem.fromSaveDirectory("loot/" + path, 0, false)) } else new Callable[FileSystem] { override def call(): FileSystem = api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, "loot/" + path) } - val stack = registerLootDisk(path, color.getOrElse(EnumDyeColor.SILVER), callable, doRecipeCycling = true) - stack.setStackDisplayName(name) + val stack = registerLootDisk(path, color.getOrElse(DyeColor.LIGHT_GRAY), callable, doRecipeCycling = true) + stack.setHoverName(new StringTextComponent(name)) if (!external) { Items.registerStack(stack, path) } diff --git a/src/main/scala/li/cil/oc/common/PacketBuilder.scala b/src/main/scala/li/cil/oc/common/PacketBuilder.scala index 27d0bb8012..382aa44236 100644 --- a/src/main/scala/li/cil/oc/common/PacketBuilder.scala +++ b/src/main/scala/li/cil/oc/common/PacketBuilder.scala @@ -1,5 +1,7 @@ package li.cil.oc.common +import java.util.function.Supplier + import java.io.BufferedOutputStream import java.io.ByteArrayOutputStream import java.io.DataOutputStream @@ -11,33 +13,36 @@ import io.netty.buffer.Unpooled import li.cil.oc.OpenComputers import li.cil.oc.api.network.EnvironmentHost import net.minecraft.entity.Entity -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.network.PacketBuffer +import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.world.World -import net.minecraftforge.fml.common.FMLCommonHandler -import net.minecraftforge.fml.common.network.internal.FMLProxyPacket +import net.minecraftforge.fml.network.PacketDistributor +import net.minecraftforge.fml.server.ServerLifecycleHooks +import net.minecraftforge.registries._ import scala.collection.convert.WrapAsScala._ abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stream) { + def writeRegistryEntry[T <: IForgeRegistryEntry[T]](registry: IForgeRegistry[T], value: T): Unit = + writeInt(registry.asInstanceOf[ForgeRegistry[T]].getID(value)) + def writeTileEntity(t: TileEntity) { - writeInt(t.getWorld.provider.getDimension) - writeInt(t.getPos.getX) - writeInt(t.getPos.getY) - writeInt(t.getPos.getZ) + writeUTF(t.getLevel.dimension.location.toString) + writeInt(t.getBlockPos.getX) + writeInt(t.getBlockPos.getY) + writeInt(t.getBlockPos.getZ) } def writeEntity(e: Entity) { - writeInt(e.world.provider.getDimension) - writeInt(e.getEntityId) + writeUTF(e.level.dimension.location.toString) + writeInt(e.getId) } - def writeDirection(d: Option[EnumFacing]) = d match { + def writeDirection(d: Option[Direction]) = d match { case Some(side) => writeByte(side.ordinal.toByte) case _ => writeByte(-1: Byte) } @@ -46,11 +51,11 @@ abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stre val haveStack = !stack.isEmpty && stack.getCount > 0 writeBoolean(haveStack) if (haveStack) { - writeNBT(stack.writeToNBT(new NBTTagCompound())) + writeNBT(stack.save(new CompoundNBT())) } } - def writeNBT(nbt: NBTTagCompound) = { + def writeNBT(nbt: CompoundNBT) = { val haveNbt = nbt != null writeBoolean(haveNbt) if (haveNbt) { @@ -60,32 +65,33 @@ abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stre def writePacketType(pt: PacketType.Value) = writeByte(pt.id) - def sendToAllPlayers() = OpenComputers.channel.sendToAll(packet) + def sendToAllPlayers() = OpenComputers.channel.send(PacketDistributor.ALL.noArg(), packet) - def sendToPlayersNearEntity(e: Entity, range: Option[Double] = None): Unit = sendToNearbyPlayers(e.getEntityWorld, e.posX, e.posY, e.posZ, range) + def sendToPlayersNearEntity(e: Entity, range: Option[Double] = None): Unit = sendToNearbyPlayers(e.level, e.getX, e.getY, e.getZ, range) - def sendToPlayersNearTileEntity(t: TileEntity, range: Option[Double] = None): Unit = sendToNearbyPlayers(t.getWorld, t.getPos.getX + 0.5, t.getPos.getY + 0.5, t.getPos.getZ + 0.5, range) + def sendToPlayersNearTileEntity(t: TileEntity, range: Option[Double] = None): Unit = sendToNearbyPlayers(t.getLevel, t.getBlockPos.getX + 0.5, t.getBlockPos.getY + 0.5, t.getBlockPos.getZ + 0.5, range) def sendToPlayersNearHost(host: EnvironmentHost, range: Option[Double] = None): Unit = sendToNearbyPlayers(host.world, host.xPosition, host.yPosition, host.zPosition, range) def sendToNearbyPlayers(world: World, x: Double, y: Double, z: Double, range: Option[Double]) { - val dimension = world.provider.getDimension - val server = FMLCommonHandler.instance.getMinecraftServerInstance + val server = ServerLifecycleHooks.getCurrentServer val manager = server.getPlayerList - for (player <- manager.getPlayers if player.dimension == dimension) { - val playerRenderDistance = 16 // ObfuscationReflectionHelper.getPrivateValue(classOf[EntityPlayerMP], player, "renderDistance").asInstanceOf[Integer] + for (player <- manager.getPlayers if player.level == world) { + val playerRenderDistance = 16 // ObfuscationReflectionHelper.getPrivateValue(classOf[ServerPlayerEntity], player, "renderDistance").asInstanceOf[Integer] val playerSpecificRange = range.getOrElse((manager.getViewDistance min playerRenderDistance) * 16.0) - if (player.getDistanceSq(x, y, z) < playerSpecificRange * playerSpecificRange) { + if (player.distanceToSqr(x, y, z) < playerSpecificRange * playerSpecificRange) { sendToPlayer(player) } } } - def sendToPlayer(player: EntityPlayerMP) = OpenComputers.channel.sendTo(packet, player) + def sendToPlayer(player: ServerPlayerEntity) = OpenComputers.channel.send(PacketDistributor.PLAYER.`with`(new Supplier[ServerPlayerEntity] { + override def get = player + }), packet) def sendToServer() = OpenComputers.channel.sendToServer(packet) - protected def packet: FMLProxyPacket + protected def packet: Array[Byte] } // Necessary to keep track of the GZIP stream. @@ -96,7 +102,7 @@ class SimplePacketBuilder(val packetType: PacketType.Value) extends PacketBuilde override protected def packet = { flush() - new FMLProxyPacket(new PacketBuffer(Unpooled.wrappedBuffer(stream.toByteArray)), "OpenComputers") + stream.toByteArray } } @@ -106,7 +112,7 @@ class CompressedPacketBuilder(val packetType: PacketType.Value, private val data override protected def packet = { flush() stream.finish() - new FMLProxyPacket(new PacketBuffer(Unpooled.wrappedBuffer(data.toByteArray)), "OpenComputers") + data.toByteArray } } diff --git a/src/main/scala/li/cil/oc/common/PacketHandler.scala b/src/main/scala/li/cil/oc/common/PacketHandler.scala index 9b32860ca0..3d4de3ff4d 100644 --- a/src/main/scala/li/cil/oc/common/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/common/PacketHandler.scala @@ -1,58 +1,56 @@ package li.cil.oc.common +import java.io.ByteArrayInputStream import java.io.DataInputStream import java.io.InputStream import java.util.zip.InflaterInputStream -import io.netty.buffer.ByteBuf -import io.netty.buffer.ByteBufInputStream import li.cil.oc.Constants import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.common.block.RobotAfterimage import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP +import li.cil.oc.util.RotationHelper +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.network.INetHandler -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import net.minecraftforge.fml.common.FMLCommonHandler +import net.minecraftforge.fml.network.NetworkDirection +import net.minecraftforge.fml.server.ServerLifecycleHooks +import net.minecraftforge.registries._ +import scala.collection.mutable.ArrayBuffer import scala.reflect.ClassTag import scala.reflect.classTag -abstract class PacketHandler { - /** Top level dispatcher based on packet type. */ - protected def onPacketData(handler: INetHandler, data: ByteBuf, player: EntityPlayer) { - val thread = FMLCommonHandler.instance.getWorldThread(handler) - if (thread.isCallingFromMinecraftThread) { - process(data, player) - } - else { - data.retain() - thread.addScheduledTask(new Runnable { - override def run(): Unit = { - process(data, player) - data.release() - } - }) - } - } +object PacketHandler { + var clientHandler: PacketHandler = _ + + var serverHandler: PacketHandler = _ - private def process(data: ByteBuf, player: EntityPlayer): Unit = { - // Don't crash on badly formatted packets (may have been altered by a - // malicious client, in which case we don't want to allow it to kill the - // server like this). Just spam the log a bit... ;) + private[oc] def handlePacket(side: NetworkDirection, arr: Array[Byte], player: PlayerEntity): Unit = { try { - val stream = new ByteBufInputStream(data) - if (stream.read() == 0) dispatch(new PacketParser(stream, player)) - else dispatch(new PacketParser(new InflaterInputStream(stream), player)) + val handler = side match { + case NetworkDirection.PLAY_TO_CLIENT => clientHandler + case NetworkDirection.PLAY_TO_SERVER => serverHandler + case _ => null + } + if (handler != null) { + val stream = new ByteArrayInputStream(arr) + if (stream.read() == 0) handler.dispatch(handler.createParser(stream, player)) + else handler.dispatch(handler.createParser(new InflaterInputStream(stream), player)) + } } catch { + // Don't crash on badly formatted packets (may have been altered by a + // malicious client, in which case we don't want to allow it to kill the + // server like this). Just spam the log a bit... ;) case e: Throwable => OpenComputers.log.warn("Received a badly formatted packet.", e) } @@ -60,11 +58,12 @@ abstract class PacketHandler { // Avoid AFK kicks by marking players as non-idle when they send packets. // This will usually be stuff like typing while in screen GUIs. player match { - case mp: EntityPlayerMP => mp.markPlayerActive() - case _ => // Uh... OK? + case mp: ServerPlayerEntity => mp.resetLastActionTime() } } +} +abstract class PacketHandler { /** * Gets the world for the specified dimension. * @@ -72,17 +71,22 @@ abstract class PacketHandler { * dimension; None otherwise. For the server it returns the world for the * specified dimension, if such a dimension exists; None otherwise. */ - protected def world(player: EntityPlayer, dimension: Int): Option[World] + protected def world(player: PlayerEntity, dimension: ResourceLocation): Option[World] protected def dispatch(p: PacketParser): Unit - protected class PacketParser(stream: InputStream, val player: EntityPlayer) extends DataInputStream(stream) { + protected def createParser(stream: InputStream, player: PlayerEntity): PacketParser + + private[oc] class PacketParser(stream: InputStream, val player: PlayerEntity) extends DataInputStream(stream) { val packetType = PacketType(readByte()) - def getTileEntity[T: ClassTag](dimension: Int, x: Int, y: Int, z: Int): Option[T] = { + def readRegistryEntry[T <: IForgeRegistryEntry[T]](registry: IForgeRegistry[T]): T = + registry.asInstanceOf[ForgeRegistry[T]].getValue(readInt()) + + def getBlockEntity[T: ClassTag](dimension: ResourceLocation, x: Int, y: Int, z: Int): Option[T] = { world(player, dimension) match { case Some(world) if world.blockExists(BlockPosition(x, y, z)) => - val t = world.getTileEntity(BlockPosition(x, y, z)) + val t = world.getBlockEntity(BlockPosition(x, y, z)) if (t != null && classTag[T].runtimeClass.isAssignableFrom(t.getClass)) { return Some(t.asInstanceOf[T]) } @@ -102,10 +106,10 @@ abstract class PacketHandler { None } - def getEntity[T: ClassTag](dimension: Int, id: Int): Option[T] = { + def getEntity[T: ClassTag](dimension: ResourceLocation, id: Int): Option[T] = { world(player, dimension) match { case Some(world) => - val e = world.getEntityByID(id) + val e = world.getEntity(id) if (e != null && classTag[T].runtimeClass.isAssignableFrom(e.getClass)) { return Some(e.asInstanceOf[T]) } @@ -114,34 +118,34 @@ abstract class PacketHandler { None } - def readTileEntity[T: ClassTag](): Option[T] = { - val dimension = readInt() + def readBlockEntity[T: ClassTag](): Option[T] = { + val dimension = new ResourceLocation(readUTF()) val x = readInt() val y = readInt() val z = readInt() - getTileEntity(dimension, x, y, z) + getBlockEntity(dimension, x, y, z) } def readEntity[T: ClassTag](): Option[T] = { - val dimension = readInt() + val dimension = new ResourceLocation(readUTF()) val id = readInt() getEntity[T](dimension, id) } - def readDirection(): Option[EnumFacing] = readByte() match { + def readDirection(): Option[Direction] = readByte() match { case id if id < 0 => None - case id => Option(EnumFacing.getFront(id)) + case id => Option(Direction.from3DDataValue(id)) } def readItemStack(): ItemStack = { val haveStack = readBoolean() if (haveStack) { - new ItemStack(readNBT()) + ItemStack.of(readNBT()) } else ItemStack.EMPTY } - def readNBT(): NBTTagCompound = { + def readNBT(): CompoundNBT = { val haveNbt = readBoolean() if (haveNbt) { CompressedStreamTools.read(this) @@ -151,5 +155,4 @@ abstract class PacketHandler { def readPacketType() = PacketType(readByte()) } - } diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 72c1b90ab8..0345d4f510 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -1,6 +1,10 @@ package li.cil.oc.common -import java.io.File +import java.nio.file.Paths +import java.util.function.BiConsumer +import java.util.function.Function +import java.util.function.Predicate +import java.util.function.Supplier import com.google.common.base.Strings import li.cil.oc._ @@ -10,34 +14,44 @@ import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.traits.Delegate -import li.cil.oc.common.recipe.Recipes import li.cil.oc.integration.Mods +import li.cil.oc.server import li.cil.oc.server._ import li.cil.oc.server.machine.luac.LuaStateFactory import li.cil.oc.server.machine.luac.NativeLua52Architecture import li.cil.oc.server.machine.luac.NativeLua53Architecture import li.cil.oc.server.machine.luaj.LuaJLuaArchitecture import net.minecraft.block.Block +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.inventory.container.Container +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.network.PacketBuffer +import net.minecraft.tags.ItemTags import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.World import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.RegistryEvent.MissingMappings -import net.minecraftforge.fml.common.FMLLog -import net.minecraftforge.fml.common.event._ -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.network.NetworkRegistry -import net.minecraftforge.fml.common.registry.{EntityRegistry} -import net.minecraftforge.oredict.OreDictionary +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.event.lifecycle._ +import net.minecraftforge.fml.loading.FMLPaths +import net.minecraftforge.fml.network.NetworkEvent +import net.minecraftforge.fml.network.NetworkRegistry +import net.minecraftforge.registries.ForgeRegistries import scala.collection.convert.WrapAsScala._ import scala.reflect.ClassTag +@Deprecated class Proxy { - def preInit(e: FMLPreInitializationEvent) { - checkForBrokenJavaVersion() - - Settings.load(new File(e.getModConfigurationDirectory, "opencomputers" + File.separator + "settings.conf")) + def preInit(e: FMLCommonSetupEvent) { + Settings.load(FMLPaths.CONFIGDIR.get.resolve(Paths.get("opencomputers", "settings.conf")).toFile) MinecraftForge.EVENT_BUS.register(this) @@ -48,22 +62,7 @@ class Proxy { OpenComputers.log.debug("Initializing additional OreDict entries.") - OreDictionary.registerOre("craftingPiston", net.minecraft.init.Blocks.PISTON) - OreDictionary.registerOre("craftingPiston", net.minecraft.init.Blocks.STICKY_PISTON) - OreDictionary.registerOre("torchRedstoneActive", net.minecraft.init.Blocks.REDSTONE_TORCH) - OreDictionary.registerOre("materialEnderPearl", net.minecraft.init.Items.ENDER_PEARL) - - // Make mods that use old wireless card name not have broken recipes - OreDictionary.registerOre("oc:wlanCard", Items.get(Constants.ItemName.WirelessNetworkCardTier2).createItemStack(1)) - - tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, "chipDiamond", net.minecraft.init.Items.DIAMOND, "gemDiamond") - - // Avoid issues with Extra Utilities registering colored obsidian as `obsidian` - // oredict entry, but not normal obsidian, breaking some recipes. - OreDictionary.registerOre("obsidian", net.minecraft.init.Blocks.OBSIDIAN) - - // To still allow using normal endstone for crafting drones. - OreDictionary.registerOre("oc:stoneEndstone", net.minecraft.init.Blocks.END_STONE) + tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, "chipDiamond", net.minecraft.item.Items.DIAMOND, "gemDiamond") OpenComputers.log.info("Initializing OpenComputers API.") @@ -101,28 +100,42 @@ class Proxy { else api.Machine.architectures.head } - def init(e: FMLInitializationEvent) { - OpenComputers.channel = NetworkRegistry.INSTANCE.newEventDrivenChannel("OpenComputers") - OpenComputers.channel.register(server.PacketHandler) + def init(e: FMLCommonSetupEvent) { + OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), new Supplier[String] { + override def get = "" + }, new Predicate[String] { + override def test(ver: String) = "".equals(ver) + }, new Predicate[String] { + override def test(ver: String) = "".equals(ver) + }) + OpenComputers.channel.registerMessage(0, classOf[Array[Byte]], new BiConsumer[Array[Byte], PacketBuffer] { + override def accept(msg: Array[Byte], buff: PacketBuffer) = buff.writeByteArray(msg) + }, new Function[PacketBuffer, Array[Byte]] { + override def apply(buff: PacketBuffer) = buff.readByteArray() + }, new BiConsumer[Array[Byte], Supplier[NetworkEvent.Context]] { + override def accept(msg: Array[Byte], ctx: Supplier[NetworkEvent.Context]) = { + val context = ctx.get + context.enqueueWork(new Runnable { + override def run = PacketHandler.handlePacket(context.getDirection, msg, context.getSender) + }) + context.setPacketHandled(true) + } + }) + PacketHandler.serverHandler = server.PacketHandler Loot.init() Achievement.init() - EntityRegistry.registerModEntity(new ResourceLocation(Settings.resourceDomain, "drone"), classOf[Drone], "Drone", 0, OpenComputers, 80, 1, true) - OpenComputers.log.debug("Initializing mod integration.") Mods.init() - OpenComputers.log.debug("Initializing recipes.") - Recipes.init() - OpenComputers.log.info("Initializing capabilities.") Capabilities.init() api.API.isPowerEnabled = !Settings.get.ignorePower } - def postInit(e: FMLPostInitializationEvent) { + def postInit(e: FMLLoadCompleteEvent) { // Don't allow driver registration after this point, to avoid issues. driver.Registry.locked = true } @@ -130,13 +143,11 @@ class Proxy { def tryRegisterNugget[TItem <: Delegate : ClassTag](nuggetItemName: String, nuggetOredictName: String, ingotItem: Item, ingotOredictName: String): Unit = { val nugget = Items.get(nuggetItemName).createItemStack(1) - registerExclusive(nuggetOredictName, nugget) - Delegator.subItem(nugget) match { case Some(subItem: TItem) => - if (OreDictionary.getOres(nuggetOredictName).exists(nugget.isItemEqual)) { - Recipes.addSubItem(subItem, nuggetItemName) - Recipes.addItem(ingotItem, ingotOredictName) + if (ItemTags.getAllTags.getTagOrEmpty(new ResourceLocation(nuggetOredictName)).contains(nugget.getItem)) { + Items.registerItem(subItem, nuggetItemName) + Items.registerItem(ingotItem, ingotOredictName) } else { subItem.showInItemList = false @@ -145,20 +156,30 @@ class Proxy { } } - def registerModel(instance: Delegate, id: String): Unit = {} - - def registerModel(instance: Item, id: String): Unit = {} + def getGuiHandler(): common.GuiHandler = server.GuiHandler - def registerModel(instance: Block, id: String): Unit = {} + @Deprecated + def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = { + player match { + case _: FakePlayer => {} // Ignore fake players. + case _: ServerPlayerEntity => { + player.openMenu(new INamedContainerProvider { + override def createMenu(id: Int, plrInv: PlayerInventory, plr: PlayerEntity): Container = + getGuiHandler.getServerGuiElement(guiId, id, plr, plr.level, x, y, z).asInstanceOf[Container] - private def registerExclusive(name: String, items: ItemStack*) { - if (OreDictionary.getOres(name).isEmpty) { - for (item <- items) { - OreDictionary.registerOre(name, item) + override def getDisplayName(): ITextComponent = StringTextComponent.EMPTY + }) } + case _ => OpenComputers.log.error(s"Unsupported entity for openGui: ${player.getClass.getName}") } } + def registerModel(instance: Delegate, id: String): Unit = {} + + def registerModel(instance: Item, id: String): Unit = {} + + def registerModel(instance: Block, id: String): Unit = {} + // Yes, this could be boiled down even further, but I like to keep it // explicit like this, because it makes it a) clearer, b) easier to // extend, in case that should ever be needed. @@ -177,11 +198,11 @@ class Proxy { @SubscribeEvent def missingBlockMappings(e: MissingMappings[Block]) { - for (missing <- e.getMappings) { - blockRenames.get(missing.key.getResourcePath) match { + for (missing <- e.getMappings(OpenComputers.ID)) { + blockRenames.get(missing.key.getPath) match { case Some(name) => if (Strings.isNullOrEmpty(name)) missing.ignore() - else missing.remap(Block.REGISTRY.getObject(new ResourceLocation(OpenComputers.ID, name))) + else missing.remap(ForgeRegistries.BLOCKS.getValue(new ResourceLocation(OpenComputers.ID, name))) case _ => missing.warn() } } @@ -189,27 +210,13 @@ class Proxy { @SubscribeEvent def missingItemMappings(e: MissingMappings[Item]) { - for (missing <- e.getMappings) { - itemRenames.get(missing.key.getResourcePath) match { + for (missing <- e.getMappings(OpenComputers.ID)) { + itemRenames.get(missing.key.getPath) match { case Some(name) => if (Strings.isNullOrEmpty(name)) missing.ignore() - else missing.remap(Item.REGISTRY.getObject(new ResourceLocation(OpenComputers.ID, name))) + else missing.remap(ForgeRegistries.ITEMS.getValue(new ResourceLocation(OpenComputers.ID, name))) case _ => missing.warn() } } } - - // OK, seriously now, I've gotten one too many bug reports because of this Java version being broken. - - private final val BrokenJavaVersions = Set("1.6.0_65, Apple Inc.") - - def isBrokenJavaVersion = { - val javaVersion = System.getProperty("java.version") + ", " + System.getProperty("java.vendor") - BrokenJavaVersions.contains(javaVersion) - } - - def checkForBrokenJavaVersion() = if (isBrokenJavaVersion) { - FMLLog.bigWarning("You're using a broken Java version! Please update now, or remove OpenComputers. DO NOT REPORT THIS! UPDATE YOUR JAVA!") - throw new Exception("You're using a broken Java version! Please update now, or remove OpenComputers. DO NOT REPORT THIS! UPDATE YOUR JAVA!") - } } diff --git a/src/main/scala/li/cil/oc/common/SaveHandler.scala b/src/main/scala/li/cil/oc/common/SaveHandler.scala index 5dfd4bc9ce..332277d699 100644 --- a/src/main/scala/li/cil/oc/common/SaveHandler.scala +++ b/src/main/scala/li/cil/oc/common/SaveHandler.scala @@ -18,13 +18,15 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.SafeThreadPool import li.cil.oc.util.ThreadPoolFactory import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ResourceLocation import net.minecraft.util.math.ChunkPos import net.minecraft.world.World -import net.minecraftforge.common.DimensionManager +import net.minecraft.world.storage.FolderName import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.EventPriority +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.server.ServerLifecycleHooks import org.apache.commons.lang3.JavaVersion import org.apache.commons.lang3.SystemUtils @@ -48,10 +50,10 @@ object SaveHandler { // which takes a lot of time and is completely unnecessary in those cases. var savingForClients = false - class SaveDataEntry(val data: Array[Byte], val pos: ChunkPos, val name: String, val dimension: Int) extends Runnable { + class SaveDataEntry(val data: Array[Byte], val pos: ChunkPos, val name: String, val dimension: ResourceLocation) extends Runnable { override def run(): Unit = { val path = statePath - val dimPath = new io.File(path, dimension.toString) + val dimPath = new io.File(path, dimension.toString.replace(':', '/').replace('.', '/')) val chunkPath = new io.File(dimPath, s"${this.pos.x}.${this.pos.z}") chunkDirs.add(chunkPath) if (!chunkPath.exists()) { @@ -75,46 +77,46 @@ object SaveHandler { val chunkDirs = new ConcurrentLinkedDeque[io.File]() val saving = mutable.HashMap.empty[String, Future[_]] - def savePath = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath) + def savePath = ServerLifecycleHooks.getCurrentServer.getWorldPath(new FolderName(Settings.savePath)).toFile def statePath = new io.File(savePath, "state") - def scheduleSave(host: MachineHost, nbt: NBTTagCompound, name: String, data: Array[Byte]) { + def scheduleSave(host: MachineHost, nbt: CompoundNBT, name: String, data: Array[Byte]) { scheduleSave(BlockPosition(host), nbt, name, data) } - def scheduleSave(host: MachineHost, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) { + def scheduleSave(host: MachineHost, nbt: CompoundNBT, name: String, save: CompoundNBT => Unit) { scheduleSave(host, nbt, name, writeNBT(save)) } - def scheduleSave(host: EnvironmentHost, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) { + def scheduleSave(host: EnvironmentHost, nbt: CompoundNBT, name: String, save: CompoundNBT => Unit) { scheduleSave(BlockPosition(host), nbt, name, writeNBT(save)) } - def scheduleSave(world: World, x: Double, z: Double, nbt: NBTTagCompound, name: String, data: Array[Byte]) { + def scheduleSave(world: World, x: Double, z: Double, nbt: CompoundNBT, name: String, data: Array[Byte]) { scheduleSave(BlockPosition(x, 0, z, world), nbt, name, data) } - def scheduleSave(world: World, x: Double, z: Double, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) { + def scheduleSave(world: World, x: Double, z: Double, nbt: CompoundNBT, name: String, save: CompoundNBT => Unit) { scheduleSave(world, x, z, nbt, name, writeNBT(save)) } - def scheduleSave(position: BlockPosition, nbt: NBTTagCompound, name: String, data: Array[Byte]) { + def scheduleSave(position: BlockPosition, nbt: CompoundNBT, name: String, data: Array[Byte]) { val world = position.world.get - val dimension = world.provider.getDimension + val dimension = world.dimension.location val chunk = new ChunkPos(position.x >> 4, position.z >> 4) // We have to save the dimension and chunk coordinates, because they are // not available on load / may have changed if the computer was moved. - nbt.setInteger("dimension", dimension) - nbt.setInteger("chunkX", chunk.x) - nbt.setInteger("chunkZ", chunk.z) + nbt.putString("dimension", dimension.toString) + nbt.putInt("chunkX", chunk.x) + nbt.putInt("chunkZ", chunk.z) scheduleSave(dimension, chunk, name, data) } - private def writeNBT(save: NBTTagCompound => Unit) = { - val tmpNbt = new NBTTagCompound() + private def writeNBT(save: CompoundNBT => Unit) = { + val tmpNbt = new CompoundNBT() save(tmpNbt) val baos = new ByteArrayOutputStream() val dos = new DataOutputStream(baos) @@ -122,7 +124,7 @@ object SaveHandler { baos.toByteArray } - def loadNBT(nbt: NBTTagCompound, name: String): NBTTagCompound = { + def loadNBT(nbt: CompoundNBT, name: String): CompoundNBT = { val data = load(nbt, name) if (data.length > 0) try { val bais = new ByteArrayInputStream(data) @@ -132,17 +134,17 @@ object SaveHandler { catch { case t: Throwable => OpenComputers.log.warn("There was an error trying to restore a block's state from external data. This indicates that data was somehow corrupted.", t) - new NBTTagCompound() + new CompoundNBT() } - else new NBTTagCompound() + else new CompoundNBT() } - def load(nbt: NBTTagCompound, name: String): Array[Byte] = { + def load(nbt: CompoundNBT, name: String): Array[Byte] = { // Since we have no world yet, we rely on the dimension we were saved in. // Same goes for the chunk. This also works around issues with computers // being moved (e.g. Redstone in Motion). - val dimension = nbt.getInteger("dimension") - val chunk = new ChunkPos(nbt.getInteger("chunkX"), nbt.getInteger("chunkZ")) + val dimension = nbt.getString("dimension") + val chunk = new ChunkPos(nbt.getInt("chunkX"), nbt.getInt("chunkZ")) // Wait for the latest save task for the requested file to complete. // This prevents the chance of loading an outdated version @@ -155,10 +157,10 @@ object SaveHandler { }) saving.remove(name) - load(dimension, chunk, name) + load(new ResourceLocation(dimension), chunk, name) } - def scheduleSave(dimension: Int, chunk: ChunkPos, name: String, data: Array[Byte]): Unit = { + def scheduleSave(dimension: ResourceLocation, chunk: ChunkPos, name: String, data: Array[Byte]): Unit = { if (chunk == null) throw new IllegalArgumentException("chunk is null") else { // Disregarding whether or not there already was a @@ -169,11 +171,11 @@ object SaveHandler { } } - def load(dimension: Int, chunk: ChunkPos, name: String): Array[Byte] = { + def load(dimension: ResourceLocation, chunk: ChunkPos, name: String): Array[Byte] = { if (chunk == null) throw new IllegalArgumentException("chunk is null") val path = statePath - val dimPath = new io.File(path, dimension.toString) + val dimPath = new io.File(path, dimension.toString.replace(':', '/').replace('.', '/')) val chunkPath = new io.File(dimPath, s"${chunk.x}.${chunk.z}") val file = new io.File(chunkPath, name) if (!file.exists()) return Array.empty[Byte] @@ -219,7 +221,7 @@ object SaveHandler { @SubscribeEvent(priority = EventPriority.HIGHEST) def onWorldLoad(e: WorldEvent.Load) { - if (!e.getWorld.isRemote) { + if (!e.getWorld.isClientSide) { // Touch all externally saved data when loading, to avoid it getting // deleted in the next save (because the now - save time will usually // be larger than the time out after loading a world again). diff --git a/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala b/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala index 23b6dda932..20d0a65c05 100644 --- a/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala +++ b/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala @@ -17,7 +17,7 @@ object ToolDurabilityProviders { if (!durability.isNaN) return Option(durability) } // Fall back to vanilla damage values. - if (stack.isItemStackDamageable) Option(1.0 - stack.getItemDamage.toDouble / stack.getMaxDamage.toDouble) + if (stack.getItem.canBeDepleted) Option(1.0 - stack.getDamageValue.toDouble / stack.getMaxDamage.toDouble) else None } } diff --git a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala deleted file mode 100644 index ff9c1aa9d3..0000000000 --- a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala +++ /dev/null @@ -1,415 +0,0 @@ -package li.cil.oc.common.asm - -import li.cil.oc.common.asm.template.SimpleComponentImpl -import li.cil.oc.integration.Mods -import net.minecraft.launchwrapper.IClassTransformer -import net.minecraft.launchwrapper.LaunchClassLoader -import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper -import org.apache.logging.log4j.LogManager -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Opcodes -import org.objectweb.asm.tree._ - -import scala.annotation.tailrec -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ - -object ObfNames { - final val Class_EntityHanging = Array("net/minecraft/entity/EntityHanging") - final val Class_EntityLiving = Array("net/minecraft/entity/EntityLiving") - final val Class_RenderLiving = Array("net/minecraft/client/renderer/entity/RenderLiving") - final val Class_TileEntity = Array("net/minecraft/tileentity/TileEntity") - final val Field_leashNBTTag = Array("leashNBTTag", "field_110170_bx") - final val Field_leashedToEntity = Array("leashedToEntity", "leashHolder", "field_110168_bw") - final val Method_recreateLeash = Array("recreateLeash", "func_110165_bF") - final val Method_recreateLeashDesc = Array("()V") - final val Method_renderLeash = Array("renderLeash", "func_110827_b") - final val Method_renderLeashDesc = Array("(Lnet/minecraft/entity/EntityLiving;DDDFF)V") - final val Method_validate = Array("validate", "func_145829_t") - final val Method_invalidate = Array("invalidate", "func_145843_s") - final val Method_onChunkUnload = Array("onChunkUnload", "func_76623_d") - final val Method_readFromNBT = Array("readFromNBT", "func_145839_a") - final val Method_writeToNBT = Array("writeToNBT", "func_189515_b") -} - -object ClassTransformer { - var hadErrors = false - var hadSimpleComponentErrors = false -} - -class ClassTransformer extends IClassTransformer { - private val loader = classOf[ClassTransformer].getClassLoader.asInstanceOf[LaunchClassLoader] - private val log = LogManager.getLogger("OpenComputers") - - override def transform(obfName: String, name: String, basicClass: Array[Byte]): Array[Byte] = { - if (basicClass == null || name.startsWith("scala.")) return basicClass - var transformedClass = basicClass - try { - if (!name.startsWith("net.minecraft.") - && !name.startsWith("net.minecraftforge.") - && !name.startsWith("li.cil.oc.common.asm.") - && !name.startsWith("li.cil.oc.integration.")) { - if (name.startsWith("li.cil.oc.")) { - // Strip foreign interfaces from scala generated classes. This is - // primarily intended to clean up mix-ins / synthetic classes - // generated by Scala. - val classNode = newClassNode(transformedClass) - val missingInterfaces = classNode.interfaces.filter(!classExists(_)) - for (interfaceName <- missingInterfaces) { - log.trace(s"Stripping interface $interfaceName from class $name because it is missing.") - } - classNode.interfaces.removeAll(missingInterfaces) - - val missingClasses = classNode.innerClasses.filter(clazz => clazz.outerName != null && !classExists(clazz.outerName)) - for (innerClass <- missingClasses) { - log.trace(s"Stripping inner class ${innerClass.name} from class $name because its type ${innerClass.outerName} is missing.") - } - classNode.innerClasses.removeAll(missingClasses) - - val incompleteMethods = classNode.methods.filter(method => missingFromSignature(method.desc).nonEmpty) - for (method <- incompleteMethods) { - val missing = missingFromSignature(method.desc).mkString(", ") - log.trace(s"Stripping method ${method.name} from class $name because the following types in its signature are missing: $missing") - } - classNode.methods.removeAll(incompleteMethods) - - // Inject available interfaces where requested. - if (classNode.visibleAnnotations != null) { - def injectInterface(annotation: AnnotationNode): Unit = { - val values = annotation.values.grouped(2).map(buffer => buffer.head -> buffer.last).toMap - (values.get("value"), values.get("modid")) match { - case (Some(interfaceName: String), Some(modid: String)) => - Mods.All.find(_.id == modid) match { - case Some(mod) => - if (mod.isModAvailable) { - val interfaceDesc = interfaceName.replaceAllLiterally(".", "/") - val node = classNodeFor(interfaceDesc) - if (node == null) { - log.warn(s"Interface $interfaceName not found, skipping injection.") - } - else { - val missing = node.methods.filterNot(im => classNode.methods.exists(cm => im.name == cm.name && im.desc == cm.desc)).map(method => s"Missing implementation of ${method.name + method.desc}") - if (missing.isEmpty) { - log.info(s"Injecting interface $interfaceName into $name.") - classNode.interfaces.add(interfaceDesc) - } - else { - log.warn(s"Missing implementations for interface $interfaceName, skipping injection.") - missing.foreach(log.warn) - ClassTransformer.hadErrors = true - } - } - } - else { - log.info(s"Skipping interface $interfaceName from missing mod $modid.") - } - case _ => - log.warn(s"Skipping interface $interfaceName from unknown mod $modid.") - ClassTransformer.hadErrors = true - } - case _ => - } - } - classNode.visibleAnnotations.find(_.desc == "Lli/cil/oc/common/asm/Injectable$Interface;") match { - case Some(annotation) => - injectInterface(annotation) - case _ => - } - classNode.visibleAnnotations.find(_.desc == "Lli/cil/oc/common/asm/Injectable$InterfaceList;") match { - case Some(annotation) => - val values = annotation.values.grouped(2).map(buffer => buffer.head -> buffer.last).toMap - values.get("value") match { - case Some(interfaceList: java.lang.Iterable[AnnotationNode]@unchecked) => - interfaceList.foreach(injectInterface) - case _ => - } - case _ => - } - } - - transformedClass = writeClass(classNode) - } - { - val classNode = newClassNode(transformedClass) - if (classNode.interfaces.contains("li/cil/oc/api/network/SimpleComponent") && - (classNode.visibleAnnotations == null || !classNode.visibleAnnotations. - exists(annotation => annotation != null && annotation.desc == "Lli/cil/oc/api/network/SimpleComponent$SkipInjection;"))) { - try { - transformedClass = injectEnvironmentImplementation(classNode) - log.info(s"Successfully injected component logic into class $name.") - } - catch { - case e: Throwable => - log.warn(s"Failed injecting component logic into class $name.", e) - ClassTransformer.hadSimpleComponentErrors = true - } - } - } - } - - // Inject some code into the EntityLiving classes recreateLeash method to allow - // proper loading of leashes tied to entities using the leash upgrade. This is - // necessary because entities only save the entity they are leashed to if that - // entity is an EntityLivingBase - which drones, for example, are not, for good - // reason. We work around this by re-leashing them in the load method of the - // leash upgrade. The leashed entity would then still unleash itself and, more - // problematically drop a leash item. To avoid this, we extend the - // if (this.isLeashed && this.field_110170_bx != null) - // check to read - // if (this.isLeashed && this.field_110170_bx != null && this.leashedToEntity == null) - // which should not interfere with any existing logic, but avoid leashing - // restored manually in the load phase to not be broken again. - if (ObfNames.Class_EntityLiving.contains(name.replace('.', '/'))) { - val classNode = newClassNode(transformedClass) - insertInto(classNode, ObfNames.Method_recreateLeash, ObfNames.Method_recreateLeashDesc, instructions => instructions.toArray.sliding(3, 1).exists { - case Array(varNode: VarInsnNode, fieldNode: FieldInsnNode, jumpNode: JumpInsnNode) - if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 0 && - fieldNode.getOpcode == Opcodes.GETFIELD && ObfNames.Field_leashNBTTag.contains(fieldNode.name) && - jumpNode.getOpcode == Opcodes.IFNULL => - classNode.fields.find(field => ObfNames.Field_leashedToEntity.contains(field.name)) match { - case Some(field) => - val toInject = new InsnList() - toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)) - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, field.name, field.desc)) - toInject.add(new JumpInsnNode(Opcodes.IFNONNULL, jumpNode.label)) - instructions.insert(jumpNode, toInject) - true - case _ => - false - } - case _ => - false - }) match { - case Some(data) => transformedClass = data - case _ => - } - } - - // Little change to the renderer used to render leashes to center it on drones. - // This injects the code - // if (entity instanceof Drone) { - // d5 = 0.0; - // d6 = 0.0; - // d7 = -0.25; - // } - // before the `instanceof EntityHanging` check in func_110827_b. - if (ObfNames.Class_RenderLiving.contains(name.replace('.', '/'))) { - val classNode = newClassNode(transformedClass) - insertInto(classNode, ObfNames.Method_renderLeash, ObfNames.Method_renderLeashDesc, instructions => instructions.toArray.sliding(3, 1).exists { - case Array(varNode: VarInsnNode, typeNode: TypeInsnNode, jumpNode: JumpInsnNode) - if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 10 && - typeNode.getOpcode == Opcodes.INSTANCEOF && ObfNames.Class_EntityHanging.contains(typeNode.desc) && - jumpNode.getOpcode == Opcodes.IFEQ => - val toInject = new InsnList() - toInject.add(new VarInsnNode(Opcodes.ALOAD, 10)) - toInject.add(new TypeInsnNode(Opcodes.INSTANCEOF, "li/cil/oc/common/entity/Drone")) - val skip = new LabelNode() - toInject.add(new JumpInsnNode(Opcodes.IFEQ, skip)) - toInject.add(new LdcInsnNode(Double.box(0.0))) - toInject.add(new VarInsnNode(Opcodes.DSTORE, 17)) - toInject.add(new LdcInsnNode(Double.box(0.0))) - toInject.add(new VarInsnNode(Opcodes.DSTORE, 19)) - toInject.add(new LdcInsnNode(Double.box(-0.25))) - toInject.add(new VarInsnNode(Opcodes.DSTORE, 21)) - toInject.add(skip) - instructions.insertBefore(varNode, toInject) - true - case _ => - false - }) match { - case Some(data) => transformedClass = data - case _ => - } - } - - transformedClass - } - catch { - case t: Throwable => - log.warn("Something went wrong!", t) - ClassTransformer.hadErrors = true - basicClass - } - } - - private def insertInto(classNode: ClassNode, methodNames: Array[String], methodDescs: Array[String], inserter: (InsnList) => Boolean): Option[Array[Byte]] = { - classNode.methods.find(method => methodNames.contains(method.name) && methodDescs.contains(method.desc)) match { - case Some(methodNode) => - if (inserter(methodNode.instructions)) { - log.info(s"Successfully patched ${classNode.name}.${methodNames(0)}.") - Option(writeClass(classNode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES)) - } - else { - log.warn(s"Failed patching ${classNode.name}.${methodNames(0)}, injection point not found.") - ClassTransformer.hadErrors = true - None - } - case _ => - log.warn(s"Failed patching ${classNode.name}.${methodNames(0)}, method not found.") - ClassTransformer.hadErrors = true - None - } - } - - private def classExists(name: String) = { - loader.getClassBytes(name) != null || - loader.getClassBytes(FMLDeobfuscatingRemapper.INSTANCE.unmap(name)) != null || - (try loader.findClass(name.replace('/', '.')) != null catch { - case _: ClassNotFoundException => false - }) - } - - private def missingFromSignature(desc: String) = { - """L([^;]+);""".r.findAllMatchIn(desc).map(_.group(1)).filter(!classExists(_)) - } - - def injectEnvironmentImplementation(classNode: ClassNode): Array[Byte] = { - log.trace(s"Injecting methods from Environment interface into ${classNode.name}.") - if (!isTileEntity(classNode)) { - throw new InjectionFailedException("Found SimpleComponent on something that isn't a tile entity, ignoring.") - } - - val template = classNodeFor("li/cil/oc/common/asm/template/SimpleEnvironment") - if (template == null) { - throw new InjectionFailedException("Could not find SimpleComponent template!") - } - - def inject(methodName: String, signature: String, required: Boolean = false) { - def filter(method: MethodNode) = method.name == methodName && method.desc == signature - if (classNode.methods.exists(filter)) { - if (required) { - throw new InjectionFailedException(s"Could not inject method '$methodName$signature' because it was already present!") - } - } - else template.methods.find(filter) match { - case Some(method) => classNode.methods.add(method) - case _ => throw new AssertionError() - } - } - inject("node", "()Lli/cil/oc/api/network/Node;", required = true) - inject("onConnect", "(Lli/cil/oc/api/network/Node;)V") - inject("onDisconnect", "(Lli/cil/oc/api/network/Node;)V") - inject("onMessage", "(Lli/cil/oc/api/network/Message;)V") - - log.trace("Injecting / wrapping overrides for required tile entity methods.") - def replace(methodName: String, methodNameSrg: String, desc: String) { - val mapper = FMLDeobfuscatingRemapper.INSTANCE - def filter(method: MethodNode) = { - val descDeObf = mapper.mapMethodDesc(method.desc) - val methodNameDeObf = mapper.mapMethodName(mapper.unmap(ObfNames.Class_TileEntity(0)), method.name, method.desc) - val areSamePlain = method.name + descDeObf == methodName + desc - val areSameDeObf = methodNameDeObf + descDeObf == methodNameSrg + desc - areSamePlain || areSameDeObf - } - if (classNode.methods.exists(method => method.name == methodName + SimpleComponentImpl.PostFix && mapper.mapMethodDesc(method.desc) == desc)) { - throw new InjectionFailedException(s"Delegator method name '${methodName + SimpleComponentImpl.PostFix}' is already in use.") - } - classNode.methods.find(filter) match { - case Some(method) => - log.trace(s"Found original implementation of '$methodName', wrapping.") - method.name = methodName + SimpleComponentImpl.PostFix - case _ => - log.trace(s"No original implementation of '$methodName', will inject override.") - @tailrec def ensureNonFinalIn(name: String) { - if (name != null) { - val node = classNodeFor(name) - if (node != null) { - node.methods.find(filter) match { - case Some(method) => - if ((method.access & Opcodes.ACC_FINAL) != 0) { - throw new InjectionFailedException(s"Method '$methodName' is final in superclass ${node.name.replace('/', '.')}.") - } - case _ => - } - ensureNonFinalIn(node.superName) - } - } - } - ensureNonFinalIn(classNode.superName) - template.methods.find(_.name == methodName + SimpleComponentImpl.PostFix) match { - case Some(method) => classNode.methods.add(method) - case _ => throw new AssertionError(s"Couldn't find '${methodName + SimpleComponentImpl.PostFix}' in template implementation.") - } - } - template.methods.find(filter) match { - case Some(method) => classNode.methods.add(method) - case _ => throw new AssertionError(s"Couldn't find '$methodName' in template implementation.") - } - } - replace(ObfNames.Method_validate(0), ObfNames.Method_validate(1), "()V") - replace(ObfNames.Method_invalidate(0), ObfNames.Method_invalidate(1), "()V") - replace(ObfNames.Method_onChunkUnload(0), ObfNames.Method_onChunkUnload(1), "()V") - replace(ObfNames.Method_readFromNBT(0), ObfNames.Method_readFromNBT(1), "(Lnet/minecraft/nbt/NBTTagCompound;)V") - replace(ObfNames.Method_writeToNBT(0), ObfNames.Method_writeToNBT(1), "(Lnet/minecraft/nbt/NBTTagCompound;)Lnet/minecraft/nbt/NBTTagCompound;") - - log.trace("Injecting interface.") - classNode.interfaces.add("li/cil/oc/common/asm/template/SimpleComponentImpl") - - writeClass(classNode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) - } - - @tailrec final def isTileEntity(classNode: ClassNode): Boolean = { - if (classNode == null) false - else { - log.trace(s"Checking if class ${classNode.name} is a TileEntity...") - ObfNames.Class_TileEntity.contains(FMLDeobfuscatingRemapper.INSTANCE.map(classNode.name)) || - (classNode.superName != null && isTileEntity(classNodeFor(classNode.superName))) - } - } - - @tailrec final def isAssignable(parent: ClassNode, child: ClassNode): Boolean = parent != null && child != null && !isFinal(parent) && { - parent.name == "java/lang/Object" || - parent.name == child.name || - parent.name == child.superName || - child.interfaces.contains(parent.name) || - (child.superName != null && isAssignable(parent, classNodeFor(child.superName))) - } - - def isFinal(node: ClassNode): Boolean = (node.access & Opcodes.ACC_FINAL) != 0 - - def isInterface(node: ClassNode): Boolean = node != null && (node.access & Opcodes.ACC_INTERFACE) != 0 - - def classNodeFor(name: String) = { - val namePlain = name.replace('/', '.') - val bytes = loader.getClassBytes(namePlain) - if (bytes != null) newClassNode(bytes) - else { - val nameObfed = FMLDeobfuscatingRemapper.INSTANCE.unmap(name).replace('/', '.') - val bytes = loader.getClassBytes(nameObfed) - if (bytes == null) null - else newClassNode(bytes) - } - } - - def newClassNode(data: Array[Byte]) = { - val classNode = new ClassNode() - new ClassReader(data).accept(classNode, 0) - classNode - } - - def writeClass(classNode: ClassNode, flags: Int = ClassWriter.COMPUTE_MAXS) = { - val writer = new ClassWriter(flags) { - // Implementation without class loads, avoids https://github.com/MinecraftForge/FML/issues/655 - override def getCommonSuperClass(type1: String, type2: String): String = { - val node1 = classNodeFor(type1) - val node2 = classNodeFor(type2) - if (isAssignable(node1, node2)) node1.name - else if (isAssignable(node2, node1)) node2.name - else if (isInterface(node1) || isInterface(node2)) "java/lang/Object" - else { - var parent = Option(node1).map(_.superName).map(classNodeFor).orNull - while (parent != null && parent.superName != null && !isAssignable(parent, node2)) { - parent = classNodeFor(parent.superName) - } - if (parent == null) "java/lang/Object" else parent.name - } - } - } - classNode.accept(writer) - writer.toByteArray - } - - class InjectionFailedException(message: String) extends Exception(message) - -} diff --git a/src/main/scala/li/cil/oc/common/asm/Injectable.java b/src/main/scala/li/cil/oc/common/asm/Injectable.java deleted file mode 100644 index f08dba3208..0000000000 --- a/src/main/scala/li/cil/oc/common/asm/Injectable.java +++ /dev/null @@ -1,50 +0,0 @@ -package li.cil.oc.common.asm; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This interface is kind of the opposite to FML's Optional annotations. - *

    - * Instead of stripping interfaces if they are not present, it will inject them - * when they are present. This helps with some strange cases where - * stripping does not work as it should. - */ -public final class Injectable { - /** - * Not constructable. - */ - private Injectable() { - } - - /** - * Mark a list of interfaces as injectable. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface InterfaceList { - public Interface[] value(); - } - - /** - * Used to inject optional interfaces. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface Interface { - /** - * The fully qualified name of the interface to inject. - */ - public String value(); - - /** - * The modid that is required to be present for the injecting to occur. - *

    - * Note that injection will not occur if the interface is not fully - * implemented. - */ - public String modid(); - } -} diff --git a/src/main/scala/li/cil/oc/common/block/Adapter.scala b/src/main/scala/li/cil/oc/common/block/Adapter.scala index 8f4e66152b..386a535949 100644 --- a/src/main/scala/li/cil/oc/common/block/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/block/Adapter.scala @@ -4,50 +4,52 @@ import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader +import net.minecraft.world.IWorldReader import net.minecraft.world.World class Adapter extends SimpleBlock with traits.GUI { override def guiType = GuiType.Adapter - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Adapter() + override def newBlockEntity(world: IBlockReader) = new tileentity.Adapter() // ----------------------------------------------------------------------- // - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos): Unit = - world.getTileEntity(pos) match { + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos, b: Boolean): Unit = + world.getBlockEntity(pos) match { case adapter: tileentity.Adapter => adapter.neighborChanged() case _ => // Ignore. } - override def onNeighborChange(world: IBlockAccess, pos: BlockPos, neighbor: BlockPos) = - world.getTileEntity(pos) match { + override def onNeighborChange(state: BlockState, world: IWorldReader, pos: BlockPos, neighbor: BlockPos) = + world.getBlockEntity(pos) match { case adapter: tileentity.Adapter => // TODO can we just pass the blockpos? val side = - if (neighbor == pos.down()) EnumFacing.DOWN - else if (neighbor == pos.up()) EnumFacing.UP - else if (neighbor == pos.north()) EnumFacing.NORTH - else if (neighbor == pos.south()) EnumFacing.SOUTH - else if (neighbor == pos.west()) EnumFacing.WEST - else if (neighbor == pos.east()) EnumFacing.EAST + if (neighbor == (pos.below():BlockPos)) Direction.DOWN + else if (neighbor == (pos.above():BlockPos)) Direction.UP + else if (neighbor == pos.north()) Direction.NORTH + else if (neighbor == pos.south()) Direction.SOUTH + else if (neighbor == pos.west()) Direction.WEST + else if (neighbor == pos.east()) Direction.EAST else throw new IllegalArgumentException("not a neighbor") // TODO wat adapter.neighborChanged(side) case _ => // Ignore. } - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { if (Wrench.holdsApplicableWrench(player, pos)) { - val sideToToggle = if (player.isSneaking) side.getOpposite else side - world.getTileEntity(pos) match { + val sideToToggle = if (player.isCrouching) side.getOpposite else side + world.getBlockEntity(pos) match { case adapter: tileentity.Adapter => - if (!world.isRemote) { + if (!world.isClientSide) { val oldValue = adapter.openSides(sideToToggle.ordinal()) adapter.setSideOpen(sideToToggle, !oldValue) } diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index f79f960e20..85da8f96c7 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -3,26 +3,16 @@ package li.cil.oc.common.block import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.util.EnumFacing +import net.minecraft.block.BlockState +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN || side == EnumFacing.UP - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN || side == EnumFacing.UP - - // ----------------------------------------------------------------------- // - override def energyThroughput = Settings.get.assemblerRate override def guiType = GuiType.Assembler - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Assembler() + override def newBlockEntity(world: IBlockReader) = new tileentity.Assembler() } diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index cd6f2314d3..27325376de 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -2,22 +2,22 @@ package li.cil.oc.common.block import java.util -import li.cil.oc.common.block.property.UnlistedInteger import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.tileentity import li.cil.oc.util.Color +import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.{Entity, EntityLivingBase} -import net.minecraft.item.{EnumDyeColor, ItemStack} +import net.minecraft.block.Blocks +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.{Entity, LivingEntity} +import net.minecraft.item.{DyeColor, ItemStack} import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceResult, Vec3d} -import net.minecraft.world.IBlockAccess +import net.minecraft.util.Direction +import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceResult} +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.world.IBlockReader import net.minecraft.world.World -import net.minecraftforge.common.property.ExtendedBlockState -import net.minecraftforge.common.property.IExtendedBlockState import scala.collection.JavaConversions._ import scala.reflect.ClassTag @@ -31,88 +31,37 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends // ----------------------------------------------------------------------- // - override def createBlockState() = new ExtendedBlockState(this, Array.empty, Array(Cable.NeighborsProp,Cable.ColorProp, Cable.IsSideCableProp)) - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = - (state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, cable: tileentity.Cable) => - var isCableMask = 0 - for (side <- EnumFacing.values) { - if (world.getTileEntity(pos.offset(side)).isInstanceOf[tileentity.Cable]){ - isCableMask = Cable.mask(side, isCableMask) - } - } - extendedState.withProperty(Cable.NeighborsProp, Int.box(Cable.neighbors(world,pos))).withProperty(Cable.ColorProp, Int.box(cable.getColor)).withProperty(Cable.IsSideCableProp, Int.box(isCableMask)) - case _ => state - } - - // ----------------------------------------------------------------------- // - - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = true - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - // ----------------------------------------------------------------------- // - - override def getPickBlock(state: IBlockState, target: RayTraceResult, world: World, pos: BlockPos, player: EntityPlayer) = - world.getTileEntity(pos) match { + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = + world.getBlockEntity(pos) match { case t: tileentity.Cable => t.createItemStack() case _ => createItemStack() } - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = Cable.bounds(world, pos) - - override def addCollisionBoxToList(state: IBlockState, worldIn: World, pos: BlockPos, entityBox: AxisAlignedBB, collidingBoxes: util.List[AxisAlignedBB], entityIn: Entity, isActualState: Boolean): Unit = { - Cable.parts(worldIn, pos, entityBox, collidingBoxes) - } - - override def collisionRayTrace(state: IBlockState, world: World, pos: BlockPos, start: Vec3d, end: Vec3d): RayTraceResult = { - var distance = Double.PositiveInfinity - var result: RayTraceResult = null - - val boxes = new util.ArrayList[AxisAlignedBB] - Cable.parts(world, pos, Block.FULL_BLOCK_AABB.offset(pos), boxes) - for (part: AxisAlignedBB <- boxes) { - val hit = part.calculateIntercept(start, end) - if (hit != null) { - val hitDistance = hit.hitVec.squareDistanceTo(start) - if (hitDistance < distance) { - distance = hitDistance; - result = hit; - } - } - } - - if (result == null) null - else new RayTraceResult(result.hitVec, result.sideHit, pos) - } + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = Cable.bounds(world, pos) // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Cable() + override def newBlockEntity(world: IBlockReader) = new tileentity.Cable() // ----------------------------------------------------------------------- // - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, neighborBlock: Block, sourcePos: BlockPos) { - world.notifyBlockUpdate(pos, state, state, 3) - super.neighborChanged(state, world, pos, neighborBlock, sourcePos) + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, sourcePos: BlockPos, b: Boolean) { + world.sendBlockUpdated(pos, state, state, 3) + super.neighborChanged(state, world, pos, neighborBlock, sourcePos, b) } - override protected def doCustomInit(tileEntity: tileentity.Cable, player: EntityLivingBase, stack: ItemStack): Unit = { + override protected def doCustomInit(tileEntity: tileentity.Cable, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isRemote) { + if (!tileEntity.world.isClientSide) { tileEntity.fromItemStack(stack) } } - override protected def doCustomDrops(tileEntity: tileentity.Cable, player: EntityPlayer, willHarvest: Boolean): Unit = { + override protected def doCustomDrops(tileEntity: tileentity.Cable, player: PlayerEntity, willHarvest: Boolean): Unit = { super.doCustomDrops(tileEntity, player, willHarvest) - if (!player.capabilities.isCreativeMode) { - Block.spawnAsEntity(tileEntity.world, tileEntity.getPos, tileEntity.createItemStack()) + if (!player.isCreative) { + Block.popResource(tileEntity.world, tileEntity.getBlockPos, tileEntity.createItemStack()) } } } @@ -134,31 +83,30 @@ object Cable { final val CachedBounds = { // 6 directions = 6 bits = 11111111b >> 2 = 0xFF >> 2 (0 to 0xFF >> 2).map(mask => { - EnumFacing.VALUES.foldLeft(DefaultBounds)((bound, side) => { - if (((1 << side.getIndex) & mask) != 0) bound.union(CachedParts(side.ordinal())) + Direction.values.foldLeft(DefaultBounds)((bound, side) => { + if (((1 << side.get3DDataValue) & mask) != 0) bound.minmax(CachedParts(side.ordinal())) else bound }) }).toArray } - final val NeighborsProp = new UnlistedInteger("neighbors") - final val ColorProp = new UnlistedInteger("color") - final val IsSideCableProp = new UnlistedInteger("is_cable") + def mask(side: Direction, value: Int = 0) = value | (1 << side.get3DDataValue) - def mask(side: EnumFacing, value: Int = 0) = value | (1 << side.getIndex) - - def neighbors(world: IBlockAccess, pos: BlockPos) = { + def neighbors(world: IBlockReader, pos: BlockPos) = { var result = 0 - val tileEntity = world.getTileEntity(pos) - for (side <- EnumFacing.values) { - val tpos = pos.offset(side) + val tileEntity = world.getBlockEntity(pos) + for (side <- Direction.values) { + val tpos = pos.relative(side) val hasNode = hasNetworkNode(tileEntity, side) if (hasNode && (world match { - case world: World => world.isBlockLoaded(tpos) - case _ => !world.isAirBlock(tpos) + case world: World => world.isLoaded(tpos) + case _ => { + val state = world.getBlockState(tpos) + state.getBlock.isAir(state, world, tpos) + } })) { - val neighborTileEntity = world.getTileEntity(tpos) - if (neighborTileEntity != null && neighborTileEntity.getWorld != null) { + val neighborTileEntity = world.getBlockEntity(tpos) + if (neighborTileEntity != null && neighborTileEntity.getLevel != null) { val neighborHasNode = hasNetworkNode(neighborTileEntity, side.getOpposite) val canConnectColor = canConnectBasedOnColor(tileEntity, neighborTileEntity) val canConnectIM = canConnectFromSideIM(tileEntity, side) && canConnectFromSideIM(neighborTileEntity, side.getOpposite) @@ -171,35 +119,35 @@ object Cable { result } - def bounds(world: IBlockAccess, pos: BlockPos) = Cable.CachedBounds(Cable.neighbors(world, pos)) + def bounds(world: IBlockReader, pos: BlockPos) = Cable.CachedBounds(Cable.neighbors(world, pos)) - def parts(world: IBlockAccess, pos: BlockPos, entityBox : AxisAlignedBB, boxes : util.List[AxisAlignedBB]) = { - val center = Cable.DefaultBounds.offset(pos) + def parts(world: IBlockReader, pos: BlockPos, entityBox : AxisAlignedBB, boxes : util.List[AxisAlignedBB]) = { + val center = Cable.DefaultBounds.move(pos) if (entityBox.intersects(center)) boxes.add(center) val mask = Cable.neighbors(world, pos) - for (side <- EnumFacing.VALUES) { - if(((1 << side.getIndex) & mask) != 0) { - val part = Cable.CachedParts(side.ordinal()).offset(pos) + for (side <- Direction.values) { + if(((1 << side.get3DDataValue) & mask) != 0) { + val part = Cable.CachedParts(side.ordinal()).move(pos) if (entityBox.intersects(part)) boxes.add(part) } } } - private def hasNetworkNode(tileEntity: TileEntity, side: EnumFacing): Boolean = { + private def hasNetworkNode(tileEntity: TileEntity, side: Direction): Boolean = { if (tileEntity != null) { if (tileEntity.isInstanceOf[tileentity.RobotProxy]) return false - if (tileEntity.hasCapability(Capabilities.SidedEnvironmentCapability, side)) { - val host = tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side) + if (tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side).isPresent) { + val host = tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side).orElse(null) if (host != null) { - return if (tileEntity.getWorld.isRemote) host.canConnect(side) else host.sidedNode(side) != null + return if (tileEntity.getLevel.isClientSide) host.canConnect(side) else host.sidedNode(side) != null } } - if (tileEntity.hasCapability(Capabilities.EnvironmentCapability, side)) { + if (tileEntity.getCapability(Capabilities.EnvironmentCapability, side).isPresent) { val host = tileEntity.getCapability(Capabilities.EnvironmentCapability, side) - if (host != null) return true + if (host.isPresent) return true } } @@ -208,21 +156,21 @@ object Cable { private def getConnectionColor(tileEntity: TileEntity): Int = { if (tileEntity != null) { - if (tileEntity.hasCapability(Capabilities.ColoredCapability, null)) { - val colored = tileEntity.getCapability(Capabilities.ColoredCapability, null) + if (tileEntity.getCapability(Capabilities.ColoredCapability, null).isPresent) { + val colored = tileEntity.getCapability(Capabilities.ColoredCapability, null).orElse(null) if (colored != null && colored.controlsConnectivity) return colored.getColor } } - Color.rgbValues(EnumDyeColor.SILVER) + Color.rgbValues(DyeColor.LIGHT_GRAY) } private def canConnectBasedOnColor(te1: TileEntity, te2: TileEntity) = { val (c1, c2) = (getConnectionColor(te1), getConnectionColor(te2)) - c1 == c2 || c1 == Color.rgbValues(EnumDyeColor.SILVER) || c2 == Color.rgbValues(EnumDyeColor.SILVER) + c1 == c2 || c1 == Color.rgbValues(DyeColor.LIGHT_GRAY) || c2 == Color.rgbValues(DyeColor.LIGHT_GRAY) } - private def canConnectFromSideIM(tileEntity: TileEntity, side: EnumFacing) = + private def canConnectFromSideIM(tileEntity: TileEntity, side: Direction) = tileEntity match { case im: tileentity.traits.ImmibisMicroblock => im.ImmibisMicroblocks_isSideOpen(side.ordinal) case _ => true diff --git a/src/main/scala/li/cil/oc/common/block/Capacitor.scala b/src/main/scala/li/cil/oc/common/block/Capacitor.scala index 54fb043f3a..2e79042be7 100644 --- a/src/main/scala/li/cil/oc/common/block/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/Capacitor.scala @@ -4,36 +4,38 @@ import java.util.Random import li.cil.oc.common.tileentity import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos +import net.minecraft.world.IBlockReader import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld class Capacitor extends SimpleBlock { - setTickRandomly(true) + @Deprecated + override def isRandomlyTicking(state: BlockState) = true // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Capacitor() + override def newBlockEntity(world: IBlockReader) = new tileentity.Capacitor() // ----------------------------------------------------------------------- // - override def hasComparatorInputOverride(state: IBlockState): Boolean = true + override def hasAnalogOutputSignal(state: BlockState): Boolean = true - override def getComparatorInputOverride(state: IBlockState, world: World, pos: BlockPos): Int = - world.getTileEntity(pos) match { - case capacitor: tileentity.Capacitor if !world.isRemote => + override def getAnalogOutputSignal(state: BlockState, world: World, pos: BlockPos): Int = + world.getBlockEntity(pos) match { + case capacitor: tileentity.Capacitor if !world.isClientSide => math.round(15 * capacitor.node.localBuffer / capacitor.node.localBufferSize).toInt case _ => 0 } - override def updateTick(world: World, pos: BlockPos, state: IBlockState, rand: Random): Unit = { - world.notifyNeighborsOfStateChange(pos, this, false) + override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: Random): Unit = { + world.updateNeighborsAt(pos, this) } - override def tickRate(world: World) = 1 - - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos): Unit = - world.getTileEntity(pos) match { + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos, b: Boolean): Unit = + world.getBlockEntity(pos) match { case capacitor: tileentity.Capacitor => capacitor.recomputeCapacity() case _ => } diff --git a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala index 03108dce6d..30094ff704 100644 --- a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala @@ -1,8 +1,8 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity -import net.minecraft.world.World +import net.minecraft.world.IBlockReader class CarpetedCapacitor extends Capacitor { - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.CarpetedCapacitor() + override def newBlockEntity(world: IBlockReader) = new tileentity.CarpetedCapacitor() } diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 08cf662b55..50819c07eb 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -8,29 +8,35 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState +import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.FluidState import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Facing, property.PropertyRunning.Running) - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta >> 1)) +import scala.collection.convert.WrapAsScala._ - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex << 1 | (if (state.getValue(property.PropertyRunning.Running)) 1 else 0) +class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing, property.PropertyRunning.Running) // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase, slots)) + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, slots)) { + tooltip.add(new StringTextComponent(curr)) + } } private def slots = tier match { @@ -46,14 +52,14 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override def guiType = GuiType.Case - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Case(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Case(tier) // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - if (player.isSneaking) { - if (!world.isRemote) world.getTileEntity(pos) match { - case computer: tileentity.Case if !computer.machine.isRunning && computer.isUsableByPlayer(player) => computer.machine.start() + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { + if (player.isCrouching) { + if (!world.isClientSide) world.getBlockEntity(pos) match { + case computer: tileentity.Case if !computer.machine.isRunning && computer.stillValid(player) => computer.machine.start() case _ => } true @@ -61,11 +67,11 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t else super.localOnBlockActivated(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ) } - override def removedByPlayer(state: IBlockState, world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean): Boolean = - world.getTileEntity(pos) match { + override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = + world.getBlockEntity(pos) match { case c: tileentity.Case => - if (c.isCreative && (!player.capabilities.isCreativeMode || !c.canInteract(player.getName))) false - else c.canInteract(player.getName) && super.removedByPlayer(state, world, pos, player, willHarvest) - case _ => super.removedByPlayer(state, world, pos, player, willHarvest) + if (c.isCreative && (!player.isCreative || !c.canInteract(player.getName.getString))) false + else c.canInteract(player.getName.getString) && super.removedByPlayer(state, world, pos, player, willHarvest, fluid) + case _ => super.removedByPlayer(state, world, pos, player, willHarvest, fluid) } } diff --git a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala index dbbe315f88..a564d38a72 100644 --- a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala @@ -1,27 +1,32 @@ package li.cil.oc.common.block +import java.util.Collections +import java.util.List + +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.material.Material -import net.minecraft.block.properties.PropertyEnum -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState -import net.minecraft.item.EnumDyeColor +import net.minecraft.state.EnumProperty +import net.minecraft.item.DyeColor +import net.minecraft.item.ItemStack +import net.minecraft.loot.LootContext +import net.minecraft.state.StateContainer object ChameliumBlock { - final val Color = PropertyEnum.create("color", classOf[EnumDyeColor]) + final val Color = EnumProperty.create("color", classOf[DyeColor]) } -class ChameliumBlock extends SimpleBlock(Material.ROCK) { - setDefaultState(blockState.getBaseState.withProperty(ChameliumBlock.Color, EnumDyeColor.BLACK)) - - override def damageDropped(state: IBlockState): Int = getMetaFromState(state) - - override def getStateFromMeta(meta: Int): IBlockState = - getDefaultState.withProperty(ChameliumBlock.Color, EnumDyeColor.byDyeDamage(meta)) - - override def getMetaFromState(state: IBlockState): Int = - state.getValue(ChameliumBlock.Color).getDyeDamage - - override def createBlockState() = new BlockStateContainer(this, ChameliumBlock.Color) - - override def hasTileEntity(state: IBlockState): Boolean = false +class ChameliumBlock(props: Properties = Properties.of(Material.STONE).strength(2, 5)) extends SimpleBlock(props) { + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]): Unit = { + builder.add(ChameliumBlock.Color) + } + registerDefaultState(stateDefinition.any.setValue(ChameliumBlock.Color, DyeColor.BLACK)) + + @Deprecated + override def getDrops(state: BlockState, ctx: LootContext.Builder): List[ItemStack] = { + val stack = new ItemStack(this, 1) + stack.setDamageValue(state.getValue(ChameliumBlock.Color).getId) + Collections.singletonList(stack) + } } diff --git a/src/main/scala/li/cil/oc/common/block/Charger.scala b/src/main/scala/li/cil/oc/common/block/Charger.scala index 70e953d019..6ed3456658 100644 --- a/src/main/scala/li/cil/oc/common/block/Charger.scala +++ b/src/main/scala/li/cil/oc/common/block/Charger.scala @@ -7,22 +7,19 @@ import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import li.cil.oc.server.PacketSender import net.minecraft.block.Block -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Facing) - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta)) - - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing) // ----------------------------------------------------------------------- // @@ -30,18 +27,18 @@ class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateA override def guiType = GuiType.Charger - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Charger() + override def newBlockEntity(world: IBlockReader) = new tileentity.Charger() // ----------------------------------------------------------------------- // - override def canConnectRedstone(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = true + override def canConnectRedstone(state: BlockState, world: IBlockReader, pos: BlockPos, side: Direction): Boolean = true // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = - if (Wrench.holdsApplicableWrench(player, pos)) world.getTileEntity(pos) match { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = + if (Wrench.holdsApplicableWrench(player, pos)) world.getBlockEntity(pos) match { case charger: tileentity.Charger => - if (!world.isRemote) { + if (!world.isClientSide) { charger.invertSignal = !charger.invertSignal charger.chargeSpeed = 1.0 - charger.chargeSpeed PacketSender.sendChargerState(charger) @@ -52,11 +49,12 @@ class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateA } else super.localOnBlockActivated(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ) - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos): Unit = { - world.getTileEntity(pos) match { + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos, b: Boolean): Unit = { + world.getBlockEntity(pos) match { case charger: tileentity.Charger => charger.onNeighborChanged() case _ => } - super.neighborChanged(state, world, pos, block, fromPos) + super.neighborChanged(state, world, pos, block, fromPos, b) } } diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index d938805201..c1a0ec06e7 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -6,15 +6,22 @@ import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import li.cil.oc.util.Tooltip -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World +import scala.collection.convert.WrapAsScala._ + class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase, (Settings.get.disassemblerBreakChance * 100).toInt.toString)) + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, (Settings.get.disassemblerBreakChance * 100).toInt.toString)) { + tooltip.add(new StringTextComponent(curr)) + } } // ----------------------------------------------------------------------- // @@ -23,5 +30,5 @@ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.Sta override def guiType = GuiType.Disassembler - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Disassembler() + override def newBlockEntity(world: IBlockReader) = new tileentity.Disassembler() } diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 3c4e1ef5b4..4178635b1f 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -7,29 +7,32 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.util.Tooltip -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState +import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World -class DiskDrive extends SimpleBlock with traits.GUI { - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Facing) - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta)) +import scala.collection.convert.WrapAsScala._ - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex +class DiskDrive extends SimpleBlock with traits.GUI { + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing) // ----------------------------------------------------------------------- // - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, flag) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, flag) if (Mods.ComputerCraft.isModAvailable) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName + ".CC")) + for (curr <- Tooltip.get(getClass.getSimpleName + ".CC")) tooltip.add(new StringTextComponent(curr)) } } @@ -37,38 +40,34 @@ class DiskDrive extends SimpleBlock with traits.GUI { override def guiType = GuiType.DiskDrive - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.DiskDrive() + override def newBlockEntity(world: IBlockReader) = new tileentity.DiskDrive() // ----------------------------------------------------------------------- // - override def hasComparatorInputOverride(state: IBlockState): Boolean = true + override def hasAnalogOutputSignal(state: BlockState): Boolean = true - override def getComparatorInputOverride(state: IBlockState, world: World, pos: BlockPos): Int = - world.getTileEntity(pos) match { - case drive: tileentity.DiskDrive if !drive.getStackInSlot(0).isEmpty => 15 + override def getAnalogOutputSignal(state: BlockState, world: World, pos: BlockPos): Int = + world.getBlockEntity(pos) match { + case drive: tileentity.DiskDrive if !drive.getItem(0).isEmpty => 15 case _ => 0 } // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { // Behavior: sneaking -> Insert[+Eject], not sneaking -> GUI. - if (player.isSneaking) world.getTileEntity(pos) match { + if (player.isCrouching) world.getBlockEntity(pos) match { case drive: tileentity.DiskDrive => - val isDiskInDrive = drive.getStackInSlot(0) != null - val isHoldingDisk = drive.isItemValidForSlot(0, heldItem) + val isDiskInDrive = drive.getItem(0) != null + val isHoldingDisk = drive.canPlaceItem(0, heldItem) if (isDiskInDrive) { - if (!world.isRemote) { + if (!world.isClientSide) { drive.dropSlot(0, 1, Option(drive.facing)) } } if (isHoldingDisk) { // Insert the disk. - drive.setInventorySlotContents(0, heldItem.copy().splitStack(1)) - if (hand == EnumHand.MAIN_HAND) - player.inventory.decrStackSize(player.inventory.currentItem, 1) - else - player.inventory.offHandInventory.get(0).shrink(1) + drive.setItem(0, heldItem.split(1)) } isDiskInDrive || isHoldingDisk case _ => false diff --git a/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala b/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala index 97297061f8..c0fb07407f 100644 --- a/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala +++ b/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala @@ -1,11 +1,8 @@ package li.cil.oc.common.block +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.BlockState import net.minecraft.block.material.Material -import net.minecraft.block.state.IBlockState -class FakeEndstone extends SimpleBlock(Material.ROCK) { - setHardness(3) - setResistance(15) - - override def hasTileEntity(state: IBlockState): Boolean = false +class FakeEndstone extends SimpleBlock(Properties.of(Material.STONE).strength(3, 5)) { } diff --git a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala index 0f87c555d7..97751c48a6 100644 --- a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala @@ -1,8 +1,9 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Geolyzer extends SimpleBlock { - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Geolyzer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Geolyzer() } diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index 05ad5ad18a..6cb1702320 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -5,47 +5,38 @@ import java.util import li.cil.oc.common.tileentity import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess -import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn + +import scala.collection.convert.WrapAsScala._ class Hologram(val tier: Int) extends SimpleBlock { val bounds = new AxisAlignedBB(0, 0, 0, 1, 0.5f, 1) // ----------------------------------------------------------------------- // - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN - - @SideOnly(Side.CLIENT) - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = { - super.shouldSideBeRendered(state, world, pos, side) || side == EnumFacing.UP - } - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN - - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = bounds + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = bounds // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase() + tier)) + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase() + tier)) { + tooltip.add(new StringTextComponent(curr)) + } } // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Hologram(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Hologram(tier) } diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 929196193d..275b36e043 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -11,46 +11,56 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ItemColorizer import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumDyeColor -import net.minecraft.item.EnumRarity -import net.minecraft.item.ItemBlock +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.BlockItem +import net.minecraft.item.BlockItemUseContext +import net.minecraft.item.DyeColor +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.item.Rarity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -class Item(value: Block) extends ItemBlock(value) { - setHasSubtypes(true) +object Item { + def setCreativeTab(block: Block, props: Properties): Properties = block match { + case simple: SimpleBlock => + if (simple.getCreativeTab != null) props.tab(simple.getCreativeTab) else props + case _ => props + } +} - override def addInformation(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - super.addInformation(stack, world, tooltip, flag) - block match { +class Item(value: Block, props: Properties = new Properties()) extends BlockItem(value, Item.setCreativeTab(value, props)) { + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) + getBlock match { case (simple: SimpleBlock) => - simple.addInformation(getMetadata(stack.getItemDamage), stack, world, tooltip, flag) + simple.appendHoverText(stack, world, tooltip, flag) case _ => } } - override def getRarity(stack: ItemStack): EnumRarity = block match { + override def getRarity(stack: ItemStack): Rarity = getBlock match { case simple: SimpleBlock => simple.rarity(stack) - case _ => EnumRarity.COMMON + case _ => Rarity.COMMON } - override def getMetadata(itemDamage: Int): Int = itemDamage - - override def getItemStackDisplayName(stack: ItemStack): String = { + override def getName(stack: ItemStack): ITextComponent = { if (api.Items.get(stack) == api.Items.get(Constants.BlockName.Print)) { val data = new PrintData(stack) - data.label.getOrElse(super.getItemStackDisplayName(stack)) + data.label.map(new StringTextComponent(_)).getOrElse(super.getName(stack)) } - else super.getItemStackDisplayName(stack) + else super.getName(stack) } - override def getUnlocalizedName: String = block match { - case simple: SimpleBlock => simple.getUnlocalizedName + @Deprecated + override def getDescriptionId: String = getBlock match { + case simple: SimpleBlock => simple.getDescriptionId case _ => Settings.namespace + "tile" } @@ -59,14 +69,14 @@ class Item(value: Block) extends ItemBlock(value) { if (ItemColorizer.hasColor(stack)) { ItemColorizer.getColor(stack) } - else Color.rgbValues(EnumDyeColor.SILVER) + else Color.rgbValues(DyeColor.LIGHT_GRAY) } else super.getDamage(stack) } override def setDamage(stack: ItemStack, damage: Int): Unit = { if (api.Items.get(stack) == api.Items.get(Constants.BlockName.Cable)) { - if(damage != Color.rgbValues(EnumDyeColor.SILVER)) { + if(damage != Color.rgbValues(DyeColor.LIGHT_GRAY)) { ItemColorizer.setColor(stack, damage) } else { ItemColorizer.removeColor(stack) @@ -75,24 +85,27 @@ class Item(value: Block) extends ItemBlock(value) { else super.setDamage(stack, damage) } - override def isBookEnchantable(a: ItemStack, b: ItemStack) = false - - override def placeBlockAt(stack: ItemStack, player: EntityPlayer, world: World, pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, newState: IBlockState): Boolean = { + override def placeBlock(ctx: BlockItemUseContext, newState: BlockState): Boolean = { // When placing robots in creative mode, we have to copy the stack // manually before it's placed to ensure different component addresses // in the different robots, to avoid interference of screens e.g. - val needsCopying = player.capabilities.isCreativeMode && api.Items.get(stack) == api.Items.get(Constants.BlockName.Robot) - val stackToUse = if (needsCopying) new RobotData(stack).copyItemStack() else stack - if (super.placeBlockAt(stackToUse, player, world, pos, side, hitX, hitY, hitZ, newState)) { + val needsCopying = ctx.getPlayer.isCreative && api.Items.get(ctx.getItemInHand) == api.Items.get(Constants.BlockName.Robot) + val ctxToUse = if (needsCopying) { + val stackToUse = new RobotData(ctx.getItemInHand).copyItemStack() + val hitResult = new BlockRayTraceResult(ctx.getClickLocation, ctx.getClickedFace, ctx.getClickedPos, ctx.isInside) + new BlockItemUseContext(ctx.getLevel, ctx.getPlayer, ctx.getHand, stackToUse, hitResult) + } + else ctx + if (super.placeBlock(ctxToUse, newState)) { // If it's a rotatable block try to make it face the player. - world.getTileEntity(pos) match { + ctx.getLevel.getBlockEntity(ctxToUse.getClickedPos) match { case keyboard: tileentity.Keyboard => - keyboard.setFromEntityPitchAndYaw(player) - keyboard.setFromFacing(side) + keyboard.setFromEntityPitchAndYaw(ctxToUse.getPlayer) + keyboard.setFromFacing(ctxToUse.getClickedFace) case rotatable: tileentity.traits.Rotatable => - rotatable.setFromEntityPitchAndYaw(player) + rotatable.setFromEntityPitchAndYaw(ctxToUse.getPlayer) if (!rotatable.validFacings.contains(rotatable.pitch)) { - rotatable.pitch = rotatable.validFacings.headOption.getOrElse(EnumFacing.NORTH) + rotatable.pitch = rotatable.validFacings.headOption.getOrElse(Direction.NORTH) } if (!rotatable.isInstanceOf[tileentity.RobotProxy]) { rotatable.invertRotation() diff --git a/src/main/scala/li/cil/oc/common/block/Keyboard.scala b/src/main/scala/li/cil/oc/common/block/Keyboard.scala index 0054c5ba37..d04afeb9d1 100644 --- a/src/main/scala/li/cil/oc/common/block/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/block/Keyboard.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.block import java.util.Random +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Constants import li.cil.oc.api import li.cil.oc.common.block.property.PropertyRotatable @@ -9,123 +10,119 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedEnumFacing._ import li.cil.oc.util.InventoryUtils +import li.cil.oc.util.RotationHelper +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block +import net.minecraft.block.Blocks +import net.minecraft.block.BlockState import net.minecraft.block.material.Material -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.state.StateContainer +import net.minecraft.world.IBlockReader +import net.minecraft.world.IWorldReader import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld -class Keyboard extends SimpleBlock(Material.ROCK) { - setLightOpacity(0) - +class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5).noOcclusion()) extends SimpleBlock(props) { // For Immibis Microblock support. val ImmibisMicroblocks_TransformableBlockMarker = null - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Pitch, PropertyRotatable.Yaw) - - override def getMetaFromState(state: IBlockState): Int = (state.getValue(PropertyRotatable.Pitch).ordinal() << 2) | state.getValue(PropertyRotatable.Yaw).getHorizontalIndex - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Pitch, EnumFacing.getFront(meta >> 2)).withProperty(PropertyRotatable.Yaw, EnumFacing.getHorizontal(meta & 0x3)) + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Pitch, PropertyRotatable.Yaw) // ----------------------------------------------------------------------- // - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = - world.getTileEntity(pos) match { + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = + world.getBlockEntity(pos) match { case keyboard: tileentity.Keyboard => val (pitch, yaw) = (keyboard.pitch, keyboard.yaw) val (forward, up) = pitch match { - case side@(EnumFacing.DOWN | EnumFacing.UP) => (side, yaw) - case _ => (yaw, EnumFacing.UP) + case side@(Direction.DOWN | Direction.UP) => (side, yaw) + case _ => (yaw, Direction.UP) } val side = forward.getRotation(up) val sizes = Array(7f / 16f, 4f / 16f, 7f / 16f) - val x0 = -up.getFrontOffsetX * sizes(1) - side.getFrontOffsetX * sizes(2) - forward.getFrontOffsetX * sizes(0) - val x1 = up.getFrontOffsetX * sizes(1) + side.getFrontOffsetX * sizes(2) - forward.getFrontOffsetX * 0.5f - val y0 = -up.getFrontOffsetY * sizes(1) - side.getFrontOffsetY * sizes(2) - forward.getFrontOffsetY * sizes(0) - val y1 = up.getFrontOffsetY * sizes(1) + side.getFrontOffsetY * sizes(2) - forward.getFrontOffsetY * 0.5f - val z0 = -up.getFrontOffsetZ * sizes(1) - side.getFrontOffsetZ * sizes(2) - forward.getFrontOffsetZ * sizes(0) - val z1 = up.getFrontOffsetZ * sizes(1) + side.getFrontOffsetZ * sizes(2) - forward.getFrontOffsetZ * 0.5f - new AxisAlignedBB(x0, y0, z0, x1, y1, z1).offset(0.5, 0.5, 0.5) + val x0 = -up.getStepX * sizes(1) - side.getStepX * sizes(2) - forward.getStepX * sizes(0) + val x1 = up.getStepX * sizes(1) + side.getStepX * sizes(2) - forward.getStepX * 0.5f + val y0 = -up.getStepY * sizes(1) - side.getStepY * sizes(2) - forward.getStepY * sizes(0) + val y1 = up.getStepY * sizes(1) + side.getStepY * sizes(2) - forward.getStepY * 0.5f + val z0 = -up.getStepZ * sizes(1) - side.getStepZ * sizes(2) - forward.getStepZ * sizes(0) + val z1 = up.getStepZ * sizes(1) + side.getStepZ * sizes(2) - forward.getStepZ * 0.5f + new AxisAlignedBB(x0, y0, z0, x1, y1, z1).move(0.5, 0.5, 0.5) case _ => super.getBoundingBox(state, world, pos) } // ----------------------------------------------------------------------- // - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = true - - override def preItemRender(metadata: Int) { - GlStateManager.translate(-0.75f, 0, 0) - GlStateManager.scale(1.5f, 1.5f, 1.5f) - } + override def newBlockEntity(world: IBlockReader) = new tileentity.Keyboard() // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Keyboard() - - // ----------------------------------------------------------------------- // + override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { + if (!world.isClientSide) { + world.asInstanceOf[ServerWorld].getBlockTicks.scheduleTick(pos, this, 10) + } + } - override def updateTick(world: World, pos: BlockPos, state: IBlockState, rand: Random) = - world.getTileEntity(pos) match { + override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: Random) = { + world.getBlockEntity(pos) match { case keyboard: tileentity.Keyboard => api.Network.joinOrCreateNetwork(keyboard) case _ => } + world.getBlockTicks.scheduleTick(pos, this, 10) + } - override def canPlaceBlockOnSide(world: World, pos: BlockPos, side: EnumFacing) = { - world.isSideSolid(pos.offset(side.getOpposite), side) && - (world.getTileEntity(pos.offset(side.getOpposite)) match { - case screen: tileentity.Screen => screen.facing != side - case _ => true - }) + override def canSurvive(state: BlockState, world: IWorldReader, pos: BlockPos) = { + world.getBlockEntity(pos) match { + case keyboard: tileentity.Keyboard => { + val side = keyboard.facing + val sidePos = pos.relative(side.getOpposite) + world.getBlockState(sidePos).isFaceSturdy(world, sidePos, side) && + (world.getBlockEntity(pos.relative(side.getOpposite)) match { + case screen: tileentity.Screen => screen.facing != side + case _ => true + }) + } + case _ => false + } } - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos): Unit = - world.getTileEntity(pos) match { - case keyboard: tileentity.Keyboard => - if (!canPlaceBlockOnSide(world, pos, keyboard.facing)) { - world.setBlockToAir(pos) - InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), api.Items.get(Constants.BlockName.Keyboard).createItemStack(1)) - } - case _ => + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos, b: Boolean): Unit = + if (!canSurvive(world.getBlockState(pos), world, pos)) { + world.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState) + InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), api.Items.get(Constants.BlockName.Keyboard).createItemStack(1)) } - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = adjacencyInfo(world, pos) match { case Some((keyboard, screen, blockPos, facing)) => screen.rightClick(world, blockPos, player, hand, heldItem, facing, 0, 0, 0, force = true) case _ => false } def adjacencyInfo(world: World, pos: BlockPos) = - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case keyboard: tileentity.Keyboard => - val blockPos = pos.offset(keyboard.facing.getOpposite) + val blockPos = pos.relative(keyboard.facing.getOpposite) world.getBlockState(blockPos).getBlock match { case screen: Screen => Some((keyboard, screen, blockPos, keyboard.facing.getOpposite)) case _ => // Special case #1: check for screen in front of the keyboard. val forward = keyboard.facing match { - case EnumFacing.UP | EnumFacing.DOWN => keyboard.yaw - case _ => EnumFacing.UP + case Direction.UP | Direction.DOWN => keyboard.yaw + case _ => Direction.UP } - val blockPos = pos.offset(forward) + val blockPos = pos.relative(forward) world.getBlockState(blockPos).getBlock match { case screen: Screen => Some((keyboard, screen, blockPos, forward)) - case _ if keyboard.facing != EnumFacing.UP && keyboard.facing != EnumFacing.DOWN => + case _ if keyboard.facing != Direction.UP && keyboard.facing != Direction.DOWN => // Special case #2: check for screen below keyboards on walls. - val blockPos = pos.offset(forward.getOpposite) + val blockPos = pos.relative(forward.getOpposite) world.getBlockState(blockPos).getBlock match { case screen: Screen => Some((keyboard, screen, blockPos, forward.getOpposite)) case _ => None diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index ab5979b96a..f5fbb183af 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -16,17 +16,20 @@ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity import li.cil.oc.util.StackOption._ import net.minecraft.block.Block -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item // Rarity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World import scala.reflect.ClassTag @@ -35,33 +38,30 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc setCreativeTab(null) ItemBlacklist.hide(this) - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Facing) - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta)) - - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing) // ----------------------------------------------------------------------- // - override def getPickBlock(state: IBlockState, target: RayTraceResult, world: World, pos: BlockPos, player: EntityPlayer): ItemStack = - world.getTileEntity(pos) match { + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = + world.getBlockEntity(pos) match { case mcu: tileentity.Microcontroller => mcu.info.copyItemStack() case _ => ItemStack.EMPTY } // ----------------------------------------------------------------------- // - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, advanced) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, advanced) if (KeyBindings.showExtendedTooltips) { val info = new MicrocontrollerData(stack) for (component <- info.components if !component.isEmpty) { - tooltip.add("- " + component.getDisplayName) + tooltip.add(new StringTextComponent("- " + component.getDisplayName)) } } } - override def rarity(stack: ItemStack): EnumRarity = { + override def rarity(stack: ItemStack): item.Rarity = { val data = new MicrocontrollerData(stack) Rarity.byTier(data.tier) } @@ -70,15 +70,15 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc override def energyThroughput: Double = Settings.get.caseRate(Tier.One) - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Microcontroller() + override def newBlockEntity(world: IBlockReader) = new tileentity.Microcontroller() // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { if (!Wrench.holdsApplicableWrench(player, pos)) { - if (!player.isSneaking) { - if (!world.isRemote) { - world.getTileEntity(pos) match { + if (!player.isCrouching) { + if (!world.isClientSide) { + world.getBlockEntity(pos) match { case mcu: tileentity.Microcontroller => if (mcu.machine.isRunning) mcu.machine.stop() else mcu.machine.start() @@ -88,10 +88,10 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc true } else if (api.Items.get(heldItem) == api.Items.get(Constants.ItemName.EEPROM)) { - if (!world.isRemote) { - world.getTileEntity(pos) match { + if (!world.isClientSide) { + world.getBlockEntity(pos) match { case mcu: tileentity.Microcontroller => - val newEeprom = player.inventory.decrStackSize(player.inventory.currentItem, 1) + val newEeprom = player.inventory.removeItem(player.inventory.selected, 1) mcu.changeEEPROM(newEeprom) match { case SomeStack(oldEeprom) => InventoryUtils.addToPlayerInventory(oldEeprom, player) case _ => @@ -105,18 +105,18 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc else false } - override protected def doCustomInit(tileEntity: tileentity.Microcontroller, player: EntityLivingBase, stack: ItemStack): Unit = { + override protected def doCustomInit(tileEntity: tileentity.Microcontroller, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isRemote) { - tileEntity.info.load(stack) + if (!tileEntity.world.isClientSide) { + tileEntity.info.loadData(stack) tileEntity.snooperNode.changeBuffer(tileEntity.info.storedEnergy - tileEntity.snooperNode.localBuffer) } } - override protected def doCustomDrops(tileEntity: tileentity.Microcontroller, player: EntityPlayer, willHarvest: Boolean): Unit = { + override protected def doCustomDrops(tileEntity: tileentity.Microcontroller, player: PlayerEntity, willHarvest: Boolean): Unit = { super.doCustomDrops(tileEntity, player, willHarvest) tileEntity.saveComponents() tileEntity.info.storedEnergy = tileEntity.snooperNode.localBuffer.toInt - Block.spawnAsEntity(tileEntity.world, tileEntity.getPos, tileEntity.info.createItemStack()) + Block.popResource(tileEntity.world, tileEntity.getBlockPos, tileEntity.info.createItemStack()) } } diff --git a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala index 82cf657bf9..2074c2d6fe 100644 --- a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala @@ -1,8 +1,9 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.world.IBlockReader import net.minecraft.world.World class MotionSensor extends SimpleBlock { - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.MotionSensor() + override def newBlockEntity(world: IBlockReader) = new tileentity.MotionSensor() } diff --git a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala index 76ca51eb69..2beeea1914 100644 --- a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala @@ -1,53 +1,38 @@ package li.cil.oc.common.block -import li.cil.oc.common.block.property.PropertyTile import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.world.IBlockReader import net.minecraft.world.World -import net.minecraftforge.common.property.ExtendedBlockState -import net.minecraftforge.common.property.IExtendedBlockState class NetSplitter extends RedstoneAware { - override def createBlockState() = new ExtendedBlockState(this, Array.empty, Array(PropertyTile.Tile)) - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = - (state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, t: tileentity.NetSplitter) => - extendedState.withProperty(property.PropertyTile.Tile, t) - case _ => state - } - - // ----------------------------------------------------------------------- // - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = false - - // ----------------------------------------------------------------------- // - - override def createNewTileEntity(world: World, meta: Int) = new tileentity.NetSplitter() + override def newBlockEntity(world: IBlockReader) = new tileentity.NetSplitter() // ----------------------------------------------------------------------- // // NOTE: must not be final for immibis microblocks to work. - override def onBlockActivated(world: World, pos: BlockPos, state: IBlockState, player: EntityPlayer, hand: EnumHand, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { if (Wrench.holdsApplicableWrench(player, pos)) { - val sideToToggle = if (player.isSneaking) side.getOpposite else side - world.getTileEntity(pos) match { + val side = trace.getDirection + val sideToToggle = if (player.isCrouching) side.getOpposite else side + world.getBlockEntity(pos) match { case splitter: tileentity.NetSplitter => - if (!world.isRemote) { + if (!world.isClientSide) { val oldValue = splitter.openSides(sideToToggle.ordinal()) splitter.setSideOpen(sideToToggle, !oldValue) } - true - case _ => false + ActionResultType.sidedSuccess(world.isClientSide) + case _ => ActionResultType.PASS } } - else super.onBlockActivated(world, pos, state, player, hand, side, hitX, hitY, hitZ) + else super.use(state, world, pos, player, hand, trace) } } diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index df3a58674e..12f75074ea 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -10,6 +10,7 @@ import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack +import net.minecraft.world.IBlockReader import net.minecraft.world.World class PowerConverter extends SimpleBlock with traits.PowerAcceptor { @@ -22,5 +23,5 @@ class PowerConverter extends SimpleBlock with traits.PowerAcceptor { override def energyThroughput: Double = Settings.get.powerConverterRate - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.PowerConverter() + override def newBlockEntity(world: IBlockReader) = new tileentity.PowerConverter() } diff --git a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala index ab71ceb3a5..9c714dfa71 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala @@ -1,9 +1,10 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.world.IBlockReader import net.minecraft.world.World class PowerDistributor extends SimpleBlock { - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.PowerDistributor() + override def newBlockEntity(world: IBlockReader) = new tileentity.PowerDistributor() } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index e385129aa7..85cc75a0a3 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -5,148 +5,114 @@ import java.util.Random import li.cil.oc.Localization import li.cil.oc.Settings -import li.cil.oc.common.block.property.PropertyTile import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.util.InventoryUtils -import net.minecraft.block.state.IBlockState +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.BlockState +import net.minecraft.block.material.Material import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLiving.SpawnPlacementType -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.EntityType +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util._ +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d -import net.minecraft.world.IBlockAccess +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World -import net.minecraftforge.common.property.ExtendedBlockState -import net.minecraftforge.common.property.IExtendedBlockState +import net.minecraft.world.server.ServerWorld import scala.collection.convert.WrapAsJava._ import scala.reflect.ClassTag -class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware with traits.CustomDrops[tileentity.Print] { - setLightOpacity(1) - setHardness(1) +class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware(Properties.of(Material.METAL).strength(1, 5).noOcclusion()) + with traits.CustomDrops[tileentity.Print] { setCreativeTab(null) ItemBlacklist.hide(this) - // ----------------------------------------------------------------------- // - - override def createBlockState() = new ExtendedBlockState(this, Array.empty, Array(PropertyTile.Tile)) - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = - (state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, t: tileentity.Print) => - extendedState.withProperty(property.PropertyTile.Tile, t) - case _ => state - } + @Deprecated + override def propagatesSkylightDown(state: BlockState, world: IBlockReader, pos: BlockPos) = false // ----------------------------------------------------------------------- // - override def canRenderInLayer(state: IBlockState, layer: BlockRenderLayer): Boolean = layer == BlockRenderLayer.CUTOUT_MIPPED - - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) = { - super.tooltipBody(metadata, stack, world, tooltip, advanced) + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) = { + super.tooltipBody(stack, world, tooltip, advanced) val data = new PrintData(stack) - data.tooltip.foreach(s => tooltip.addAll(s.lines.toIterable)) + data.tooltip.foreach(s => tooltip.addAll(s.lines.map(new StringTextComponent(_)).toIterable)) } - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) = { - super.tooltipTail(metadata, stack, world, tooltip, advanced) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) = { + super.tooltipTail(stack, world, tooltip, advanced) val data = new PrintData(stack) if (data.isBeaconBase) { - tooltip.add(Localization.Tooltip.PrintBeaconBase) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintBeaconBase)) } if (data.emitRedstone) { - tooltip.add(Localization.Tooltip.PrintRedstoneLevel(data.redstoneLevel)) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintRedstoneLevel(data.redstoneLevel))) } if (data.emitLight) { - tooltip.add(Localization.Tooltip.PrintLightValue(data.lightLevel)) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintLightValue(data.lightLevel))) } } - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def getLightValue(state: IBlockState, world: IBlockAccess, pos: BlockPos): Int = + override def getLightValue(state: BlockState, world: IBlockReader, pos: BlockPos): Int = world match { - case world: World if world.isBlockLoaded(pos) => world.getTileEntity(pos) match { + case world: World if world.isLoaded(pos) => world.getBlockEntity(pos) match { case print: tileentity.Print => print.data.lightLevel case _ => super.getLightValue(state, world, pos) } case _ => super.getLightValue(state, world, pos) } - override def getLightOpacity(state: IBlockState, world: IBlockAccess, pos: BlockPos): Int = + @Deprecated + override def getLightBlock(state: BlockState, world: IBlockReader, pos: BlockPos): Int = world match { - case world: World if world.isBlockLoaded(pos) => world.getTileEntity(pos) match { + case world: World if world.isLoaded(pos) => world.getBlockEntity(pos) match { case print: tileentity.Print if Settings.get.printsHaveOpacity => (print.data.opacity * 4).toInt - case _ => super.getLightOpacity(state, world, pos) + case _ => super.getLightBlock(state, world, pos) } - case _ => super.getLightOpacity(state, world, pos) - } - - override def isFullCube(state: IBlockState): Boolean = false - - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = true - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = - world.getTileEntity(pos) match { - case print: tileentity.Print => print.isSideSolid(side) - case _ => false + case _ => super.getLightBlock(state, world, pos) } - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = - isBlockSolid(world, pos, side) - - override def getPickBlock(state: IBlockState, target: RayTraceResult, world: World, pos: BlockPos, player: EntityPlayer): ItemStack = { - world.getTileEntity(pos) match { + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = { + world.getBlockEntity(pos) match { case print: tileentity.Print => print.data.createItemStack() case _ => ItemStack.EMPTY } } - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = { - world.getTileEntity(pos) match { + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + world.getBlockEntity(pos) match { case print: tileentity.Print => print.bounds case _ => super.getBoundingBox(state, world, pos) } } - override def addCollisionBoxToList(state: IBlockState, world: World, pos: BlockPos, mask: AxisAlignedBB, list: util.List[AxisAlignedBB], entity: Entity, par7: Boolean): Unit = { - world.getTileEntity(pos) match { - case print: tileentity.Print => print.addCollisionBoxesToList(mask, list, pos) - case _ => super.addCollisionBoxToList(state, world, pos, mask, list, entity, par7) - } - } - - override def collisionRayTrace(state: IBlockState, world: World, pos: BlockPos, start: Vec3d, end: Vec3d): RayTraceResult = { - world.getTileEntity(pos) match { - case print: tileentity.Print => print.rayTrace(start, end, pos) - case _ => super.collisionRayTrace(state, world, pos, start, end) - } - } - - override def canCreatureSpawn(state: IBlockState, world: IBlockAccess, pos: BlockPos, `type`: SpawnPlacementType): Boolean = true + override def isValidSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, `type`: EntityType[_]): Boolean = true - override def tickRate(world: World) = 20 + def tickRate(world: World) = 20 - override def updateTick(world: World, pos: BlockPos, state: IBlockState, rand: Random): Unit = { - if (!world.isRemote) world.getTileEntity(pos) match { + override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: Random): Unit = { + if (!world.isClientSide) world.getBlockEntity(pos) match { case print: tileentity.Print => if (print.state) print.toggleState() - if (print.state) world.scheduleUpdate(pos, state.getBlock, tickRate(world)) case _ => } } - override def isBeaconBase(world: IBlockAccess, pos: BlockPos, beacon: BlockPos): Boolean = { - world.getTileEntity(pos) match { + @Deprecated + def isBeaconBase(world: IBlockReader, pos: BlockPos, beacon: BlockPos): Boolean = { + world.getBlockEntity(pos) match { case print: tileentity.Print => print.data.isBeaconBase case _ => false } @@ -154,41 +120,41 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends // ----------------------------------------------------------------------- // - override def createNewTileEntity(worldIn: World, meta: Int) = new tileentity.Print() + override def newBlockEntity(worldIn: IBlockReader) = new tileentity.Print() // ----------------------------------------------------------------------- // - override def onBlockActivated(world: World, pos: BlockPos, state: IBlockState, player: EntityPlayer, hand: EnumHand, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - world.getTileEntity(pos) match { - case print: tileentity.Print => print.activate() - case _ => super.onBlockActivated(world, pos, state, player, hand, side, hitX, hitY, hitZ) + override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { + world.getBlockEntity(pos) match { + case print: tileentity.Print => if (print.activate()) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + case _ => super.use(state, world, pos, player, hand, trace) } } - override protected def doCustomInit(tileEntity: tileentity.Print, player: EntityLivingBase, stack: ItemStack): Unit = { + override protected def doCustomInit(tileEntity: tileentity.Print, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - tileEntity.data.load(stack) + tileEntity.data.loadData(stack) tileEntity.updateBounds() tileEntity.updateRedstone() - tileEntity.getWorld.checkLight(tileEntity.getPos) + tileEntity.getLevel.getLightEngine.checkBlock(tileEntity.getBlockPos) } - override protected def doCustomDrops(tileEntity: tileentity.Print, player: EntityPlayer, willHarvest: Boolean): Unit = { + override protected def doCustomDrops(tileEntity: tileentity.Print, player: PlayerEntity, willHarvest: Boolean): Unit = { super.doCustomDrops(tileEntity, player, willHarvest) - if (!player.capabilities.isCreativeMode) { + if (!player.isCreative) { InventoryUtils.spawnStackInWorld(tileEntity.position, tileEntity.data.createItemStack()) } } - override def breakBlock(world: World, pos: BlockPos, state: IBlockState): Unit = { - world.getTileEntity(pos) match { + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { + world.getBlockEntity(pos) match { case print: tileentity.Print if print.data.emitRedstone(print.state) => - world.notifyNeighborsOfStateChange(pos, this, false) - for (side <- EnumFacing.values) { - world.notifyNeighborsOfStateChange(pos.offset(side), this, false) + world.updateNeighborsAt(pos, this) + for (side <- Direction.values) { + world.updateNeighborsAt(pos.relative(side), this) } case _ => } - super.breakBlock(world, pos, state) + super.onRemove(state, world, pos, newState, moved) } } diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala index 78bc42fd20..08b5f34525 100644 --- a/src/main/scala/li/cil/oc/common/block/Printer.scala +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -2,24 +2,14 @@ package li.cil.oc.common.block import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.util.EnumFacing +import net.minecraft.block.BlockState +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Printer extends SimpleBlock with traits.StateAware with traits.GUI { - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.DOWN - - // ----------------------------------------------------------------------- // - override def guiType = GuiType.Printer - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Printer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Printer() } diff --git a/src/main/scala/li/cil/oc/common/block/Rack.scala b/src/main/scala/li/cil/oc/common/block/Rack.scala index e2e20051ed..6563e5bafa 100644 --- a/src/main/scala/li/cil/oc/common/block/Rack.scala +++ b/src/main/scala/li/cil/oc/common/block/Rack.scala @@ -5,58 +5,25 @@ import li.cil.oc.api.component.RackMountable import li.cil.oc.common.GuiType import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumFacing.Axis -import net.minecraft.util.EnumHand +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Direction.Axis +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d -import net.minecraft.world.IBlockAccess +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.world.IBlockReader import net.minecraft.world.World -import net.minecraftforge.common.property.ExtendedBlockState -import net.minecraftforge.common.property.IExtendedBlockState class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { - override def createBlockState() = new ExtendedBlockState(this, Array(PropertyRotatable.Facing), Array(property.PropertyTile.Tile)) - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta)) - - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = { - ((state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, tile: tileentity.traits.TileEntity) => - extendedState.withProperty(property.PropertyTile.Tile, tile) - case _ => state - }).withProperty(PropertyRotatable.Facing, getFacing(world, pos)) - } - - // @SideOnly(Side.CLIENT) - // override def getMixedBrightnessForBlock(world: IBlockAccess, pos: BlockPos) = { - // if (pos.getY >= 0 && pos.getY < 256) world.getTileEntity(pos) match { - // case rack: tileentity.Rack => - // def brightness(pos: BlockPos) = world.getCombinedLight(pos, world.getBlockState(pos).getBlock.getLightValue(world, pos)) - // val value = brightness(pos.offset(rack.facing)) - // val skyBrightness = (value >> 20) & 15 - // val blockBrightness = (value >> 4) & 15 - // ((skyBrightness * 3 / 4) << 20) | ((blockBrightness * 3 / 4) << 4) - // case _ => super.getMixedBrightnessForBlock(world, pos) - // } - // else super.getMixedBrightnessForBlock(world, pos) - // } - - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = side == EnumFacing.SOUTH - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = toLocal(world, pos, side) != EnumFacing.SOUTH + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing) // ----------------------------------------------------------------------- // @@ -64,62 +31,24 @@ class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAwar override def guiType = GuiType.Rack - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Rack() + override def newBlockEntity(world: IBlockReader) = new tileentity.Rack() // ----------------------------------------------------------------------- // - final val collisionBounds = Array( - new AxisAlignedBB(0, 0, 0, 1, 1 / 16f, 1), - new AxisAlignedBB(0, 15 / 16f, 0, 1, 1, 1), - new AxisAlignedBB(0, 0, 0, 1, 1, 1 / 16f), - new AxisAlignedBB(0, 0, 15 / 16f, 1, 1, 1), - new AxisAlignedBB(0, 0, 0, 1 / 16f, 1, 1), - new AxisAlignedBB(15 / 16f, 0, 0, 1, 1, 1), - new AxisAlignedBB(0.5f / 16f, 0.5f / 16f, 0.5f / 16f, 15.5f / 16f, 15.5f / 16f, 15.5f / 16f) - ) - - override def collisionRayTrace(state: IBlockState, world: World, pos: BlockPos, start: Vec3d, end: Vec3d): RayTraceResult = { - world.getTileEntity(pos) match { - case rack: tileentity.Rack => - var closestDistance = Double.PositiveInfinity - var closest: Option[RayTraceResult] = None - - def intersect(bounds: AxisAlignedBB): Unit = { - val hit = bounds.offset(pos.getX, pos.getY, pos.getZ).calculateIntercept(start, end) - if (hit != null) { - val distance = hit.hitVec.distanceTo(start) - if (distance < closestDistance) { - closestDistance = distance - closest = Option(hit) - } - } - } - val facings = EnumFacing.VALUES - for (i <- 0 until facings.length) { - if (rack.facing != facings(i)) { - intersect(collisionBounds(i)) - } - } - intersect(collisionBounds.last) - closest.map(hit => new RayTraceResult(hit.hitVec, hit.sideHit, pos)).orNull - case _ => super.collisionRayTrace(state, world, pos, start, end) - } - } - - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - world.getTileEntity(pos) match { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + world.getBlockEntity(pos) match { case rack: tileentity.Rack => rack.slotAt(side, hitX, hitY, hitZ) match { case Some(slot) => // Snap to grid to get same behavior on client and server... - val hitVec = new Vec3d((hitX * 16f).toInt / 16f, (hitY * 16f).toInt / 16f, (hitZ * 16f).toInt / 16f) + val hitVec = new Vector3d((hitX * 16f).toInt / 16f, (hitY * 16f).toInt / 16f, (hitZ * 16f).toInt / 16f) val rotation = side match { - case EnumFacing.WEST => Math.toRadians(90).toFloat - case EnumFacing.NORTH => Math.toRadians(180).toFloat - case EnumFacing.EAST => Math.toRadians(270).toFloat + case Direction.WEST => Math.toRadians(90).toFloat + case Direction.NORTH => Math.toRadians(180).toFloat + case Direction.EAST => Math.toRadians(270).toFloat case _ => 0 } // Rotate *centers* of pixels to keep association when reversing axis. - val localHitVec = rotate(hitVec.addVector(-0.5 + 1 / 32f, -0.5 + 1 / 32f, -0.5 + 1 / 32f), rotation).addVector(0.5 - 1 / 32f, 0.5 - 1 / 32f, 0.5 - 1 / 32f) + val localHitVec = rotate(hitVec.add(-0.5 + 1 / 32f, -0.5 + 1 / 32f, -0.5 + 1 / 32f), rotation).add(0.5 - 1 / 32f, 0.5 - 1 / 32f, 0.5 - 1 / 32f) val globalX = (localHitVec.x * 16.05f).toInt // [0, 15], work around floating point inaccuracies val globalY = (localHitVec.y * 16.05f).toInt // [0, 15], work around floating point inaccuracies val localX = (if (side.getAxis != Axis.Z) 15 - globalX else globalX) - 1 @@ -135,9 +64,9 @@ class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAwar super.localOnBlockActivated(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ) } - def rotate(v: Vec3d, t: Float): Vec3d = { + def rotate(v: Vector3d, t: Float): Vector3d = { val cos = Math.cos(t) val sin = Math.sin(t) - new Vec3d(v.x * cos - v.z * sin, v.y, v.x * sin + v.z * cos) + new Vector3d(v.x * cos - v.z * sin, v.y, v.x * sin + v.z * cos) } } diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index b230a8b924..466cf6f98b 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -8,31 +8,31 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity import net.minecraft.block.Block -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World import scala.reflect.ClassTag class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends SimpleBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { - override def createBlockState() = new BlockStateContainer(this, PropertyRotatable.Facing) + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Facing) - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Facing, EnumFacing.getHorizontal(meta)) - - override def getMetaFromState(state: IBlockState): Int = state.getValue(PropertyRotatable.Facing).getHorizontalIndex - - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, advanced) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, advanced) if (KeyBindings.showExtendedTooltips) { val data = new RaidData(stack) for (disk <- data.disks if !disk.isEmpty) { - tooltip.add("- " + disk.getDisplayName) + tooltip.add(new StringTextComponent("- " + disk.getDisplayName)) } } } @@ -41,43 +41,43 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si override def guiType = GuiType.Raid - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Raid() + override def newBlockEntity(world: IBlockReader) = new tileentity.Raid() // ----------------------------------------------------------------------- // - override def hasComparatorInputOverride(state: IBlockState): Boolean = true + override def hasAnalogOutputSignal(state: BlockState): Boolean = true - override def getComparatorInputOverride(state: IBlockState, world: World, pos: BlockPos): Int = - world.getTileEntity(pos) match { + override def getAnalogOutputSignal(state: BlockState, world: World, pos: BlockPos): Int = + world.getBlockEntity(pos) match { case raid: tileentity.Raid if raid.presence.forall(ok => ok) => 15 case _ => 0 } - override protected def doCustomInit(tileEntity: tileentity.Raid, player: EntityLivingBase, stack: ItemStack): Unit = { + override protected def doCustomInit(tileEntity: tileentity.Raid, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isRemote) { + if (!tileEntity.world.isClientSide) { val data = new RaidData(stack) - for (i <- 0 until math.min(data.disks.length, tileEntity.getSizeInventory)) { - tileEntity.setInventorySlotContents(i, data.disks(i)) + for (i <- 0 until math.min(data.disks.length, tileEntity.getContainerSize)) { + tileEntity.setItem(i, data.disks(i)) } data.label.foreach(tileEntity.label.setLabel) - if (!data.filesystem.hasNoTags) { - tileEntity.tryCreateRaid(data.filesystem.getCompoundTag("node").getString("address")) - tileEntity.filesystem.foreach(_.load(data.filesystem)) + if (!data.filesystem.isEmpty) { + tileEntity.tryCreateRaid(data.filesystem.getCompound("node").getString("address")) + tileEntity.filesystem.foreach(_.loadData(data.filesystem)) } } } - override protected def doCustomDrops(tileEntity: tileentity.Raid, player: EntityPlayer, willHarvest: Boolean): Unit = { + override protected def doCustomDrops(tileEntity: tileentity.Raid, player: PlayerEntity, willHarvest: Boolean): Unit = { super.doCustomDrops(tileEntity, player, willHarvest) val stack = createItemStack() if (tileEntity.items.exists(!_.isEmpty)) { val data = new RaidData() data.disks = tileEntity.items.clone() - tileEntity.filesystem.foreach(_.save(data.filesystem)) + tileEntity.filesystem.foreach(_.saveData(data.filesystem)) data.label = Option(tileEntity.label.getLabel) - data.save(stack) + data.saveData(stack) } - Block.spawnAsEntity(tileEntity.world, tileEntity.getPos, stack) + Block.popResource(tileEntity.world, tileEntity.getBlockPos, stack) } } diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index 8e3fef5e3d..c4c226e538 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -6,20 +6,25 @@ import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World +import scala.collection.convert.WrapAsScala._ + class Redstone extends RedstoneAware { - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, advanced) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, advanced) // todo more generic way for redstone mods to provide lines if (Mods.ProjectRedTransmission.isModAvailable) { - tooltip.addAll(Tooltip.get("RedstoneCard.ProjectRed")) + for (curr <- Tooltip.get("RedstoneCard.ProjectRed")) tooltip.add(new StringTextComponent(curr)) } } // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Redstone() + override def newBlockEntity(world: IBlockReader) = new tileentity.Redstone() } diff --git a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala index 8be1840b00..a822aa0b1f 100644 --- a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala @@ -1,35 +1,39 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState -import net.minecraft.util.EnumFacing +import net.minecraft.block.BlockState +import net.minecraft.block.material.Material +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World -abstract class RedstoneAware extends SimpleBlock { - override def canProvidePower(state: IBlockState): Boolean = true +abstract class RedstoneAware(props: Properties = Properties.of(Material.METAL)) extends SimpleBlock(props) { + override def isSignalSource(state: BlockState): Boolean = true - override def canConnectRedstone(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = - world.getTileEntity(pos) match { + override def canConnectRedstone(state: BlockState, world: IBlockReader, pos: BlockPos, side: Direction): Boolean = + world.getBlockEntity(pos) match { case redstone: tileentity.traits.RedstoneAware => redstone.isOutputEnabled case _ => false } - override def getStrongPower(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = - getWeakPower(state, world, pos, side) + override def getDirectSignal(state: BlockState, world: IBlockReader, pos: BlockPos, side: Direction) = + getSignal(state, world, pos, side) - override def getWeakPower(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = - world.getTileEntity(pos) match { + @Deprecated + override def getSignal(state: BlockState, world: IBlockReader, pos: BlockPos, side: Direction) = + world.getBlockEntity(pos) match { case redstone: tileentity.traits.RedstoneAware if side != null => redstone.getOutput(side.getOpposite) max 0 - case _ => super.getWeakPower(state, world, pos, side) + case _ => super.getSignal(state, world, pos, side) } // ----------------------------------------------------------------------- // - override def neighborChanged(state: IBlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos): Unit = { - world.getTileEntity(pos) match { + @Deprecated + override def neighborChanged(state: BlockState, world: World, pos: BlockPos, block: Block, fromPos: BlockPos, b: Boolean): Unit = { + world.getBlockEntity(pos) match { case redstone: tileentity.traits.RedstoneAware => redstone.checkRedstoneInputChanged() case _ => // Ignore. } diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala index 2d9ea12b20..49e5fc5aa9 100644 --- a/src/main/scala/li/cil/oc/common/block/Relay.scala +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.block import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { @@ -10,5 +11,5 @@ class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { override def energyThroughput = Settings.get.accessPointRate - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Relay() + override def newBlockEntity(world: IBlockReader) = new tileentity.Relay() } diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index 7d8e890daf..c72bb45e10 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -9,101 +9,94 @@ import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.util.Rarity -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.Blocks +import net.minecraft.block.BlockState +import net.minecraft.block.material.Material +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.FluidState import net.minecraft.item.ItemStack -import net.minecraft.util._ +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3i -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld -class RobotAfterimage extends SimpleBlock { - setLightOpacity(0) +class RobotAfterimage(props: Properties = Properties.of(Material.STONE).strength(2, 5).noOcclusion()) extends SimpleBlock(props) { setCreativeTab(null) ItemBlacklist.hide(this) // ----------------------------------------------------------------------- // - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def getPickBlock(state: IBlockState, target: RayTraceResult, world: World, pos: BlockPos, player: EntityPlayer): ItemStack = + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = findMovingRobot(world, pos) match { case Some(robot) => robot.info.createItemStack() case _ => ItemStack.EMPTY } - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = { + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { findMovingRobot(world, pos) match { case Some(robot) => - val block = robot.getBlockType.asInstanceOf[SimpleBlock] - val bounds = block.getBoundingBox(state, world, robot.getPos) - val delta = robot.moveFrom.fold(Vec3i.NULL_VECTOR)(vec => { - val blockPos = robot.getPos + val block = robot.getBlockState.getBlock.asInstanceOf[SimpleBlock] + val bounds = block.getBoundingBox(state, world, robot.getBlockPos) + val delta = robot.moveFrom.fold(BlockPos.ZERO)(vec => { + val blockPos = robot.getBlockPos new BlockPos(blockPos.getX - vec.getX, blockPos.getY - vec.getY, blockPos.getZ - vec.getZ) }) - bounds.offset(delta.getX, delta.getY, delta.getZ) + bounds.move(delta) case _ => super.getBoundingBox(state, world, pos) } } // ----------------------------------------------------------------------- // - override def hasTileEntity(state: IBlockState): Boolean = false - - override def createNewTileEntity(worldIn: World, meta: Int) = null - - // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack) = { val data = new RobotData(stack) Rarity.byTier(data.tier) } - override def isAir(state: IBlockState, world: IBlockAccess, pos: BlockPos): Boolean = true + override def isAir(state: BlockState, world: IBlockReader, pos: BlockPos): Boolean = true // ----------------------------------------------------------------------- // - override def onBlockAdded(world: World, pos: BlockPos, state: IBlockState) { - world.scheduleUpdate(pos, this, Math.max((Settings.get.moveDelay * 20).toInt, 1) - 1) + override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { + if (!world.isClientSide) { + world.asInstanceOf[ServerWorld].getBlockTicks.scheduleTick(pos, this, Math.max((Settings.get.moveDelay * 20).toInt, 1) - 1) + } } - override def updateTick(world: World, pos: BlockPos, state: IBlockState, rand: Random) { - world.setBlockToAir(pos) + override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: Random) { + world.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState) } - override def removedByPlayer(state: IBlockState, world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean): Boolean = { + override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = { findMovingRobot(world, pos) match { case Some(robot) if robot.isAnimatingMove && robot.moveFrom.contains(pos) => - robot.proxy.getBlockType.removedByPlayer(state, world, pos, player, false) - case _ => super.removedByPlayer(state, world, pos, player, willHarvest) // Probably broken by the robot we represent. + robot.proxy.getBlockState.getBlock.removedByPlayer(state, world, pos, player, false, fluid) + case _ => super.removedByPlayer(state, world, pos, player, willHarvest, fluid) // Probably broken by the robot we represent. } } - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { + @Deprecated + override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { findMovingRobot(world, pos) match { - case Some(robot) => api.Items.get(Constants.BlockName.Robot).block.onBlockActivated(world, robot.getPos, world.getBlockState(robot.getPos), player, hand, side, hitX, hitY, hitZ) - case _ => world.setBlockToAir(pos) + case Some(robot) => api.Items.get(Constants.BlockName.Robot).block.use(world.getBlockState(robot.getBlockPos), world, robot.getBlockPos, player, hand, trace) + case _ => if (world.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState)) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS } } - def findMovingRobot(world: IBlockAccess, pos: BlockPos): Option[tileentity.Robot] = { - for (side <- EnumFacing.values) { - val tpos = pos.offset(side) + def findMovingRobot(world: IBlockReader, pos: BlockPos): Option[tileentity.Robot] = { + for (side <- Direction.values) { + val tpos = pos.relative(side) if (world match { - case world: World => world.isBlockLoaded(tpos) + case world: World => world.isLoaded(tpos) case _ => true - }) world.getTileEntity(tpos) match { + }) world.getBlockEntity(tpos) match { case proxy: tileentity.RobotProxy if proxy.robot.moveFrom.contains(pos) => return Some(proxy.robot) case _ => } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 437b13dc45..dfa985b749 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -17,27 +17,37 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip -import net.minecraft.block.state.IBlockState +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.BlockState +import net.minecraft.block.material.Material import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.FluidState +import net.minecraft.item import net.minecraft.item.ItemStack -import net.minecraft.util._ +import net.minecraft.loot.LootContext +import net.minecraft.loot.LootParameters +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d -import net.minecraft.world.IBlockAccess +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World -class RobotProxy extends RedstoneAware with traits.StateAware { - setLightOpacity(0) +import scala.collection.convert.WrapAsScala._ + +class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 10).noOcclusion()) extends RedstoneAware(props) with traits.StateAware { setCreativeTab(null) ItemBlacklist.hide(this) - override val getUnlocalizedName = "robot" + override val getDescriptionId = "robot" var moving = new ThreadLocal[Option[tileentity.Robot]] { override protected def initialValue = None @@ -45,33 +55,23 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // ----------------------------------------------------------------------- // - override def isOpaqueCube(state: IBlockState): Boolean = false - - override def isFullCube(state: IBlockState): Boolean = false - - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = false - - override def getPickBlock(state: IBlockState, target: RayTraceResult, world: World, pos: BlockPos, player: EntityPlayer): ItemStack = - world.getTileEntity(pos) match { + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = + world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy => proxy.robot.info.copyItemStack() case _ => ItemStack.EMPTY } - override def getBoundingBox(state: IBlockState, world: IBlockAccess, pos: BlockPos): AxisAlignedBB = { - world.getTileEntity(pos) match { + override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy => val robot = proxy.robot val bounds = new AxisAlignedBB(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) if (robot.isAnimatingMove) { val remaining = robot.animationTicksLeft.toDouble / robot.animationTicksTotal.toDouble val blockPos = robot.moveFrom.get - val vec = robot.getPos + val vec = robot.getBlockPos val delta = new BlockPos(blockPos.getX - vec.getX, blockPos.getY - vec.getY, blockPos.getZ - vec.getZ) - bounds.offset(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) + bounds.move(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) } else bounds case _ => super.getBoundingBox(state, world, pos) @@ -80,47 +80,55 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack): EnumRarity = { + override def rarity(stack: ItemStack): item.Rarity = { val data = new RobotData(stack) Rarity.byTier(data.tier) } - override protected def tooltipHead(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipHead(metadata, stack, world, tooltip, advanced) + override protected def tooltipHead(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + super.tooltipHead(stack, world, tooltip, advanced) addLines(stack, tooltip) } - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - tooltip.addAll(Tooltip.get("robot")) + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + for (curr <- Tooltip.get("robot")) { + tooltip.add(new StringTextComponent(curr)) + } } - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, flag) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, flag) if (KeyBindings.showExtendedTooltips) { val info = new RobotData(stack) val components = info.containers ++ info.components if (components.length > 0) { - tooltip.addAll(Tooltip.get("server.Components")) + for (curr <- Tooltip.get("server.Components")) { + tooltip.add(new StringTextComponent(curr)) + } for (component <- components if !component.isEmpty) { - tooltip.add("- " + component.getDisplayName) + tooltip.add(new StringTextComponent("- " + component.getDisplayName)) } } } } - private def addLines(stack: ItemStack, tooltip: util.List[String]) { - if (stack.hasTagCompound) { - if (stack.getTagCompound.hasKey(Settings.namespace + "xp")) { - val xp = stack.getTagCompound.getDouble(Settings.namespace + "xp") + private def addLines(stack: ItemStack, tooltip: util.List[ITextComponent]) { + if (stack.hasTag) { + if (stack.getTag.contains(Settings.namespace + "xp")) { + val xp = stack.getTag.getDouble(Settings.namespace + "xp") val level = Math.min((Math.pow(xp - Settings.get.baseXpToLevel, 1 / Settings.get.exponentialXpGrowth) / Settings.get.constantXpGrowth).toInt, 30) if (level > 0) { - tooltip.addAll(Tooltip.get(getUnlocalizedName + "_level", level)) + for (curr <- Tooltip.get(getDescriptionId + "_level", level)) { + tooltip.add(new StringTextComponent(curr)) + } } } - if (stack.getTagCompound.hasKey(Settings.namespace + "storedEnergy")) { - val energy = stack.getTagCompound.getInteger(Settings.namespace + "storedEnergy") + if (stack.getTag.contains(Settings.namespace + "storedEnergy")) { + val energy = stack.getTag.getInt(Settings.namespace + "storedEnergy") if (energy > 0) { - tooltip.addAll(Tooltip.get(getUnlocalizedName + "_storedenergy", energy)) + for (curr <- Tooltip.get(getDescriptionId + "_storedenergy", energy)) { + tooltip.add(new StringTextComponent(curr)) + } } } } @@ -128,7 +136,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int): tileentity.RobotProxy = { + override def newBlockEntity(world: IBlockReader): tileentity.RobotProxy = { moving.get match { case Some(robot) => new tileentity.RobotProxy(robot) case _ => new tileentity.RobotProxy() @@ -137,9 +145,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // ----------------------------------------------------------------------- // - override def getExplosionResistance(entity: Entity) = 10f - - override def getDrops(world: IBlockAccess, pos: BlockPos, state: IBlockState, fortune: Int): util.ArrayList[ItemStack] = { + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { val list = new java.util.ArrayList[ItemStack]() // Superspecial hack... usually this will not work, because Minecraft calls @@ -153,7 +159,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { // mod calls this before the block is broken *and* calls removedByPlayer // this will lead to dupes, but in some initial testing this wasn't the // case anywhere (TE autonomous activator, CC turtles). - world.getTileEntity(pos) match { + ctx.getParameter(LootParameters.BLOCK_ENTITY) match { case proxy: tileentity.RobotProxy => val robot = proxy.robot if (robot.node != null) { @@ -179,35 +185,27 @@ class RobotProxy extends RedstoneAware with traits.StateAware { private def gettingDropsForActualDrop = new Exception().getStackTrace.exists(element => getDropForRealDropCallers.contains(element.getClassName + "." + element.getMethodName)) - override def collisionRayTrace(state: IBlockState, world: World, pos: BlockPos, start: Vec3d, end: Vec3d): RayTraceResult = { - val bounds = getCollisionBoundingBox(state, world, pos) - world.getTileEntity(pos) match { - case proxy: tileentity.RobotProxy if proxy.robot.animationTicksLeft <= 0 && bounds.contains(start) => null - case _ => super.collisionRayTrace(state, world, pos, start, end) - } - } - // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - if (!player.isSneaking) { - if (!world.isRemote) { + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + if (!player.isCrouching) { + if (!world.isClientSide) { // We only send slot changes to nearby players, so if there was no slot // change since this player got into range he might have the wrong one, // so we send him the current one just in case. - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy if proxy.robot.node.network != null => PacketSender.sendRobotSelectedSlotChange(proxy.robot) - player.openGui(OpenComputers, GuiType.Robot.id, world, pos.getX, pos.getY, pos.getZ) + OpenComputers.openGui(player, GuiType.Robot.id, world, pos.getX, pos.getY, pos.getZ) case _ => } } true } else if (heldItem.isEmpty) { - if (!world.isRemote) { - world.getTileEntity(pos) match { - case proxy: tileentity.RobotProxy if !proxy.machine.isRunning && proxy.isUsableByPlayer(player) => proxy.machine.start() + if (!world.isClientSide) { + world.getBlockEntity(pos) match { + case proxy: tileentity.RobotProxy if !proxy.machine.isRunning && proxy.stillValid(player) => proxy.machine.start() case _ => } } @@ -216,49 +214,49 @@ class RobotProxy extends RedstoneAware with traits.StateAware { else false } - override def onBlockPlacedBy(world: World, pos: BlockPos, state: IBlockState, entity: EntityLivingBase, stack: ItemStack) { - super.onBlockPlacedBy(world, pos, state, entity, stack) - if (!world.isRemote) ((entity, world.getTileEntity(pos)) match { + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, entity: LivingEntity, stack: ItemStack) { + super.setPlacedBy(world, pos, state, entity, stack) + if (!world.isClientSide) ((entity, world.getBlockEntity(pos)) match { case (player: agent.Player, proxy: tileentity.RobotProxy) => Some((proxy.robot, player.agent.ownerName, player.agent.ownerUUID)) - case (player: EntityPlayer, proxy: tileentity.RobotProxy) => - Some((proxy.robot, player.getName, player.getGameProfile.getId)) + case (player: PlayerEntity, proxy: tileentity.RobotProxy) => + Some((proxy.robot, player.getName.getString, player.getGameProfile.getId)) case _ => None }) match { case Some((robot, owner, uuid)) => robot.ownerName = owner robot.ownerUUID = agent.Player.determineUUID(Option(uuid)) - robot.info.load(stack) + robot.info.loadData(stack) robot.bot.node.changeBuffer(robot.info.robotEnergy - robot.bot.node.localBuffer) robot.updateInventorySize() case _ => } } - override def removedByPlayer(state: IBlockState, world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean): Boolean = { - world.getTileEntity(pos) match { + override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = { + world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy => val robot = proxy.robot // Only allow breaking creative tier robots by allowed users. // Unlike normal robots, griefing isn't really a valid concern // here, because to get a creative robot you need creative // mode in the first place. - if (robot.isCreative && (!player.capabilities.isCreativeMode || !robot.canInteract(player.getName))) return false - if (!world.isRemote) { + if (robot.isCreative && (!player.isCreative || !robot.canInteract(player.getName.getString))) return false + if (!world.isClientSide) { if (robot.player == player) return false robot.node.remove() robot.saveComponents() InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), robot.info.createItemStack()) } robot.moveFrom.foreach(fromPos => if (world.getBlockState(fromPos).getBlock == api.Items.get(Constants.BlockName.RobotAfterimage).block) { - world.setBlockState(fromPos, net.minecraft.init.Blocks.AIR.getDefaultState, 1) + world.setBlock(fromPos, net.minecraft.block.Blocks.AIR.defaultBlockState, 1) }) case _ => } - super.removedByPlayer(state, world, pos, player, willHarvest) + super.removedByPlayer(state, world, pos, player, willHarvest, fluid) } - override def breakBlock(world: World, pos: BlockPos, state: IBlockState): Unit = + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = if (moving.get.isEmpty) - super.breakBlock(world, pos, state) + super.onRemove(state, world, pos, newState, moved) } diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 83b29c46f1..94b31ad532 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -2,94 +2,85 @@ package li.cil.oc.common.block import java.util -import _root_.net.minecraft.entity.Entity -import _root_.net.minecraft.entity.EntityLivingBase -import _root_.net.minecraft.entity.player.EntityPlayer -import _root_.net.minecraft.entity.projectile.EntityArrow -import _root_.net.minecraft.item.ItemStack -import _root_.net.minecraft.util.EnumFacing -import _root_.net.minecraft.util.EnumHand -import _root_.net.minecraft.world.{IBlockAccess, World} -import _root_.net.minecraftforge.common.property.ExtendedBlockState -import _root_.net.minecraftforge.common.property.IExtendedBlockState import li.cil.oc.Constants import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.common.GuiType import li.cil.oc.common.block.property.PropertyRotatable -import li.cil.oc.common.block.property.PropertyTile import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import li.cil.oc.util.PackedColor import li.cil.oc.util.Rarity +import li.cil.oc.util.RotationHelper import li.cil.oc.util.Tooltip -import net.minecraft.block.state.IBlockState +import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.client.Minecraft import net.minecraft.client.util.ITooltipFlag +import net.minecraft.entity.Entity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.projectile.ArrowEntity +import net.minecraft.item.ItemStack +import net.minecraft.state.StateContainer +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.{AxisAlignedBB, BlockPos} -import net.minecraftforge.fml.relauncher.{Side, SideOnly} +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.{IBlockReader, World} +import net.minecraftforge.api.distmarker.{Dist, OnlyIn} -class Screen(val tier: Int) extends RedstoneAware { - override def createBlockState() = new ExtendedBlockState(this, Array(PropertyRotatable.Pitch, PropertyRotatable.Yaw), Array(PropertyTile.Tile)) - - override def getMetaFromState(state: IBlockState): Int = (state.getValue(PropertyRotatable.Pitch).ordinal() << 2) | state.getValue(PropertyRotatable.Yaw).getHorizontalIndex - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Pitch, EnumFacing.getFront(meta >> 2)).withProperty(PropertyRotatable.Yaw, EnumFacing.getHorizontal(meta & 0x3)) - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = - (state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, tile: tileentity.Screen) => - extendedState. - withProperty(property.PropertyTile.Tile, tile). - withProperty(PropertyRotatable.Pitch, tile.pitch). - withProperty(PropertyRotatable.Yaw, tile.yaw) - case _ => state - } +import scala.collection.convert.WrapAsScala._ - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = toLocal(world, pos, side) != EnumFacing.SOUTH +class Screen(val tier: Int) extends RedstoneAware { + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Pitch, PropertyRotatable.Yaw) // ----------------------------------------------------------------------- // override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { + override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { val (w, h) = Settings.screenResolutionsByTier(tier) val depth = PackedColor.Depth.bits(Settings.screenDepthsByTier(tier)) - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase, w, h, depth)) + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, w, h, depth)) { + tooltip.add(new StringTextComponent(curr)) + } } // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Screen(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Screen(tier) // ----------------------------------------------------------------------- // - override def onBlockPlacedBy(world: World, pos: BlockPos, state: IBlockState, placer: EntityLivingBase, stack: ItemStack) { - super.onBlockPlacedBy(world, pos, state, placer, stack) - world.getTileEntity(pos) match { + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack) { + super.setPlacedBy(world, pos, state, placer, stack) + world.getBlockEntity(pos) match { case screen: tileentity.Screen => screen.delayUntilCheckForMultiBlock = 0 case _ => } } - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = rightClick(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ, force = false) + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = rightClick(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ, force = false) - def rightClick(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, - side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = { + def rightClick(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, + side: Direction, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = { if (Wrench.holdsApplicableWrench(player, pos) && getValidRotations(world, pos).contains(side) && !force) false else if (api.Items.get(heldItem) == api.Items.get(Constants.ItemName.Analyzer)) false - else world.getTileEntity(pos) match { - case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isSneaking == screen.origin.invertTouchMode) => + else world.getBlockEntity(pos) match { + case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isCrouching == screen.origin.invertTouchMode) => // Yep, this GUI is actually purely client side. We could skip this // if, but it is clearer this way (to trigger it from the server we // would have to give screens a "container", which we do not want). - if (world.isRemote) { - player.openGui(OpenComputers, GuiType.Screen.id, world, pos.getX, pos.getY, pos.getZ) + if (world.isClientSide) { + OpenComputers.openGui(player, GuiType.Screen.id, world, pos.getX, pos.getY, pos.getZ) } true case screen: tileentity.Screen if screen.tier > 0 && side == screen.facing => - if (world.isRemote && player == Minecraft.getMinecraft.player) { + if (world.isClientSide && player == Minecraft.getInstance.player) { screen.click(hitX, hitY, hitZ) } else true @@ -97,32 +88,32 @@ class Screen(val tier: Int) extends RedstoneAware { } } - override def onEntityWalk(world: World, pos: BlockPos, entity: Entity): Unit = - if (!world.isRemote) world.getTileEntity(pos) match { - case screen: tileentity.Screen if screen.tier > 0 && screen.facing == EnumFacing.UP => screen.walk(entity) - case _ => super.onEntityWalk(world, pos, entity) + override def stepOn(world: World, pos: BlockPos, entity: Entity): Unit = + if (!world.isClientSide) world.getBlockEntity(pos) match { + case screen: tileentity.Screen if screen.tier > 0 && screen.facing == Direction.UP => screen.walk(entity) + case _ => super.stepOn(world, pos, entity) } - override def onEntityCollidedWithBlock(world: World, pos: BlockPos, state: IBlockState, entity: Entity): Unit = - if (world.isRemote) (entity, world.getTileEntity(pos)) match { - case (arrow: EntityArrow, screen: tileentity.Screen) if screen.tier > 0 => - val hitX = math.max(0, math.min(1, arrow.posX - pos.getX)) - val hitY = math.max(0, math.min(1, arrow.posY - pos.getY)) - val hitZ = math.max(0, math.min(1, arrow.posZ - pos.getZ)) + override def entityInside(state: BlockState, world: World, pos: BlockPos, entity: Entity): Unit = + if (world.isClientSide) (entity, world.getBlockEntity(pos)) match { + case (arrow: ArrowEntity, screen: tileentity.Screen) if screen.tier > 0 => + val hitX = math.max(0, math.min(1, arrow.getX - pos.getX)) + val hitY = math.max(0, math.min(1, arrow.getY - pos.getY)) + val hitZ = math.max(0, math.min(1, arrow.getZ - pos.getZ)) val absX = math.abs(hitX - 0.5) val absY = math.abs(hitY - 0.5) val absZ = math.abs(hitZ - 0.5) val side = if (absX > absY && absX > absZ) { - if (hitX < 0.5) EnumFacing.WEST - else EnumFacing.EAST + if (hitX < 0.5) Direction.WEST + else Direction.EAST } else if (absY > absZ) { - if (hitY < 0.5) EnumFacing.DOWN - else EnumFacing.UP + if (hitY < 0.5) Direction.DOWN + else Direction.UP } else { - if (hitZ < 0.5) EnumFacing.NORTH - else EnumFacing.SOUTH + if (hitZ < 0.5) Direction.NORTH + else Direction.SOUTH } if (side == screen.facing) { screen.shot(arrow) @@ -133,21 +124,12 @@ class Screen(val tier: Int) extends RedstoneAware { // ----------------------------------------------------------------------- // override def getValidRotations(world: World, pos: BlockPos) = - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case screen: tileentity.Screen => - if (screen.facing == EnumFacing.UP || screen.facing == EnumFacing.DOWN) EnumFacing.values - else EnumFacing.values.filter { + if (screen.facing == Direction.UP || screen.facing == Direction.DOWN) Direction.values + else Direction.values.filter { d => d != screen.facing && d != screen.facing.getOpposite } case _ => super.getValidRotations(world, pos) } - - val emptyBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0) - - @SideOnly(Side.CLIENT) - override def getSelectedBoundingBox(state: IBlockState, worldIn: World, pos: BlockPos): AxisAlignedBB = - if (!Minecraft.getMinecraft.player.isSneaking) - emptyBB - else - super.getSelectedBoundingBox(state, worldIn, pos) } diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index d187e662a7..f115ee2a3c 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -8,107 +8,121 @@ import li.cil.oc.common.tileentity.traits.Colored import li.cil.oc.common.tileentity.traits.Inventory import li.cil.oc.common.tileentity.traits.Rotatable import li.cil.oc.util.Color +import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.Tooltip -import net.minecraft.block.BlockContainer +import net.minecraft.block.AbstractBlock.IExtendedPositionPredicate +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.BlockState +import net.minecraft.block.ContainerBlock import net.minecraft.block.material.Material -import net.minecraft.block.state.BlockFaceShape -import net.minecraft.block.state.IBlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLiving.SpawnPlacementType -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumDyeColor -import net.minecraft.item.EnumRarity +import net.minecraft.entity.EntityType +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.DyeColor +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack +import net.minecraft.item.Rarity import net.minecraft.tileentity.TileEntity -import net.minecraft.util._ +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader +import net.minecraft.world.IWorldReader import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.world.server.ServerWorld +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.ToolType -abstract class SimpleBlock(material: Material = Material.IRON) extends BlockContainer(material) { - setHardness(2f) - setResistance(5) - setCreativeTab(CreativeTab) +import scala.collection.convert.WrapAsScala._ +abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).strength(2, 5)) extends ContainerBlock(props.isValidSpawn(new IExtendedPositionPredicate[EntityType[_]] { + override def test(state: BlockState, world: IBlockReader, pos: BlockPos, entity: EntityType[_]) = state.getBlock.asInstanceOf[SimpleBlock].isValidSpawn(state, world, pos, entity) +})) { var showInItemList = true - protected val validRotations_ = Array(EnumFacing.UP, EnumFacing.DOWN) + @Deprecated + private var creativeTab: ItemGroup = CreativeTab - def createItemStack(amount: Int = 1) = new ItemStack(this, amount) + @Deprecated + def getCreativeTab = creativeTab - override def createNewTileEntity(world: World, meta: Int): TileEntity = null - - @SideOnly(Side.CLIENT) - override def shouldSideBeRendered(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = { - val bounds = getBoundingBox(state, world, pos) - (side == EnumFacing.DOWN && bounds.minY > 0) || - (side == EnumFacing.UP && bounds.maxY < 1) || - (side == EnumFacing.NORTH && bounds.minZ > 0) || - (side == EnumFacing.SOUTH && bounds.maxZ < 1) || - (side == EnumFacing.WEST && bounds.minX > 0) || - (side == EnumFacing.EAST && bounds.maxX < 1) || - isOpaqueCube(state) - } + @Deprecated + protected def setCreativeTab(tab: ItemGroup) = creativeTab = tab - // ----------------------------------------------------------------------- // - // Rendering - // ----------------------------------------------------------------------- // + @Deprecated + private var unlocalizedName = super.getDescriptionId() + + @Deprecated + private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name + + @Deprecated + override def getDescriptionId = unlocalizedName + + protected val validRotations_ = Array(Direction.UP, Direction.DOWN) - override def getRenderType(state: IBlockState): EnumBlockRenderType = EnumBlockRenderType.MODEL + def createItemStack(amount: Int = 1) = new ItemStack(this, amount) - @SideOnly(Side.CLIENT) - def preItemRender(metadata: Int) {} + override def newBlockEntity(world: IBlockReader): TileEntity = null // ----------------------------------------------------------------------- // - // ItemBlock + // BlockItem // ----------------------------------------------------------------------- // - def rarity(stack: ItemStack) = EnumRarity.COMMON + def rarity(stack: ItemStack) = Rarity.COMMON - @SideOnly(Side.CLIENT) - def addInformation(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - tooltipHead(metadata, stack, world, tooltip, flag) - tooltipBody(metadata, stack, world, tooltip, flag) - tooltipTail(metadata, stack, world, tooltip, flag) + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + tooltipHead(stack, world, tooltip, flag) + tooltipBody(stack, world, tooltip, flag) + tooltipTail(stack, world, tooltip, flag) } - protected def tooltipHead(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { + protected def tooltipHead(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { } - protected def tooltipBody(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase)) + protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { + tooltip.add(new StringTextComponent(curr)) + } } - protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { + protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { } // ----------------------------------------------------------------------- // // Rotation // ----------------------------------------------------------------------- // - def getFacing(world: IBlockAccess, pos: BlockPos): EnumFacing = - world.getTileEntity(pos) match { + def getFacing(world: IBlockReader, pos: BlockPos): Direction = + world.getBlockEntity(pos) match { case tileEntity: Rotatable => tileEntity.facing - case _ => EnumFacing.SOUTH + case _ => Direction.SOUTH } - def setFacing(world: World, pos: BlockPos, value: EnumFacing): Boolean = - world.getTileEntity(pos) match { + def setFacing(world: World, pos: BlockPos, value: Direction): Boolean = + world.getBlockEntity(pos) match { case rotatable: Rotatable => rotatable.setFromFacing(value); true case _ => false } def setRotationFromEntityPitchAndYaw(world: World, pos: BlockPos, value: Entity): Boolean = - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case rotatable: Rotatable => rotatable.setFromEntityPitchAndYaw(value); true case _ => false } - def toLocal(world: IBlockAccess, pos: BlockPos, value: EnumFacing): EnumFacing = - world.getTileEntity(pos) match { + def toLocal(world: IBlockReader, pos: BlockPos, value: Direction): Direction = + world.getBlockEntity(pos) match { case rotatable: Rotatable => rotatable.toLocal(value) case _ => value } @@ -117,64 +131,66 @@ abstract class SimpleBlock(material: Material = Material.IRON) extends BlockCont // Block // ----------------------------------------------------------------------- // - override def getBlockFaceShape(world: IBlockAccess, state: IBlockState, pos: BlockPos, side: EnumFacing): BlockFaceShape = if(isBlockSolid(world, pos, side)) BlockFaceShape.SOLID else BlockFaceShape.UNDEFINED - - def isBlockSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = world.getBlockState(pos).getMaterial.isSolid + @Deprecated + def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + val shape = super.getShape(state, world, pos, ISelectionContext.empty()) + if (shape.isEmpty) shape.bounds else new AxisAlignedBB(0, 0, 0, 1, 1, 1) + } - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = true + @Deprecated + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = + VoxelShapes.create(getBoundingBox(state, world, pos)) - override def canHarvestBlock(world: IBlockAccess, pos: BlockPos, player: EntityPlayer) = true + override def canHarvestBlock(state: BlockState, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = true - override def getHarvestTool(state: IBlockState): String = null + override def getHarvestTool(state: BlockState): ToolType = null - override def canBeReplacedByLeaves(state: IBlockState, world: IBlockAccess, pos: BlockPos): Boolean = false + override def canBeReplacedByLeaves(state: BlockState, world: IWorldReader, pos: BlockPos): Boolean = false - override def canCreatureSpawn(state: IBlockState, world: IBlockAccess, pos: BlockPos, `type`: SpawnPlacementType): Boolean = false + @Deprecated + def isValidSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, `type`: EntityType[_]): Boolean = false - override def getValidRotations(world: World, pos: BlockPos): Array[EnumFacing] = validRotations_ + def getValidRotations(world: World, pos: BlockPos): Array[Direction] = validRotations_ - override def breakBlock(world: World, pos: BlockPos, state: IBlockState): Unit = { - if (!world.isRemote) world.getTileEntity(pos) match { + @Deprecated + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { + if (!world.isClientSide) world.getBlockEntity(pos) match { case inventory: Inventory => inventory.dropAllSlots() case _ => // Ignore. } - super.breakBlock(world, pos, state) + super.onRemove(state, world, pos, newState, moved) } // ----------------------------------------------------------------------- // - override def rotateBlock(world: World, pos: BlockPos, axis: EnumFacing): Boolean = - world.getTileEntity(pos) match { + @Deprecated + def rotateBlock(world: World, pos: BlockPos, axis: Direction): Boolean = + world.getBlockEntity(pos) match { case rotatable: tileentity.traits.Rotatable if rotatable.rotate(axis) => - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3) + world.sendBlockUpdated(pos, world.getBlockState(pos), world.getBlockState(pos), 3) true case _ => false } - override def recolorBlock(world: World, pos: BlockPos, side: EnumFacing, color: EnumDyeColor): Boolean = - world.getTileEntity(pos) match { - case colored: Colored if colored.getColor != Color.rgbValues(color) => - colored.setColor(Color.rgbValues(color)) - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3) - true // Blame Vexatos. - case _ => super.recolorBlock(world, pos, side, color) - } - // ----------------------------------------------------------------------- // - override def onBlockActivated(world: World, pos: BlockPos, state: IBlockState, player: EntityPlayer, hand: EnumHand, facing: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - val heldItem = player.getHeldItem(hand) - world.getTileEntity(pos) match { + override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { + val heldItem = player.getItemInHand(hand) + world.getBlockEntity(pos) match { case colored: Colored if Color.isDye(heldItem) => colored.setColor(Color.rgbValues(Color.dyeColor(heldItem))) - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3) - if (!player.capabilities.isCreativeMode && colored.consumesDye) { - heldItem.splitStack(1) + world.sendBlockUpdated(pos, world.getBlockState(pos), world.getBlockState(pos), 3) + if (!player.isCreative && colored.consumesDye) { + heldItem.split(1) } - true - case _ => localOnBlockActivated(world, pos, player, hand, heldItem, facing, hitX, hitY, hitZ) + ActionResultType.sidedSuccess(world.isClientSide) + case _ => { + val loc = trace.getLocation + if (localOnBlockActivated(world, pos, player, hand, heldItem, trace.getDirection, loc.x.toFloat, loc.y.toFloat, loc.z.toFloat)) + ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + } } } - def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = false + def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = false } diff --git a/src/main/scala/li/cil/oc/common/block/Transposer.scala b/src/main/scala/li/cil/oc/common/block/Transposer.scala index 78f01d35e3..c830c63b61 100644 --- a/src/main/scala/li/cil/oc/common/block/Transposer.scala +++ b/src/main/scala/li/cil/oc/common/block/Transposer.scala @@ -1,16 +1,12 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity -import net.minecraft.block.state.IBlockState -import net.minecraft.util.EnumFacing +import net.minecraft.block.BlockState +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World class Transposer extends SimpleBlock { - override def isSideSolid(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = false - - // ----------------------------------------------------------------------- // - - override def createNewTileEntity(world: World, meta: Int) = new tileentity.Transposer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Transposer() } diff --git a/src/main/scala/li/cil/oc/common/block/Waypoint.scala b/src/main/scala/li/cil/oc/common/block/Waypoint.scala index 8f6cbc5bab..3d09f57c3e 100644 --- a/src/main/scala/li/cil/oc/common/block/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/block/Waypoint.scala @@ -2,55 +2,44 @@ package li.cil.oc.common.block import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType -import li.cil.oc.common.block.property.{PropertyRotatable, PropertyTile} +import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity -import net.minecraft.block.state.BlockStateContainer -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import li.cil.oc.util.RotationHelper +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.state.StateContainer +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.world.{IBlockAccess, World} -import net.minecraftforge.common.property.{ExtendedBlockState, IExtendedBlockState} +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.world.{IBlockReader, World} class Waypoint extends RedstoneAware { - override def createBlockState() = new ExtendedBlockState(this, Array(PropertyRotatable.Pitch, PropertyRotatable.Yaw), Array(PropertyTile.Tile)) - - override def getMetaFromState(state: IBlockState): Int = (state.getValue(PropertyRotatable.Pitch).ordinal() << 2) | state.getValue(PropertyRotatable.Yaw).getHorizontalIndex - - override def getStateFromMeta(meta: Int): IBlockState = getDefaultState.withProperty(PropertyRotatable.Pitch, EnumFacing.getFront(meta >> 2)).withProperty(PropertyRotatable.Yaw, EnumFacing.getHorizontal(meta & 0x3)) - - override def getExtendedState(state: IBlockState, world: IBlockAccess, pos: BlockPos): IBlockState = - (state, world.getTileEntity(pos)) match { - case (extendedState: IExtendedBlockState, tile: tileentity.Screen) => - extendedState. - withProperty(property.PropertyTile.Tile, tile). - withProperty(PropertyRotatable.Pitch, tile.pitch). - withProperty(PropertyRotatable.Yaw, tile.yaw) - case _ => state - } + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = + builder.add(PropertyRotatable.Pitch, PropertyRotatable.Yaw) // ----------------------------------------------------------------------- // - override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Waypoint() + override def newBlockEntity(world: IBlockReader) = new tileentity.Waypoint() // ----------------------------------------------------------------------- // - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - if (!player.isSneaking) { - if (world.isRemote) { - player.openGui(OpenComputers, GuiType.Waypoint.id, world, pos.getX, pos.getY, pos.getZ) + override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { + if (!player.isCrouching) { + if (world.isClientSide) { + OpenComputers.openGui(player, GuiType.Waypoint.id, world, pos.getX, pos.getY, pos.getZ) } - true + ActionResultType.sidedSuccess(world.isClientSide) } - else super.localOnBlockActivated(world, pos, player, hand, heldItem, side, hitX, hitY, hitZ) + else super.use(state, world, pos, player, hand, trace) } - override def getValidRotations(world: World, pos: BlockPos): Array[EnumFacing] = - world.getTileEntity(pos) match { + override def getValidRotations(world: World, pos: BlockPos): Array[Direction] = + world.getBlockEntity(pos) match { case waypoint: tileentity.Waypoint => - EnumFacing.values.filter { + Direction.values.filter { d => d != waypoint.facing && d != waypoint.facing.getOpposite } case _ => super.getValidRotations(world, pos) diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala index 930e337e46..92961e330e 100644 --- a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala +++ b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala @@ -2,13 +2,13 @@ package li.cil.oc.common.block.property import com.google.common.base.Predicate import com.google.common.base.Predicates -import net.minecraft.block.properties.PropertyDirection -import net.minecraft.util.EnumFacing +import net.minecraft.state.DirectionProperty +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ object PropertyRotatable { - final val Facing = PropertyDirection.create("facing", EnumFacing.Plane.HORIZONTAL.asInstanceOf[Predicate[EnumFacing]]) - final val Pitch = PropertyDirection.create("pitch", Predicates.in(Set(EnumFacing.DOWN, EnumFacing.UP, EnumFacing.NORTH))) - final val Yaw = PropertyDirection.create("yaw", EnumFacing.Plane.HORIZONTAL.asInstanceOf[Predicate[EnumFacing]]) + final val Facing = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL.asInstanceOf[Predicate[Direction]]) + final val Pitch = DirectionProperty.create("pitch", Predicates.in(Set(Direction.DOWN, Direction.UP, Direction.NORTH))) + final val Yaw = DirectionProperty.create("yaw", Direction.Plane.HORIZONTAL.asInstanceOf[Predicate[Direction]]) } diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyRunning.scala b/src/main/scala/li/cil/oc/common/block/property/PropertyRunning.scala index f9ad0a7d2e..7aa3d7622f 100644 --- a/src/main/scala/li/cil/oc/common/block/property/PropertyRunning.scala +++ b/src/main/scala/li/cil/oc/common/block/property/PropertyRunning.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.block.property -import net.minecraft.block.properties.PropertyBool +import net.minecraft.state.BooleanProperty object PropertyRunning { - final val Running = PropertyBool.create("running") + final val Running = BooleanProperty.create("running") } diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyTile.scala b/src/main/scala/li/cil/oc/common/block/property/PropertyTile.scala deleted file mode 100644 index c28a8cd095..0000000000 --- a/src/main/scala/li/cil/oc/common/block/property/PropertyTile.scala +++ /dev/null @@ -1,19 +0,0 @@ -package li.cil.oc.common.block.property - -import net.minecraft.tileentity.TileEntity -import net.minecraftforge.common.property.IUnlistedProperty - -object PropertyTile { - final val Tile = new PropertyTile() -} - -// Custom unlisted property used to pass a long tile entities to a block's renderer. -class PropertyTile extends IUnlistedProperty[TileEntity] { - override def getName = "tile" - - override def isValid(value: TileEntity) = true - - override def getType = classOf[TileEntity] - - override def valueToString(value: TileEntity) = value.toString -} diff --git a/src/main/scala/li/cil/oc/common/block/property/UnlistedInteger.scala b/src/main/scala/li/cil/oc/common/block/property/UnlistedInteger.scala deleted file mode 100644 index ed833c2ce4..0000000000 --- a/src/main/scala/li/cil/oc/common/block/property/UnlistedInteger.scala +++ /dev/null @@ -1,13 +0,0 @@ -package li.cil.oc.common.block.property - -import net.minecraftforge.common.property.IUnlistedProperty - -class UnlistedInteger(val name:String) extends IUnlistedProperty[Integer]{ - override def getName: String = name - - override def isValid(value: Integer): Boolean = value != null - - override def getType: Class[Integer] = classOf[Integer] - - override def valueToString(value: Integer): String = value.toString -} diff --git a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala index f201ba1931..c5a768bf43 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala @@ -3,13 +3,15 @@ package li.cil.oc.common.block.traits import java.util import li.cil.oc.common.block.SimpleBlock -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.BlockState +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.FluidState import net.minecraft.item.ItemStack +import net.minecraft.loot.LootContext import net.minecraft.tileentity.TileEntity import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World import scala.reflect.ClassTag @@ -17,31 +19,31 @@ import scala.reflect.ClassTag trait CustomDrops[Tile <: TileEntity] extends SimpleBlock { protected def tileTag: ClassTag[Tile] - override def getDrops(world: IBlockAccess, pos: BlockPos, state: IBlockState, fortune: Int): util.List[ItemStack] = new java.util.ArrayList[ItemStack]() + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = new util.ArrayList[ItemStack]() - override def breakBlock(world: World, pos: BlockPos, state: IBlockState): Unit = {} + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = {} - override def removedByPlayer(state: IBlockState, world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean): Boolean = { - if (!world.isRemote) { + override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = { + if (!world.isClientSide) { val matcher = tileTag - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case matcher(tileEntity) => doCustomDrops(tileEntity, player, willHarvest) case _ => } } - super.removedByPlayer(state, world, pos, player, willHarvest) + super.removedByPlayer(state, world, pos, player, willHarvest, fluid) } - override def onBlockPlacedBy(world: World, pos: BlockPos, state: IBlockState, placer: EntityLivingBase, stack: ItemStack): Unit = { - super.onBlockPlacedBy(world, pos, state, placer, stack) + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { + super.setPlacedBy(world, pos, state, placer, stack) val matcher = tileTag - world.getTileEntity(pos) match { + world.getBlockEntity(pos) match { case matcher(tileEntity) => doCustomInit(tileEntity, placer, stack) case _ => } } - protected def doCustomInit(tileEntity: Tile, player: EntityLivingBase, stack: ItemStack): Unit = {} + protected def doCustomInit(tileEntity: Tile, player: LivingEntity, stack: ItemStack): Unit = {} - protected def doCustomDrops(tileEntity: Tile, player: EntityPlayer, willHarvest: Boolean): Unit = {} + protected def doCustomDrops(tileEntity: Tile, player: PlayerEntity, willHarvest: Boolean): Unit = {} } diff --git a/src/main/scala/li/cil/oc/common/block/traits/GUI.scala b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala index ac895350ab..f78cd73cda 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/GUI.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala @@ -3,20 +3,20 @@ package li.cil.oc.common.block.traits import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType import li.cil.oc.common.block.SimpleBlock -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.world.World trait GUI extends SimpleBlock { def guiType: GuiType.EnumVal - override def localOnBlockActivated(world: World, pos: BlockPos, player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - if (!player.isSneaking) { - if (!world.isRemote) { - player.openGui(OpenComputers, guiType.id, world, pos.getX, pos.getY, pos.getZ) + override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + if (!player.isCrouching) { + if (!world.isClientSide) { + OpenComputers.openGui(player, guiType.id, world, pos.getX, pos.getY, pos.getZ) } true } diff --git a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala index a2ba010bf5..7fc5f3cc09 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala @@ -5,17 +5,23 @@ import java.util import li.cil.oc.common.block.SimpleBlock import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.world.World +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader + +import scala.collection.convert.WrapAsScala._ trait PowerAcceptor extends SimpleBlock { def energyThroughput: Double // ----------------------------------------------------------------------- // - override protected def tooltipTail(metadata: Int, stack: ItemStack, world: World, tooltip: util.List[String], advanced: ITooltipFlag) { - super.tooltipTail(metadata, stack, world, tooltip, advanced) - tooltip.addAll(Tooltip.extended("PowerAcceptor", energyThroughput.toInt)) + override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { + super.tooltipTail(stack, world, tooltip, advanced) + for (curr <- Tooltip.extended("PowerAcceptor", energyThroughput.toInt)) { + tooltip.add(new StringTextComponent(curr)) + } } } diff --git a/src/main/scala/li/cil/oc/common/block/traits/StateAware.scala b/src/main/scala/li/cil/oc/common/block/traits/StateAware.scala index a406f8760e..3ad68b5148 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/StateAware.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/StateAware.scala @@ -2,15 +2,15 @@ package li.cil.oc.common.block.traits import li.cil.oc.api import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos import net.minecraft.world.World trait StateAware extends Block { - override def hasComparatorInputOverride(state: IBlockState): Boolean = true + override def hasAnalogOutputSignal(state: BlockState): Boolean = true - override def getComparatorInputOverride(state: IBlockState, world: World, pos: BlockPos): Int = - world.getTileEntity(pos) match { + override def getAnalogOutputSignal(state: BlockState, world: World, pos: BlockPos): Int = + world.getBlockEntity(pos) match { case stateful: api.util.StateAware => if (stateful.getCurrentState.contains(api.util.StateAware.State.IsWorking)) 15 else if (stateful.getCurrentState.contains(api.util.StateAware.State.CanWork)) 10 diff --git a/src/main/scala/li/cil/oc/common/capabilities/CapabilityColored.scala b/src/main/scala/li/cil/oc/common/capabilities/CapabilityColored.scala index b186caa6c3..f133a1a781 100644 --- a/src/main/scala/li/cil/oc/common/capabilities/CapabilityColored.scala +++ b/src/main/scala/li/cil/oc/common/capabilities/CapabilityColored.scala @@ -2,25 +2,29 @@ package li.cil.oc.common.capabilities import li.cil.oc.api.internal.Colored import li.cil.oc.integration.Mods -import net.minecraft.nbt.NBTBase -import net.minecraft.nbt.NBTTagInt +import net.minecraft.nbt.INBT +import net.minecraft.nbt.IntNBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier object CapabilityColored { final val ProviderColored = new ResourceLocation(Mods.IDs.OpenComputers, "colored") - class Provider(val tileEntity: TileEntity with Colored) extends ICapabilityProvider with Colored { - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { - capability == Capabilities.ColoredCapability - } + class Provider(val tileEntity: TileEntity with Colored) extends ICapabilityProvider with NonNullSupplier[Provider] with Colored { + private val wrapper = LazyOptional.of(this) + + def get = this + + def invalidate() = wrapper.invalidate - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - if (hasCapability(capability, facing)) this.asInstanceOf[T] - else null.asInstanceOf[T] + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + if (capability == Capabilities.ColoredCapability) wrapper.cast[T] + else LazyOptional.empty[T] } override def getColor = tileEntity.getColor @@ -41,15 +45,15 @@ object CapabilityColored { } class DefaultStorage extends Capability.IStorage[Colored] { - override def writeNBT(capability: Capability[Colored], t: Colored, enumFacing: EnumFacing): NBTBase = { + override def writeNBT(capability: Capability[Colored], t: Colored, Direction: Direction): INBT = { val color = t.getColor - new NBTTagInt(color) + IntNBT.valueOf(color) } - override def readNBT(capability: Capability[Colored], t: Colored, enumFacing: EnumFacing, nbtBase: NBTBase): Unit = { + override def readNBT(capability: Capability[Colored], t: Colored, Direction: Direction, nbtBase: INBT): Unit = { nbtBase match { - case nbt: NBTTagInt => - t.setColor(nbt.getInt) + case nbt: IntNBT => + t.setColor(nbt.getAsInt) case _ => } } diff --git a/src/main/scala/li/cil/oc/common/capabilities/CapabilityEnvironment.scala b/src/main/scala/li/cil/oc/common/capabilities/CapabilityEnvironment.scala index fe5ee5992b..19b321ef0f 100644 --- a/src/main/scala/li/cil/oc/common/capabilities/CapabilityEnvironment.scala +++ b/src/main/scala/li/cil/oc/common/capabilities/CapabilityEnvironment.scala @@ -6,25 +6,29 @@ import li.cil.oc.api.network.Message import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility import li.cil.oc.integration.Mods -import net.minecraft.nbt.NBTBase -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.INBT +import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier object CapabilityEnvironment { final val ProviderEnvironment = new ResourceLocation(Mods.IDs.OpenComputers, "environment") - class Provider(val tileEntity: TileEntity with Environment) extends ICapabilityProvider with Environment { - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { - capability == Capabilities.EnvironmentCapability - } + class Provider(val tileEntity: TileEntity with Environment) extends ICapabilityProvider with NonNullSupplier[Provider] with Environment { + private val wrapper = LazyOptional.of(this) + + def get = this + + def invalidate() = wrapper.invalidate - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - if (hasCapability(capability, facing)) this.asInstanceOf[T] - else null.asInstanceOf[T] + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + if (capability == Capabilities.EnvironmentCapability) wrapper.cast[T] + else LazyOptional.empty[T] } override def node = tileEntity.node @@ -47,21 +51,21 @@ object CapabilityEnvironment { } class DefaultStorage extends Capability.IStorage[Environment] { - override def writeNBT(capability: Capability[Environment], t: Environment, facing: EnumFacing): NBTBase = { + override def writeNBT(capability: Capability[Environment], t: Environment, facing: Direction): INBT = { val node = t.node if (node != null) { - val nbt = new NBTTagCompound() - node.save(nbt) + val nbt = new CompoundNBT() + node.saveData(nbt) nbt } else null } - override def readNBT(capability: Capability[Environment], t: Environment, facing: EnumFacing, nbtBase: NBTBase): Unit = { + override def readNBT(capability: Capability[Environment], t: Environment, facing: Direction, nbtBase: INBT): Unit = { nbtBase match { - case nbt: NBTTagCompound => + case nbt: CompoundNBT => val node = t.node - if (node != null) node.load(nbt) + if (node != null) node.loadData(nbt) case _ => } } diff --git a/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedComponent.scala b/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedComponent.scala index aecb4b2e39..c0fafb4485 100644 --- a/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedComponent.scala +++ b/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedComponent.scala @@ -5,27 +5,31 @@ import li.cil.oc.api.network.SidedComponent import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.integration.Mods import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier object CapabilitySidedComponent { final val SidedComponent = new ResourceLocation(Mods.IDs.OpenComputers, "sided_component") - class Provider(val tileEntity: TileEntity with Environment with SidedComponent) extends ICapabilityProvider with SidedEnvironment { - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { - capability == Capabilities.SidedEnvironmentCapability - } + class Provider(val tileEntity: TileEntity with Environment with SidedComponent) extends ICapabilityProvider with NonNullSupplier[Provider] with SidedEnvironment { + private val wrapper = LazyOptional.of(this) + + def get = this + + def invalidate() = wrapper.invalidate - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - if (hasCapability(capability, facing)) this.asInstanceOf[T] - else null.asInstanceOf[T] + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + if (capability == Capabilities.SidedEnvironmentCapability) wrapper.cast[T] + else LazyOptional.empty[T] } - override def sidedNode(side: EnumFacing) = if (tileEntity.canConnectNode(side)) tileEntity.node else null + override def sidedNode(side: Direction) = if (tileEntity.canConnectNode(side)) tileEntity.node else null - override def canConnect(side: EnumFacing) = tileEntity.canConnectNode(side) + override def canConnect(side: Direction) = tileEntity.canConnectNode(side) } } diff --git a/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedEnvironment.scala b/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedEnvironment.scala index f2e619135c..4f42147374 100644 --- a/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedEnvironment.scala +++ b/src/main/scala/li/cil/oc/common/capabilities/CapabilitySidedEnvironment.scala @@ -3,41 +3,45 @@ package li.cil.oc.common.capabilities import li.cil.oc.api.network.Node import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.integration.Mods -import net.minecraft.nbt.NBTBase +import net.minecraft.nbt.INBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier object CapabilitySidedEnvironment { final val ProviderSidedEnvironment = new ResourceLocation(Mods.IDs.OpenComputers, "sided_environment") - class Provider(val tileEntity: TileEntity with SidedEnvironment) extends ICapabilityProvider with SidedEnvironment { - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { - capability == Capabilities.SidedEnvironmentCapability - } + class Provider(val tileEntity: TileEntity with SidedEnvironment) extends ICapabilityProvider with NonNullSupplier[Provider] with SidedEnvironment { + private val wrapper = LazyOptional.of(this) + + def get = this + + def invalidate() = wrapper.invalidate - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - if (hasCapability(capability, facing)) this.asInstanceOf[T] - else null.asInstanceOf[T] + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + if (capability == Capabilities.SidedEnvironmentCapability) wrapper.cast[T] + else LazyOptional.empty[T] } - override def sidedNode(side: EnumFacing) = tileEntity.sidedNode(side) + override def sidedNode(side: Direction) = tileEntity.sidedNode(side) - override def canConnect(side: EnumFacing) = tileEntity.canConnect(side) + override def canConnect(side: Direction) = tileEntity.canConnect(side) } class DefaultImpl extends SidedEnvironment { - override def sidedNode(side: EnumFacing): Node = null + override def sidedNode(side: Direction): Node = null - override def canConnect(side: EnumFacing): Boolean = false + override def canConnect(side: Direction): Boolean = false } class DefaultStorage extends Capability.IStorage[SidedEnvironment] { - override def writeNBT(capability: Capability[SidedEnvironment], t: SidedEnvironment, enumFacing: EnumFacing): NBTBase = null + override def writeNBT(capability: Capability[SidedEnvironment], t: SidedEnvironment, Direction: Direction): INBT = null - override def readNBT(capability: Capability[SidedEnvironment], t: SidedEnvironment, enumFacing: EnumFacing, nbtBase: NBTBase): Unit = {} + override def readNBT(capability: Capability[SidedEnvironment], t: SidedEnvironment, Direction: Direction, nbtBase: INBT): Unit = {} } } diff --git a/src/main/scala/li/cil/oc/common/command/SimpleCommand.scala b/src/main/scala/li/cil/oc/common/command/SimpleCommand.scala deleted file mode 100644 index f36204e0de..0000000000 --- a/src/main/scala/li/cil/oc/common/command/SimpleCommand.scala +++ /dev/null @@ -1,23 +0,0 @@ -package li.cil.oc.common.command - -import java.util - -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.server.MinecraftServer -import net.minecraftforge.fml.common.FMLCommonHandler - -import scala.collection.convert.WrapAsJava._ -import scala.collection.mutable - -abstract class SimpleCommand(val name: String) extends CommandBase { - protected var aliases = mutable.ListBuffer.empty[String] - - override def getName = name - - override def getAliases: util.List[String] = aliases - - override def checkPermission(server: MinecraftServer, sender: ICommandSender): Boolean = super.checkPermission(server, sender)|| (FMLCommonHandler.instance().getMinecraftServerInstance != null && FMLCommonHandler.instance().getMinecraftServerInstance.isSinglePlayer) - - override def isUsernameIndex(command: Array[String], i: Int) = false -} diff --git a/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala b/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala index 2d9f8f4dfe..53a6c5af46 100644 --- a/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala @@ -3,12 +3,15 @@ package li.cil.oc.common.component import java.io.InvalidObjectException import java.security.InvalidParameterException +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.api.network.{Environment, Message, Node} -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT import li.cil.oc.api.internal.TextBuffer.ColorDepth import li.cil.oc.api import li.cil.oc.common.component.traits.{TextBufferProxy, VideoRamDevice, VideoRamRasterizer} +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class GpuTextBuffer(val owner: String, val id: Int, val data: li.cil.oc.util.TextBuffer) extends traits.TextBufferProxy { @@ -30,14 +33,14 @@ class GpuTextBuffer(val owner: String, val id: Int, val data: li.cil.oc.util.Tex override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int): Unit = dirty = true override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char): Unit = dirty = true - override def load(nbt: NBTTagCompound): Unit = { + override def loadData(nbt: CompoundNBT): Unit = { // the data is initially dirty because other devices don't know about it yet - data.load(nbt) + data.loadData(nbt) dirty = true } - override def save(nbt: NBTTagCompound): Unit = { - data.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = { + data.saveData(nbt) dirty = false } @@ -52,18 +55,19 @@ class GpuTextBuffer(val owner: String, val id: Int, val data: li.cil.oc.util.Tex override def setViewport(width: Int, height: Int): Boolean = false override def setMaximumColorDepth(depth: ColorDepth): Unit = {} override def getMaximumColorDepth: ColorDepth = data.format.depth - override def renderText: Boolean = false + @OnlyIn(Dist.CLIENT) + override def renderText(stack: MatrixStack): Boolean = false override def renderWidth: Int = 0 override def renderHeight: Int = 0 override def setRenderingEnabled(enabled: Boolean): Unit = {} override def isRenderingEnabled: Boolean = false - override def keyDown(character: Char, code: Int, player: EntityPlayer): Unit = {} - override def keyUp(character: Char, code: Int, player: EntityPlayer): Unit = {} - override def clipboard(value: String, player: EntityPlayer): Unit = {} - override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = {} - override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = {} - override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = {} - override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit = {} + override def keyDown(character: Char, code: Int, player: PlayerEntity): Unit = {} + override def keyUp(character: Char, code: Int, player: PlayerEntity): Unit = {} + override def clipboard(value: String, player: PlayerEntity): Unit = {} + override def mouseDown(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = {} + override def mouseDrag(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = {} + override def mouseUp(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = {} + override def mouseScroll(x: Double, y: Double, delta: Int, player: PlayerEntity): Unit = {} override def canUpdate: Boolean = false override def update(): Unit = {} override def onConnect(node: Node): Unit = {} @@ -91,7 +95,7 @@ object ClientGpuTextBufferHandler { } } - def loadBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int, nbt: NBTTagCompound): Boolean = { + def loadBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int, nbt: CompoundNBT): Boolean = { buffer match { case screen: VideoRamRasterizer => screen.loadBuffer(owner, id, nbt) case _ => false // ignore, not compatible with bitblts diff --git a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala index 4832c5d136..0bf50876dc 100644 --- a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala +++ b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala @@ -25,12 +25,12 @@ import li.cil.oc.common.Tier import li.cil.oc.common.item import li.cil.oc.common.item.Delegator import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagString -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.StringNBT +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.WrapAsScala._ @@ -53,10 +53,10 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ val keyboardItem = api.Items.get(Constants.BlockName.Keyboard).createItemStack(1) val keyboard = api.Driver.driverFor(keyboardItem, getClass).createEnvironment(keyboardItem, this).asInstanceOf[api.internal.Keyboard] keyboard.setUsableOverride(new UsabilityChecker { - override def isUsableByPlayer(keyboard: api.internal.Keyboard, player: EntityPlayer) = { - val stack = player.getHeldItemMainhand + override def isUsableByPlayer(keyboard: api.internal.Keyboard, player: PlayerEntity) = { + val stack = player.getItemInHand(Hand.MAIN_HAND) Delegator.subItem(stack) match { - case Some(t: item.Terminal) if stack.hasTagCompound => sidedKeys.contains(stack.getTagCompound.getString(Settings.namespace + "key")) + case Some(t: item.Terminal) if stack.hasTag => sidedKeys.contains(stack.getTag.getString(Settings.namespace + "key")) case _ => false } } @@ -71,7 +71,7 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ if (rack != null) { val data = rack.getMountableData(slot) if (data != null) { - return data.hasKey("terminalAddress") + return data.contains("terminalAddress") } } false @@ -80,8 +80,8 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ def address: String = rack.getMountableData(slot).getString("terminalAddress") def sidedKeys = { - if (!rack.world.isRemote) keys - else rack.getMountableData(slot).getTagList("keys", NBT.TAG_STRING).map((tag: NBTTagString) => tag.getString) + if (!rack.world.isClientSide) keys + else rack.getMountableData(slot).getList("keys", NBT.TAG_STRING).map((tag: StringNBT) => tag.getAsString) } // ----------------------------------------------------------------------- // @@ -133,12 +133,12 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ // ----------------------------------------------------------------------- // // RackMountable - override def getData: NBTTagCompound = { + override def getData: CompoundNBT = { if (node.address == null) api.Network.joinNewNetwork(node) - val nbt = new NBTTagCompound() + val nbt = new CompoundNBT() nbt.setNewTagList("keys", keys) - nbt.setString("terminalAddress", node.address) + nbt.putString("terminalAddress", node.address) nbt } @@ -146,25 +146,20 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ override def getConnectableAt(index: Int): RackBusConnectable = null - override def onActivate(player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { + override def onActivate(player: PlayerEntity, hand: Hand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { if (api.Items.get(heldItem) == api.Items.get(Constants.ItemName.Terminal)) { - if (!world.isRemote) { + if (!world.isClientSide) { val key = UUID.randomUUID().toString - if (!heldItem.hasTagCompound) { - heldItem.setTagCompound(new NBTTagCompound()) - } - else { - keys -= heldItem.getTagCompound.getString(Settings.namespace + "key") - } + keys -= heldItem.getOrCreateTag.getString(Settings.namespace + "key") val maxSize = Settings.get.terminalsPerServer while (keys.length >= maxSize) { keys.remove(0) } keys += key - heldItem.getTagCompound.setString(Settings.namespace + "key", key) - heldItem.getTagCompound.setString(Settings.namespace + "server", node.address) + heldItem.getTag.putString(Settings.namespace + "key", key) + heldItem.getTag.putString(Settings.namespace + "server", node.address) rack.markChanged(slot) - player.inventory.markDirty() + player.inventory.setChanged() } true } @@ -178,20 +173,20 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ private final val KeyboardTag = Settings.namespace + "keyboard" private final val KeysTag = Settings.namespace + "keys" - override def load(nbt: NBTTagCompound): Unit = { - if (!rack.world.isRemote) { - node.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = { + if (!rack.world.isClientSide) { + node.loadData(nbt) } - buffer.load(nbt.getCompoundTag(BufferTag)) - keyboard.load(nbt.getCompoundTag(KeyboardTag)) + buffer.loadData(nbt.getCompound(BufferTag)) + keyboard.loadData(nbt.getCompound(KeyboardTag)) keys.clear() - nbt.getTagList(KeysTag, NBT.TAG_STRING).foreach((tag: NBTTagString) => keys += tag.getString) + nbt.getList(KeysTag, NBT.TAG_STRING).foreach((tag: StringNBT) => keys += tag.getAsString) } - override def save(nbt: NBTTagCompound): Unit = { - node.save(nbt) - nbt.setNewCompoundTag(BufferTag, buffer.save) - nbt.setNewCompoundTag(KeyboardTag, keyboard.save) + override def saveData(nbt: CompoundNBT): Unit = { + node.saveData(nbt) + nbt.setNewCompoundTag(BufferTag, buffer.saveData) + nbt.setNewCompoundTag(KeyboardTag, keyboard.saveData) nbt.setNewTagList(KeysTag, keys) } @@ -201,7 +196,7 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ override def canUpdate: Boolean = true override def update(): Unit = { - if (world.isRemote || (node.address != null && node.network != null)) { + if (world.isClientSide || (node.address != null && node.network != null)) { buffer.update() } } @@ -216,12 +211,12 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ // ----------------------------------------------------------------------- // // Analyzable - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(buffer.node, keyboard.node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(buffer.node, keyboard.node) // ----------------------------------------------------------------------- // // LifeCycle - override def onLifecycleStateChange(state: Lifecycle.LifecycleState): Unit = if (rack.world.isRemote) state match { + override def onLifecycleStateChange(state: Lifecycle.LifecycleState): Unit = if (rack.world.isClientSide) state match { case Lifecycle.LifecycleState.Initialized => TerminalServer.loaded.add(this) case Lifecycle.LifecycleState.Disposed => diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index 51cb8c83d8..a7ea84b2d5 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.component import com.google.common.base.Strings +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.Constants import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass @@ -31,14 +32,14 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.PackedColor import li.cil.oc.util.SideTracker import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumHand +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Hand import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -125,7 +126,7 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w override def update() { super.update() - if (isDisplaying && host.world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (isDisplaying && host.world.getGameTime % Settings.get.tickFrequency == 0) { if (relativeLitArea < 0) { // The relative lit area is the number of pixels that are not blank // versus the number of pixels in the *current* resolution. This is @@ -361,43 +362,43 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w proxy.onBufferRawSetForeground(col, row, color) } - @SideOnly(Side.CLIENT) - override def renderText: Boolean = relativeLitArea != 0 && proxy.render() + @OnlyIn(Dist.CLIENT) + override def renderText(stack: MatrixStack): Boolean = relativeLitArea != 0 && proxy.render(stack) - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def renderWidth: Int = TextBufferRenderCache.renderer.charRenderWidth * getViewportWidth - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def renderHeight: Int = TextBufferRenderCache.renderer.charRenderHeight * getViewportHeight - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def setRenderingEnabled(enabled: Boolean): Unit = isRendering = enabled - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def isRenderingEnabled: Boolean = isRendering - override def keyDown(character: Char, code: Int, player: EntityPlayer): Unit = + override def keyDown(character: Char, code: Int, player: PlayerEntity): Unit = proxy.keyDown(character, code, player) - override def keyUp(character: Char, code: Int, player: EntityPlayer): Unit = + override def keyUp(character: Char, code: Int, player: PlayerEntity): Unit = proxy.keyUp(character, code, player) - override def clipboard(value: String, player: EntityPlayer): Unit = + override def clipboard(value: String, player: PlayerEntity): Unit = proxy.clipboard(value, player) - override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = + override def mouseDown(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = proxy.mouseDown(x, y, button, player) - override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = + override def mouseDrag(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = proxy.mouseDrag(x, y, button, player) - override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit = + override def mouseUp(x: Double, y: Double, button: Int, player: PlayerEntity): Unit = proxy.mouseUp(x, y, button, player) - override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit = + override def mouseScroll(x: Double, y: Double, delta: Int, player: PlayerEntity): Unit = proxy.mouseScroll(x, y, delta, player) - def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = { + def copyToAnalyzer(line: Int, player: PlayerEntity): Unit = { proxy.copyToAnalyzer(line, player) } @@ -428,38 +429,38 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w private final val ViewportWidthTag = Settings.namespace + "viewportWidth" private final val ViewportHeightTag = Settings.namespace + "viewportHeight" - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) if (SideTracker.isClient) { if (!Strings.isNullOrEmpty(proxy.nodeAddress)) return // Only load once. - proxy.nodeAddress = nbt.getCompoundTag(NodeData.NodeTag).getString(NodeData.AddressTag) + proxy.nodeAddress = nbt.getCompound(NodeData.NodeTag).getString(NodeData.AddressTag) TextBuffer.registerClientBuffer(this) } else { - if (nbt.hasKey(NodeData.BufferTag)) { - data.load(nbt.getCompoundTag(NodeData.BufferTag)) + if (nbt.contains(NodeData.BufferTag)) { + data.loadData(nbt.getCompound(NodeData.BufferTag)) } else if (!Strings.isNullOrEmpty(node.address)) { - data.load(SaveHandler.loadNBT(nbt, bufferPath)) + data.loadData(SaveHandler.loadNBT(nbt, bufferPath)) } } - if (nbt.hasKey(IsOnTag)) { + if (nbt.contains(IsOnTag)) { isDisplaying = nbt.getBoolean(IsOnTag) } - if (nbt.hasKey(HasPowerTag)) { + if (nbt.contains(HasPowerTag)) { hasPower = nbt.getBoolean(HasPowerTag) } - if (nbt.hasKey(MaxWidthTag) && nbt.hasKey(MaxHeightTag)) { - val maxWidth = nbt.getInteger(MaxWidthTag) - val maxHeight = nbt.getInteger(MaxHeightTag) + if (nbt.contains(MaxWidthTag) && nbt.contains(MaxHeightTag)) { + val maxWidth = nbt.getInt(MaxWidthTag) + val maxHeight = nbt.getInt(MaxHeightTag) maxResolution = (maxWidth, maxHeight) } precisionMode = nbt.getBoolean(PreciseTag) - if (nbt.hasKey(ViewportWidthTag)) { - val vpw = nbt.getInteger(ViewportWidthTag) - val vph = nbt.getInteger(ViewportHeightTag) + if (nbt.contains(ViewportWidthTag)) { + val vpw = nbt.getInt(ViewportWidthTag) + val vph = nbt.getInt(ViewportHeightTag) viewport = (vpw min data.width max 1, vph min data.height max 1) } else { viewport = data.size @@ -467,8 +468,8 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w } // Null check for Waila (and other mods that may call this client side). - override def save(nbt: NBTTagCompound): Unit = if (node != null) { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = if (node != null) { + super.saveData(nbt) // Happy thread synchronization hack! Here's the problem: GPUs allow direct // calls for modifying screens to give a more responsive experience. This // causes the following problem: when saving, if the screen is saved first, @@ -486,14 +487,14 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w } } - SaveHandler.scheduleSave(host, nbt, bufferPath, data.save _) - nbt.setBoolean(IsOnTag, isDisplaying) - nbt.setBoolean(HasPowerTag, hasPower) - nbt.setInteger(MaxWidthTag, maxResolution._1) - nbt.setInteger(MaxHeightTag, maxResolution._2) - nbt.setBoolean(PreciseTag, precisionMode) - nbt.setInteger(ViewportWidthTag, viewport._1) - nbt.setInteger(ViewportHeightTag, viewport._2) + SaveHandler.scheduleSave(host, nbt, bufferPath, data.saveData _) + nbt.putBoolean(IsOnTag, isDisplaying) + nbt.putBoolean(HasPowerTag, hasPower) + nbt.putInt(MaxWidthTag, maxResolution._1) + nbt.putInt(MaxHeightTag, maxResolution._2) + nbt.putBoolean(PreciseTag, precisionMode) + nbt.putInt(ViewportWidthTag, viewport._1) + nbt.putInt(ViewportHeightTag, viewport._2) } } @@ -501,11 +502,12 @@ object TextBuffer { var clientBuffers = mutable.ListBuffer.empty[TextBuffer] @SubscribeEvent - def onChunkUnload(e: ChunkEvent.Unload) { + def onChunkUnloaded(e: ChunkEvent.Unload) { val chunk = e.getChunk clientBuffers = clientBuffers.filter(t => { val blockPos = BlockPosition(t.host) - val keep = t.host.world != e.getWorld || !chunk.isAtLocation(blockPos.x >> 4, blockPos.z >> 4) + val chunkPos = chunk.getPos + val keep = t.host.world != e.getWorld || ((blockPos.x >> 4) != chunkPos.x || (blockPos.z >> 4) != chunkPos.z) if (!keep) { ClientComponentTracker.remove(t.host.world, t) } @@ -537,11 +539,12 @@ object TextBuffer { var nodeAddress = "" - def markDirty() { + def setChanged() { dirty = true } - def render() = false + @OnlyIn(Dist.CLIENT) + def render(stack: MatrixStack) = false def onBufferColorChange(): Unit @@ -596,21 +599,21 @@ object TextBuffer { owner.relativeLitArea = -1 } - def keyDown(character: Char, code: Int, player: EntityPlayer): Unit + def keyDown(character: Char, code: Int, player: PlayerEntity): Unit - def keyUp(character: Char, code: Int, player: EntityPlayer): Unit + def keyUp(character: Char, code: Int, player: PlayerEntity): Unit - def clipboard(value: String, player: EntityPlayer): Unit + def clipboard(value: String, player: PlayerEntity): Unit - def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer): Unit + def mouseDown(x: Double, y: Double, button: Int, player: PlayerEntity): Unit - def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer): Unit + def mouseDrag(x: Double, y: Double, button: Int, player: PlayerEntity): Unit - def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit + def mouseUp(x: Double, y: Double, button: Int, player: PlayerEntity): Unit - def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit + def mouseScroll(x: Double, y: Double, delta: Int, player: PlayerEntity): Unit - def copyToAnalyzer(line: Int, player: EntityPlayer): Unit + def copyToAnalyzer(line: Int, player: PlayerEntity): Unit } class ClientProxy(val owner: TextBuffer) extends Proxy { @@ -624,52 +627,53 @@ object TextBuffer { override def viewport: (Int, Int) = owner.viewport } - override def render() = { + @OnlyIn(Dist.CLIENT) + override def render(stack: MatrixStack) = { val wasDirty = dirty - TextBufferRenderCache.render(renderer) + TextBufferRenderCache.render(stack.asInstanceOf[MatrixStack], renderer) wasDirty } override def onBufferColorChange() { - markDirty() + setChanged() } override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { super.onBufferCopy(col, row, w, h, tx, ty) - markDirty() + setChanged() } override def onBufferDepthChange(depth: api.internal.TextBuffer.ColorDepth) { - markDirty() + setChanged() } override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char) { super.onBufferFill(col, row, w, h, c) - markDirty() + setChanged() } override def onBufferPaletteChange(index: Int) { - markDirty() + setChanged() } override def onBufferResolutionChange(w: Int, h: Int) { super.onBufferResolutionChange(w, h) - markDirty() + setChanged() } override def onBufferViewportResolutionChange(w: Int, h: Int) { super.onBufferViewportResolutionChange(w, h) - markDirty() + setChanged() } override def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean) { super.onBufferSet(col, row, s, vertical) - markDirty() + setChanged() } override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = { super.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow) - markDirty() + setChanged() } override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { @@ -680,49 +684,49 @@ object TextBuffer { super.onBufferRamDestroy(ram) } - override def keyDown(character: Char, code: Int, player: EntityPlayer) { + override def keyDown(character: Char, code: Int, player: PlayerEntity) { debug(s"{type = keyDown, char = $character, code = $code}") ClientPacketSender.sendKeyDown(nodeAddress, character, code) } - override def keyUp(character: Char, code: Int, player: EntityPlayer) { + override def keyUp(character: Char, code: Int, player: PlayerEntity) { debug(s"{type = keyUp, char = $character, code = $code}") ClientPacketSender.sendKeyUp(nodeAddress, character, code) } - override def clipboard(value: String, player: EntityPlayer) { + override def clipboard(value: String, player: PlayerEntity) { debug(s"{type = clipboard}") ClientPacketSender.sendClipboard(nodeAddress, value) } - override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseDown(x: Double, y: Double, button: Int, player: PlayerEntity) { debug(s"{type = mouseDown, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = false, button) } - override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseDrag(x: Double, y: Double, button: Int, player: PlayerEntity) { debug(s"{type = mouseDrag, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = true, button) } - override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseUp(x: Double, y: Double, button: Int, player: PlayerEntity) { debug(s"{type = mouseUp, x = $x, y = $y, button = $button}") ClientPacketSender.sendMouseUp(nodeAddress, x, y, button) } - override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) { + override def mouseScroll(x: Double, y: Double, delta: Int, player: PlayerEntity) { debug(s"{type = mouseScroll, x = $x, y = $y, delta = $delta}") ClientPacketSender.sendMouseScroll(nodeAddress, x, y, delta) } - override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = { + override def copyToAnalyzer(line: Int, player: PlayerEntity): Unit = { ClientPacketSender.sendCopyToAnalyzer(nodeAddress, line) } private lazy val Debugger = api.Items.get(Constants.ItemName.Debugger) private def debug(message: String) { - if (Minecraft.getMinecraft != null && Minecraft.getMinecraft.player != null && api.Items.get(Minecraft.getMinecraft.player.getHeldItemMainhand) == Debugger) { + if (Minecraft.getInstance != null && Minecraft.getInstance.player != null && api.Items.get(Minecraft.getInstance.player.getItemInHand(Hand.MAIN_HAND)) == Debugger) { OpenComputers.log.info(s"[NETWORK DEBUGGER] Sending packet to node $nodeAddress: " + message) } } @@ -791,8 +795,8 @@ object TextBuffer { override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { super.onBufferRamInit(ram) owner.host.markChanged() - val nbt = new NBTTagCompound() - ram.save(nbt) + val nbt = new CompoundNBT() + ram.saveData(nbt) owner.synchronized(ServerPacketSender.appendTextBufferRamInit(owner.pendingCommands, ram.owner, ram.id, nbt)) } @@ -820,56 +824,49 @@ object TextBuffer { owner.synchronized(ServerPacketSender.appendTextBufferRawSetForeground(owner.pendingCommands, col, row, color)) } - override def keyDown(character: Char, code: Int, player: EntityPlayer) { + override def keyDown(character: Char, code: Int, player: PlayerEntity) { sendToKeyboards("keyboard.keyDown", player, Char.box(character), Int.box(code)) } - override def keyUp(character: Char, code: Int, player: EntityPlayer) { + override def keyUp(character: Char, code: Int, player: PlayerEntity) { sendToKeyboards("keyboard.keyUp", player, Char.box(character), Int.box(code)) } - override def clipboard(value: String, player: EntityPlayer) { + override def clipboard(value: String, player: PlayerEntity) { sendToKeyboards("keyboard.clipboard", player, value) } - override def mouseDown(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseDown(x: Double, y: Double, button: Int, player: PlayerEntity) { sendMouseEvent(player, "touch", x, y, button) } - override def mouseDrag(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseDrag(x: Double, y: Double, button: Int, player: PlayerEntity) { sendMouseEvent(player, "drag", x, y, button) } - override def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer) { + override def mouseUp(x: Double, y: Double, button: Int, player: PlayerEntity) { sendMouseEvent(player, "drop", x, y, button) } - override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) { + override def mouseScroll(x: Double, y: Double, delta: Int, player: PlayerEntity) { sendMouseEvent(player, "scroll", x, y, delta) } - override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = { - val stack = player.getHeldItem(EnumHand.MAIN_HAND) + override def copyToAnalyzer(line: Int, player: PlayerEntity): Unit = { + val stack = player.getItemInHand(Hand.MAIN_HAND) if (!stack.isEmpty) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - stack.getTagCompound.removeTag(Settings.namespace + "clipboard") + stack.removeTagKey(Settings.namespace + "clipboard") if (line >= 0 && line < owner.getViewportHeight) { val text = new String(owner.data.buffer(line)).trim if (!Strings.isNullOrEmpty(text)) { - stack.getTagCompound.setString(Settings.namespace + "clipboard", text) + stack.getOrCreateTag.putString(Settings.namespace + "clipboard", text) } } - - if (stack.getTagCompound.hasNoTags) { - stack.setTagCompound(null) - } } } - private def sendMouseEvent(player: EntityPlayer, name: String, x: Double, y: Double, data: Int) = { + private def sendMouseEvent(player: PlayerEntity, name: String, x: Double, y: Double, data: Int) = { val args = mutable.ArrayBuffer.empty[AnyRef] args += player diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala index 7bd2b41151..4d75c0b2bd 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.component.traits import li.cil.oc.common.component -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable trait VideoRamDevice { @@ -37,9 +37,9 @@ trait VideoRamDevice { def removeAllBuffers(): Int = removeBuffers(bufferIndexes()) - def loadBuffer(address: String, id: Int, nbt: NBTTagCompound): Unit = { + def loadBuffer(address: String, id: Int, nbt: CompoundNBT): Unit = { val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat) - src.load(nbt) + src.loadData(nbt) addBuffer(component.GpuTextBuffer.wrap(address, id, src)) } diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala index 1aa3910cce..94d9202d47 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala @@ -2,8 +2,7 @@ package li.cil.oc.common.component.traits import li.cil.oc.common.component import li.cil.oc.common.component.GpuTextBuffer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.village.VillageDoorInfo +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable @@ -67,9 +66,9 @@ trait VideoRamRasterizer { count } - def loadBuffer(owner: String, id: Int, nbt: NBTTagCompound): Boolean = { + def loadBuffer(owner: String, id: Int, nbt: CompoundNBT): Boolean = { val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat) - src.load(nbt) + src.loadData(nbt) addBuffer(component.GpuTextBuffer.wrap(owner, id, src)) } diff --git a/src/main/scala/li/cil/oc/common/container/Adapter.scala b/src/main/scala/li/cil/oc/common/container/Adapter.scala index c3d93ddf68..bc4ab2200b 100644 --- a/src/main/scala/li/cil/oc/common/container/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/container/Adapter.scala @@ -2,9 +2,9 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Adapter(playerInventory: InventoryPlayer, adapter: tileentity.Adapter) extends Player(playerInventory, adapter) { +class Adapter(id: Int, playerInventory: PlayerInventory, adapter: tileentity.Adapter) extends Player(null, id, playerInventory, adapter) { addSlotToContainer(80, 35, Slot.Upgrade) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index 8f843acb59..5d18366cf6 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -5,25 +5,25 @@ import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Assembler) extends Player(playerInventory, assembler) { +class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) extends Player(null, id, playerInventory, assembler) { // Computer case. { - val index = inventorySlots.size - addSlotToContainer(new StaticComponentSlot(this, otherInventory, index, 12, 12, "template", common.Tier.Any) { - @SideOnly(Side.CLIENT) override - def isEnabled = !isAssembling && super.isEnabled + val index = slots.size + addSlot(new StaticComponentSlot(this, otherInventory, index, 12, 12, "template", common.Tier.Any) { + @OnlyIn(Dist.CLIENT) override + def isActive = !isAssembling && super.isActive override def getBackgroundLocation = if (isAssembling) Textures.Icons.get(common.Tier.None) else super.getBackgroundLocation }) } private def slotInfo(slot: DynamicComponentSlot) = { - AssemblerTemplates.select(getSlot(0).getStack) match { + AssemblerTemplates.select(getSlot(0).getItem) match { case Some(template) => val index = slot.getSlotIndex val tplSlot = @@ -71,12 +71,12 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse def assemblyProgress = synchronizedData.getDouble("assemblyProgress") - def assemblyRemainingTime = synchronizedData.getInteger("assemblyRemainingTime") + def assemblyRemainingTime = synchronizedData.getInt("assemblyRemainingTime") - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { - synchronizedData.setBoolean("isAssembling", assembler.isAssembling) - synchronizedData.setDouble("assemblyProgress", assembler.progress) - synchronizedData.setInteger("assemblyRemainingTime", assembler.timeRemaining) + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { + synchronizedData.putBoolean("isAssembling", assembler.isAssembling) + synchronizedData.putDouble("assemblyProgress", assembler.progress) + synchronizedData.putInt("assemblyRemainingTime", assembler.timeRemaining) super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Case.scala b/src/main/scala/li/cil/oc/common/container/Case.scala index 021324c215..9520252a1e 100644 --- a/src/main/scala/li/cil/oc/common/container/Case.scala +++ b/src/main/scala/li/cil/oc/common/container/Case.scala @@ -3,48 +3,49 @@ package li.cil.oc.common.container import li.cil.oc.common.InventorySlots import li.cil.oc.common.Tier import li.cil.oc.common.tileentity -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Case(playerInventory: InventoryPlayer, computer: tileentity.Case) extends Player(playerInventory, computer) { +class Case(id: Int, playerInventory: PlayerInventory, computer: tileentity.Case) extends Player(null, id, playerInventory, computer) { for (i <- 0 to (if (computer.tier >= Tier.Three) 2 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(98, 16 + i * slotSize, slot.slot, slot.tier) } for (i <- 0 to (if (computer.tier == Tier.One) 0 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(120, 16 + (i + 1) * slotSize, slot.slot, slot.tier) } for (i <- 0 to (if (computer.tier == Tier.One) 0 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(142, 16 + i * slotSize, slot.slot, slot.tier) } if (computer.tier >= Tier.Three) { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(142, 16 + 2 * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(120, 16, slot.slot, slot.tier) } if (computer.tier == Tier.One) { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(120, 16 + 2 * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.computer(computer.tier)(getInventory.size) + val slot = InventorySlots.computer(computer.tier)(getItems.size) addSlotToContainer(48, 34, slot.slot, slot.tier) } // Show the player's inventory. addPlayerInventorySlots(8, 84) - override def canInteractWith(player: EntityPlayer) = - super.canInteractWith(player) && computer.canInteract(player.getName) + override def stillValid(player: PlayerEntity) = + super.stillValid(player) && computer.canInteract(player.getName.getString) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/container/Charger.scala b/src/main/scala/li/cil/oc/common/container/Charger.scala index 0448e3ee40..4adf029aa1 100644 --- a/src/main/scala/li/cil/oc/common/container/Charger.scala +++ b/src/main/scala/li/cil/oc/common/container/Charger.scala @@ -1,9 +1,9 @@ package li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Charger(playerInventory: InventoryPlayer, charger: tileentity.Charger) extends Player(playerInventory, charger) { +class Charger(id: Int, playerInventory: PlayerInventory, charger: tileentity.Charger) extends Player(null, id, playerInventory, charger) { addSlotToContainer(80, 35, "tablet") addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index 67de747f07..a1811e24e0 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -1,18 +1,18 @@ package li.cil.oc.common.container import li.cil.oc.common -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory -import net.minecraft.inventory.Slot +import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsScala._ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) extends Slot(inventory, index, x, y) { - def container: Player + def agentContainer: Player def slot: String @@ -24,38 +24,42 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) // ----------------------------------------------------------------------- // - def hasBackground = backgroundLocation != null + @OnlyIn(Dist.CLIENT) + def hasBackground = getBackgroundLocation != null - @SideOnly(Side.CLIENT) - override def isEnabled = slot != common.Slot.None && tier != common.Tier.None && super.isEnabled + @OnlyIn(Dist.CLIENT) + def getBackgroundLocation: ResourceLocation = null - override def isItemValid(stack: ItemStack) = inventory.isItemValidForSlot(getSlotIndex, stack) + @OnlyIn(Dist.CLIENT) + override def isActive = slot != common.Slot.None && tier != common.Tier.None && super.isActive - override def onTake(player: EntityPlayer, stack: ItemStack) = { - for (slot <- container.inventorySlots) slot match { + override def mayPlace(stack: ItemStack) = inventory.canPlaceItem(getSlotIndex, stack) + + override def onTake(player: PlayerEntity, stack: ItemStack) = { + for (slot <- agentContainer.slots) slot match { case dynamic: ComponentSlot => dynamic.clearIfInvalid(player) case _ => } super.onTake(player, stack) } - override def putStack(stack: ItemStack): Unit = { - super.putStack(stack) + override def set(stack: ItemStack): Unit = { + super.set(stack) inventory match { case playerAware: common.tileentity.traits.PlayerInputAware => - playerAware.onSetInventorySlotContents(container.playerInventory.player, getSlotIndex, stack) + playerAware.onSetInventorySlotContents(agentContainer.playerInventory.player, getSlotIndex, stack) case _ => } } - override def onSlotChanged() { - super.onSlotChanged() - for (slot <- container.inventorySlots) slot match { - case dynamic: ComponentSlot => dynamic.clearIfInvalid(container.playerInventory.player) + override def setChanged() { + super.setChanged() + for (slot <- agentContainer.slots) slot match { + case dynamic: ComponentSlot => dynamic.clearIfInvalid(agentContainer.playerInventory.player) case _ => } changeListener.foreach(_(this)) } - protected def clearIfInvalid(player: EntityPlayer) {} + protected def clearIfInvalid(player: PlayerEntity) {} } diff --git a/src/main/scala/li/cil/oc/common/container/Database.scala b/src/main/scala/li/cil/oc/common/container/Database.scala index b3201ec444..bbc580e87c 100644 --- a/src/main/scala/li/cil/oc/common/container/Database.scala +++ b/src/main/scala/li/cil/oc/common/container/Database.scala @@ -1,13 +1,15 @@ package li.cil.oc.common.container import li.cil.oc.common.inventory.DatabaseInventory -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory._ +import net.minecraft.inventory.container.ClickType +import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -class Database(playerInventory: InventoryPlayer, databaseInventory: DatabaseInventory) extends Player(playerInventory, databaseInventory) { - val rows = math.sqrt(databaseInventory.getSizeInventory).ceil.toInt +class Database(id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) extends Player(null, id, playerInventory, databaseInventory) { + val rows = math.sqrt(databaseInventory.getContainerSize).ceil.toInt val offset = 8 + Array(3, 2, 0)(databaseInventory.tier) * slotSize for (row <- 0 until rows; col <- 0 until rows) { @@ -17,49 +19,49 @@ class Database(playerInventory: InventoryPlayer, databaseInventory: DatabaseInve // Show the player's inventory. addPlayerInventorySlots(8, 174) - override def canInteractWith(player: EntityPlayer) = player == playerInventory.player + override def stillValid(player: PlayerEntity) = player == playerInventory.player - override def slotClick(slot: Int, dragType: Int, clickType: ClickType, player: EntityPlayer): ItemStack = { - if (slot >= databaseInventory.getSizeInventory() || slot < 0) { + override def clicked(slot: Int, dragType: Int, clickType: ClickType, player: PlayerEntity): ItemStack = { + if (slot >= databaseInventory.getContainerSize() || slot < 0) { // if the slot interaction is with the user inventory use // default behavior - return super.slotClick(slot, dragType, clickType, player) + return super.clicked(slot, dragType, clickType, player) } // remove the ghost item - val ghostSlot = this.inventorySlots.get(slot); + val ghostSlot = this.slots.get(slot); if (ghostSlot != null) { val inventoryPlayer = player.inventory - val hand = inventoryPlayer.getItemStack() + val hand = inventoryPlayer.getCarried() var itemToAdd = ItemStack.EMPTY // if the player is holding an item, place a copy if (!hand.isEmpty()) { itemToAdd = hand.copy() } - ghostSlot.putStack(itemToAdd) + ghostSlot.set(itemToAdd) } ItemStack.EMPTY } override protected def tryTransferStackInSlot(from: Slot, intoPlayerInventory: Boolean) { if (intoPlayerInventory) { - from.onSlotChanged() + from.setChanged() return } - val fromStack = from.getStack().copy() + val fromStack = from.getItem().copy() if (fromStack.isEmpty) { return } fromStack.setCount(1) - val (begin, end) = (0, inventorySlots.size - 1) + val (begin, end) = (0, slots.size - 1) for (i <- begin to end) { - val intoSlot = inventorySlots.get(i) - if (intoSlot.inventory != from.inventory) { - if (!intoSlot.getHasStack && intoSlot.isItemValid(fromStack)) { - if (intoSlot.getSlotStackLimit > 0) { - intoSlot.putStack(fromStack) + val intoSlot = slots.get(i) + if (intoSlot.container != from.container) { + if (!intoSlot.hasItem && intoSlot.mayPlace(fromStack)) { + if (intoSlot.getMaxStackSize > 0) { + intoSlot.set(fromStack) return } } diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index 248e137b8d..63a19641c0 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -1,17 +1,17 @@ package li.cil.oc.common.container import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT -class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentity.Disassembler) extends Player(playerInventory, disassembler) { +class Disassembler(id: Int, playerInventory: PlayerInventory, val disassembler: tileentity.Disassembler) extends Player(null, id, playerInventory, disassembler) { addSlotToContainer(80, 35, "ocitem") addPlayerInventorySlots(8, 84) def disassemblyProgress = synchronizedData.getDouble("disassemblyProgress") - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { - synchronizedData.setDouble("disassemblyProgress", disassembler.progress) + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { + synchronizedData.putDouble("disassemblyProgress", disassembler.progress) super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala index 8a2c4b3fbf..71808b9d82 100644 --- a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala @@ -1,10 +1,10 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory -class DiskDrive(playerInventory: InventoryPlayer, drive: IInventory) extends Player(playerInventory, drive) { +class DiskDrive(id: Int, playerInventory: PlayerInventory, drive: IInventory) extends Player(null, id, playerInventory, drive) { addSlotToContainer(80, 35, Slot.Floppy) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index fb978f074c..f85e8f5b76 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -3,37 +3,37 @@ package li.cil.oc.common.container import li.cil.oc.client.Textures import li.cil.oc.common import li.cil.oc.common.entity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Drone(playerInventory: InventoryPlayer, drone: entity.Drone) extends Player(playerInventory, drone.mainInventory) { +class Drone(id: Int, playerInventory: PlayerInventory, drone: entity.Drone) extends Player(null, id, playerInventory, drone.mainInventory) { val deltaY = 0 for (i <- 0 to 1) { val y = 8 + i * slotSize - deltaY for (j <- 0 to 3) { val x = 98 + j * slotSize - addSlotToContainer(new InventorySlot(this, otherInventory, inventorySlots.size, x, y)) + addSlot(new InventorySlot(this, otherInventory, slots.size, x, y)) } } addPlayerInventorySlots(8, 66) class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { - def isValid = (0 until drone.mainInventory.getSizeInventory).contains(getSlotIndex) + def isValid = (0 until drone.mainInventory.getContainerSize).contains(getSlotIndex) - @SideOnly(Side.CLIENT) override - def isEnabled = isValid && super.isEnabled + @OnlyIn(Dist.CLIENT) override + def isActive = isValid && super.isActive override def getBackgroundLocation = if (isValid) super.getBackgroundLocation else Textures.Icons.get(common.Tier.None) - override def getStack = { - if (isValid) super.getStack + override def getItem = { + if (isValid) super.getItem else ItemStack.EMPTY } } diff --git a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala index 612a0e5a7f..7438e51c53 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -5,12 +5,12 @@ import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.util.InventoryUtils import li.cil.oc.util.SideTracker -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation -class DynamicComponentSlot(val container: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) extends ComponentSlot(inventory, index, x, y) { +class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) extends ComponentSlot(inventory, index, x, y) { override def tier: Int = { val mainTier = containerTierGetter() if (mainTier >= 0) info(this).tier @@ -29,17 +29,17 @@ class DynamicComponentSlot(val container: Player, inventory: IInventory, index: override def getBackgroundLocation: ResourceLocation = Option(Textures.Icons.get(slot)).getOrElse(super.getBackgroundLocation) - override def getSlotStackLimit: Int = + override def getMaxStackSize: Int = slot match { - case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getSlotStackLimit + case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getMaxStackSize case common.Slot.None => 0 case _ => 1 } - override protected def clearIfInvalid(player: EntityPlayer) { - if (SideTracker.isServer && getHasStack && !isItemValid(getStack)) { - val stack = getStack - putStack(ItemStack.EMPTY) + override protected def clearIfInvalid(player: PlayerEntity) { + if (SideTracker.isServer && hasItem && !mayPlace(getItem)) { + val stack = getItem + set(ItemStack.EMPTY) InventoryUtils.addToPlayerInventory(stack, player) } } diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index 64f20eae05..489e552d8b 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -5,47 +5,56 @@ import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.Tier import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.SideTracker -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory._ +import net.minecraft.inventory.container.ClickType +import net.minecraft.inventory.container.Container +import net.minecraft.inventory.container.ContainerType +import net.minecraft.inventory.container.IContainerListener +import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTBase -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.INBT +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.util.FakePlayer import scala.collection.convert.WrapAsScala._ +import scala.collection.mutable -abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: IInventory) extends Container { +abstract class Player(cType: ContainerType[_ <: Player], id: Int, val playerInventory: PlayerInventory, val otherInventory: IInventory) extends Container(cType, id) { /** Number of player inventory slots to display horizontally. */ - protected val playerInventorySizeX = math.min(9, InventoryPlayer.getHotbarSize) + protected val playerInventorySizeX = math.min(9, PlayerInventory.getSelectionSize) - /** Subtract four for armor slots. */ - protected val playerInventorySizeY = math.min(4, (playerInventory.getSizeInventory - 4) / playerInventorySizeX) + protected val playerInventorySizeY = math.min(4, playerInventory.items.size / playerInventorySizeX) /** Render size of slots (width and height). */ protected val slotSize = 18 private var lastSync = System.currentTimeMillis() - override def canInteractWith(player: EntityPlayer) = otherInventory.isUsableByPlayer(player) + protected val playerListeners = mutable.ArrayBuffer.empty[ServerPlayerEntity] - override def slotClick(slot: Int, dragType: Int, clickType: ClickType, player: EntityPlayer): ItemStack = { - val result = super.slotClick(slot, dragType, clickType, player) + override def stillValid(player: PlayerEntity) = otherInventory.stillValid(player) + + override def clicked(slot: Int, dragType: Int, clickType: ClickType, player: PlayerEntity): ItemStack = { + val result = super.clicked(slot, dragType, clickType, player) if (SideTracker.isServer) { - detectAndSendChanges() // We have to enforce this more than MC does itself + broadcastChanges() // We have to enforce this more than MC does itself // because stacks can change their... "character" just by being inserted in // certain containers - by being assigned an address. } result } - override def transferStackInSlot(player: EntityPlayer, index: Int): ItemStack = { - val slot = Option(inventorySlots.get(index)).orNull - if (slot != null && slot.getHasStack) { - tryTransferStackInSlot(slot, slot.inventory == otherInventory) + override def quickMoveStack(player: PlayerEntity, index: Int): ItemStack = { + val slot = Option(slots.get(index)).orNull + if (slot != null && slot.hasItem) { + tryTransferStackInSlot(slot, slot.container == otherInventory) if (SideTracker.isServer) { - detectAndSendChanges() + broadcastChanges() } } ItemStack.EMPTY @@ -57,30 +66,30 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: return false // nowhere to move it if (from == null || - !from.getHasStack || - from.getStack.isEmpty) + !from.hasItem || + from.getItem.isEmpty) return true // all moved because nothing to move - if (to.inventory == from.inventory) + if (to.container == from.container) return false // not intended for moving in the same inventory // for ghost slots we don't care about stack size - val fromStack = from.getStack - val toStack = if (to.getHasStack) to.getStack else ItemStack.EMPTY + val fromStack = from.getItem + val toStack = if (to.hasItem) to.getItem else ItemStack.EMPTY val toStackSize = if (!toStack.isEmpty) toStack.getCount else 0 - val maxStackSize = math.min(fromStack.getMaxStackSize, to.getSlotStackLimit) + val maxStackSize = math.min(fromStack.getMaxStackSize, to.getMaxStackSize) val itemsMoved = math.min(maxStackSize - toStackSize, fromStack.getCount) if (!toStack.isEmpty) { if (toStackSize < maxStackSize && - fromStack.isItemEqual(toStack) && - ItemStack.areItemStackTagsEqual(fromStack, toStack) && + fromStack.sameItem(toStack) && + ItemStack.tagMatches(fromStack, toStack) && itemsMoved > 0) { - toStack.grow(from.decrStackSize(itemsMoved).getCount) + toStack.grow(from.remove(itemsMoved).getCount) } else return false - } else if (to.isItemValid(fromStack)) { - to.putStack(from.decrStackSize(itemsMoved)) + } else if (to.mayPlace(fromStack)) { + to.set(from.remove(itemsMoved)) if (maxStackSize == 0) { // Special case: we have an inventory with "phantom/ghost stacks", i.e. // zero size stacks, usually used for configuring machinery. In that @@ -92,14 +101,14 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: } } else return false - to.onSlotChanged() - from.onSlotChanged() + to.setChanged() + from.setChanged() false } protected def fillOrder(backFill: Boolean): Seq[Int] = { - (if (backFill) inventorySlots.indices.reverse else inventorySlots.indices).sortBy(i => inventorySlots(i) match { - case s: Slot if s.getHasStack => -1 + (if (backFill) slots.indices.reverse else slots.indices).sortBy(i => slots(i) match { + case s: Slot if s.hasItem => -1 case s: ComponentSlot => s.tier case _ => 99 }) @@ -107,24 +116,24 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: protected def tryTransferStackInSlot(from: Slot, intoPlayerInventory: Boolean) { for (i <- fillOrder(intoPlayerInventory)) { - if (inventorySlots.get(i) match { case slot: Slot => tryMoveAllSlotToSlot(from, slot) case _ => false }) + if (slots.get(i) match { case slot: Slot => tryMoveAllSlotToSlot(from, slot) case _ => false }) return } } def addSlotToContainer(x: Int, y: Int, slot: String = common.Slot.Any, tier: Int = common.Tier.Any) { - val index = inventorySlots.size - addSlotToContainer(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier)) + val index = slots.size + addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier)) } def addSlotToContainer(x: Int, y: Int, info: Array[Array[InventorySlot]], containerTierGetter: () => Int) { - val index = inventorySlots.size - addSlotToContainer(new DynamicComponentSlot(this, otherInventory, index, x, y, slot => info(slot.containerTierGetter())(slot.getSlotIndex), containerTierGetter)) + val index = slots.size + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, slot => info(slot.containerTierGetter())(slot.getSlotIndex), containerTierGetter)) } def addSlotToContainer(x: Int, y: Int, info: DynamicComponentSlot => InventorySlot) { - val index = inventorySlots.size - addSlotToContainer(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => Tier.One)) + val index = slots.size + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => Tier.One)) } /** Render player inventory at the specified coordinates. */ @@ -136,7 +145,7 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: val x = left + slotX * slotSize // Compensate for hot bar offset. val y = top + (slotY - 1) * slotSize - addSlotToContainer(new Slot(playerInventory, index, x, y)) + addSlot(new Slot(playerInventory, index, x, y)) } } @@ -145,113 +154,120 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory: for (index <- 0 until playerInventorySizeX) { val x = left + index * slotSize val y = top + slotSize * (playerInventorySizeY - 1) + quickBarSpacing - addSlotToContainer(new Slot(playerInventory, index, x, y)) + addSlot(new Slot(playerInventory, index, x, y)) } } - protected def sendWindowProperty(id: Int, value: Int) { - listeners.foreach(_.sendWindowProperty(this, id, value)) + override def addSlotListener(listener: IContainerListener): Unit = { + listener match { + case _: FakePlayer => // Nope + case player: ServerPlayerEntity => playerListeners += player + case _ => + } + super.addSlotListener(listener) } - override def detectAndSendChanges(): Unit = { - super.detectAndSendChanges() + @OnlyIn(Dist.CLIENT) + override def removeSlotListener(listener: IContainerListener): Unit = { + if (listener.isInstanceOf[ServerPlayerEntity]) playerListeners -= listener.asInstanceOf[ServerPlayerEntity] + super.removeSlotListener(listener) + } + + override def broadcastChanges(): Unit = { + super.broadcastChanges() if (SideTracker.isServer) { - val nbt = new NBTTagCompound() + val nbt = new CompoundNBT() detectCustomDataChanges(nbt) - for (entry <- listeners) entry match { - case _: FakePlayer => // Nope - case player: EntityPlayerMP => ServerPacketSender.sendContainerUpdate(this, nbt, player) - case _ => - } + for (player <- playerListeners) ServerPacketSender.sendContainerUpdate(this, nbt, player) } } // Used for custom value synchronization, because shorts simply don't cut it most of the time. - protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { val delta = synchronizedData.getDelta - if (delta != null && !delta.hasNoTags) { - nbt.setTag("delta", delta) + if (delta != null && !delta.isEmpty) { + nbt.put("delta", delta) } else if (System.currentTimeMillis() - lastSync > 250) { - nbt.setTag("delta", synchronizedData) + nbt.put("delta", synchronizedData) lastSync = Long.MaxValue } } - def updateCustomData(nbt: NBTTagCompound): Unit = { - if (nbt.hasKey("delta")) { - val delta = nbt.getCompoundTag("delta") - delta.getKeySet.foreach { - case key: String => synchronizedData.setTag(key, delta.getTag(key)) + def updateCustomData(nbt: CompoundNBT): Unit = { + if (nbt.contains("delta")) { + val delta = nbt.getCompound("delta") + delta.getAllKeys.foreach { + case key: String => synchronizedData.put(key, delta.get(key)) } } } - protected class SynchronizedData extends NBTTagCompound { - private var delta = new NBTTagCompound() + protected class SynchronizedData extends CompoundNBT { + private var delta = new CompoundNBT() - def getDelta: NBTTagCompound = this.synchronized { - if (delta.hasNoTags) null + def getDelta: CompoundNBT = this.synchronized { + if (delta.isEmpty) null else { val result = delta - delta = new NBTTagCompound() + delta = new CompoundNBT() result } } - override def setTag(key: String, value: NBTBase): Unit = this.synchronized { - if (!value.equals(getTag(key))) delta.setTag(key, value) - super.setTag(key, value) + override def put(key: String, value: INBT): INBT = this.synchronized { + if (!value.equals(get(key))) delta.put(key, value) + super.put(key, value) } - override def setByte(key: String, value: Byte): Unit = this.synchronized { - if (value != getByte(key)) delta.setByte(key, value) - super.setByte(key, value) + override def putByte(key: String, value: Byte): Unit = this.synchronized { + if (value != getByte(key)) delta.putByte(key, value) + super.putByte(key, value) } - override def setShort(key: String, value: Short): Unit = this.synchronized { - if (value != getShort(key)) delta.setShort(key, value) - super.setShort(key, value) + override def putShort(key: String, value: Short): Unit = this.synchronized { + if (value != getShort(key)) delta.putShort(key, value) + super.putShort(key, value) } - override def setInteger(key: String, value: Int): Unit = this.synchronized { - if (value != getInteger(key)) delta.setInteger(key, value) - super.setInteger(key, value) + override def putInt(key: String, value: Int): Unit = this.synchronized { + if (value != getInt(key)) delta.putInt(key, value) + super.putInt(key, value) } - override def setLong(key: String, value: Long): Unit = this.synchronized { - if (value != getLong(key)) delta.setLong(key, value) - super.setLong(key, value) + override def putLong(key: String, value: Long): Unit = this.synchronized { + if (value != getLong(key)) delta.putLong(key, value) + super.putLong(key, value) } - override def setFloat(key: String, value: Float): Unit = this.synchronized { - if (value != getFloat(key)) delta.setFloat(key, value) - super.setFloat(key, value) + override def putFloat(key: String, value: Float): Unit = this.synchronized { + if (value != getFloat(key)) delta.putFloat(key, value) + super.putFloat(key, value) } - override def setDouble(key: String, value: Double): Unit = this.synchronized { - if (value != getDouble(key)) delta.setDouble(key, value) - super.setDouble(key, value) + override def putDouble(key: String, value: Double): Unit = this.synchronized { + if (value != getDouble(key)) delta.putDouble(key, value) + super.putDouble(key, value) } - override def setString(key: String, value: String): Unit = this.synchronized { - if (value != getString(key)) delta.setString(key, value) - super.setString(key, value) + override def putString(key: String, value: String): Unit = this.synchronized { + if (value != getString(key)) delta.putString(key, value) + super.putString(key, value) } - override def setByteArray(key: String, value: Array[Byte]): Unit = this.synchronized { - if (value.deep != getByteArray(key).deep) delta.setByteArray(key, value) - super.setByteArray(key, value) + override def putByteArray(key: String, value: Array[Byte]): Unit = this.synchronized { + if (value.deep != getByteArray(key).deep) delta.putByteArray(key, value) + super.putByteArray(key, value) } - override def setIntArray(key: String, value: Array[Int]): Unit = this.synchronized { - if (value.deep != getIntArray(key).deep) delta.setIntArray(key, value) - super.setIntArray(key, value) + override def putIntArray(key: String, value: Array[Int]): Unit = this.synchronized { + if (value.deep != getIntArray(key).deep) delta.putIntArray(key, value) + super.putIntArray(key, value) } - override def setBoolean(key: String, value: Boolean): Unit = this.synchronized { - if (value != getBoolean(key)) delta.setBoolean(key, value) - super.setBoolean(key, value) + override def putBoolean(key: String, value: Boolean): Unit = this.synchronized { + if (value != getBoolean(key)) delta.putBoolean(key, value) + super.putBoolean(key, value) } } diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index 8dbe78c5b8..16d1fba76b 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -2,10 +2,10 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT -class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) extends Player(playerInventory, printer) { +class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity.Printer) extends Player(null, id, playerInventory, printer) { addSlotToContainer(18, 19, Slot.Filtered) addSlotToContainer(18, 51, Slot.Filtered) addSlotToContainer(152, 35) @@ -15,14 +15,14 @@ class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) def progress = synchronizedData.getDouble("progress") - def amountMaterial = synchronizedData.getInteger("amountMaterial") + def amountMaterial = synchronizedData.getInt("amountMaterial") - def amountInk = synchronizedData.getInteger("amountInk") + def amountInk = synchronizedData.getInt("amountInk") - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { - synchronizedData.setDouble("progress", if (printer.isPrinting) printer.progress / 100.0 else 0) - synchronizedData.setInteger("amountMaterial", printer.amountMaterial) - synchronizedData.setInteger("amountInk", printer.amountInk) + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { + synchronizedData.putDouble("progress", if (printer.isPrinting) printer.progress / 100.0 else 0) + synchronizedData.putInt("amountMaterial", printer.amountMaterial) + synchronizedData.putInt("amountInk", printer.amountInk) super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Rack.scala b/src/main/scala/li/cil/oc/common/container/Rack.scala index 7c3fe1900f..707b16d36c 100644 --- a/src/main/scala/li/cil/oc/common/container/Rack.scala +++ b/src/main/scala/li/cil/oc/common/container/Rack.scala @@ -4,13 +4,14 @@ import li.cil.oc.api.component.RackMountable import li.cil.oc.common.Slot import li.cil.oc.common.tileentity import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagIntArray -import net.minecraft.util.EnumFacing +import li.cil.oc.util.RotationHelper +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.IntArrayNBT +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends Player(playerInventory, rack) { +class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) extends Player(null, id, playerInventory, rack) { addSlotToContainer(20, 23, Slot.RackMountable) addSlotToContainer(20, 43, Slot.RackMountable) addSlotToContainer(20, 63, Slot.RackMountable) @@ -20,25 +21,25 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends final val MaxConnections = 4 val nodePresence: Array[Array[Boolean]] = Array.fill(4)(Array.fill(4)(false)) - override def updateCustomData(nbt: NBTTagCompound): Unit = { + override def updateCustomData(nbt: CompoundNBT): Unit = { super.updateCustomData(nbt) - nbt.getTagList("nodeMapping", NBT.TAG_INT_ARRAY).map((sides: NBTTagIntArray) => { - sides.getIntArray.map(side => if (side >= 0) Option(EnumFacing.getFront(side)) else None) + nbt.getList("nodeMapping", NBT.TAG_INT_ARRAY).map((sides: IntArrayNBT) => { + sides.getAsIntArray.map(side => if (side >= 0) Option(Direction.from3DDataValue(side)) else None) }).copyToArray(rack.nodeMapping) nbt.getBooleanArray("nodePresence").grouped(MaxConnections).copyToArray(nodePresence) rack.isRelayEnabled = nbt.getBoolean("isRelayEnabled") } - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { super.detectCustomDataChanges(nbt) nbt.setNewTagList("nodeMapping", rack.nodeMapping.map(sides => toNbt(sides.map { case Some(side) => side.ordinal() case _ => -1 }))) - nbt.setBooleanArray("nodePresence", (0 until rack.getSizeInventory).flatMap(slot => rack.getMountable(slot) match { + nbt.setBooleanArray("nodePresence", (0 until rack.getContainerSize).flatMap(slot => rack.getMountable(slot) match { case mountable: RackMountable => (Seq(true) ++ (0 until math.min(MaxConnections - 1, mountable.getConnectableCount)).map(index => mountable.getConnectableAt(index) != null)).padTo(MaxConnections, false) case _ => Array.fill(MaxConnections)(false) }).toArray) - nbt.setBoolean("isRelayEnabled", rack.isRelayEnabled) + nbt.putBoolean("isRelayEnabled", rack.isRelayEnabled) } } diff --git a/src/main/scala/li/cil/oc/common/container/Raid.scala b/src/main/scala/li/cil/oc/common/container/Raid.scala index 6774008d7e..cc630605da 100644 --- a/src/main/scala/li/cil/oc/common/container/Raid.scala +++ b/src/main/scala/li/cil/oc/common/container/Raid.scala @@ -3,9 +3,9 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory -class Raid(playerInventory: InventoryPlayer, raid: tileentity.Raid) extends Player(playerInventory, raid) { +class Raid(id: Int, playerInventory: PlayerInventory, raid: tileentity.Raid) extends Player(null, id, playerInventory, raid) { addSlotToContainer(60, 23, Slot.HDD, Tier.Three) addSlotToContainer(80, 23, Slot.HDD, Tier.Three) addSlotToContainer(100, 23, Slot.HDD, Tier.Three) diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala index d9ee4a54f9..4562e747cb 100644 --- a/src/main/scala/li/cil/oc/common/container/Relay.scala +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -2,32 +2,32 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.tileentity -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT -class Relay(playerInventory: InventoryPlayer, relay: tileentity.Relay) extends Player(playerInventory, relay) { +class Relay(id: Int, playerInventory: PlayerInventory, relay: tileentity.Relay) extends Player(null, id, playerInventory, relay) { addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) addSlotToContainer(151, 53, Slot.HDD) addSlotToContainer(178, 15, Slot.Card) addPlayerInventorySlots(8, 84) - def relayDelay = synchronizedData.getInteger("relayDelay") + def relayDelay = synchronizedData.getInt("relayDelay") - def relayAmount = synchronizedData.getInteger("relayAmount") + def relayAmount = synchronizedData.getInt("relayAmount") - def maxQueueSize = synchronizedData.getInteger("maxQueueSize") + def maxQueueSize = synchronizedData.getInt("maxQueueSize") - def packetsPerCycleAvg = synchronizedData.getInteger("packetsPerCycleAvg") + def packetsPerCycleAvg = synchronizedData.getInt("packetsPerCycleAvg") - def queueSize = synchronizedData.getInteger("queueSize") + def queueSize = synchronizedData.getInt("queueSize") - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { - synchronizedData.setInteger("relayDelay", relay.relayDelay) - synchronizedData.setInteger("relayAmount", relay.relayAmount) - synchronizedData.setInteger("maxQueueSize", relay.maxQueueSize) - synchronizedData.setInteger("packetsPerCycleAvg", relay.packetsPerCycleAvg()) - synchronizedData.setInteger("queueSize", relay.queue.size) + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { + synchronizedData.putInt("relayDelay", relay.relayDelay) + synchronizedData.putInt("relayAmount", relay.relayAmount) + synchronizedData.putInt("maxQueueSize", relay.maxQueueSize) + synchronizedData.putInt("packetsPerCycleAvg", relay.packetsPerCycleAvg()) + synchronizedData.putInt("queueSize", relay.queue.size) super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 4d73cb09a7..3006cd51d2 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -5,14 +5,15 @@ import li.cil.oc.client.Textures import li.cil.oc.common import li.cil.oc.common.tileentity import li.cil.oc.util.SideTracker -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack +import net.minecraft.util.IntReferenceHolder import net.minecraft.util.ResourceLocation -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Robot(playerInventory: InventoryPlayer, robot: tileentity.Robot) extends Player(playerInventory, robot) { +class Robot(id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) extends Player(null, id, playerInventory, robot) { val hasScreen: Boolean = robot.components.exists { case Some(buffer: api.internal.TextBuffer) => true case _ => false @@ -26,16 +27,20 @@ class Robot(playerInventory: InventoryPlayer, robot: tileentity.Robot) extends P addSlotToContainer(170 + 2 * slotSize, 232 - deltaY, robot.containerSlotType(2), robot.containerSlotTier(2)) addSlotToContainer(170 + 3 * slotSize, 232 - deltaY, robot.containerSlotType(3), robot.containerSlotTier(3)) - for (i <- 0 to 3) { - val y = 156 + i * slotSize - deltaY - for (j <- 0 to 3) { - val x = 170 + j * slotSize - addSlotToContainer(new InventorySlot(this, otherInventory, inventorySlots.size, x, y)) + // Slot.x and Slot.y are final, so have to rebuild when scrolling + def generateSlotsFor(scroll: Int) { + for (i <- 0 to 15) { + val y = 156 + (i - scroll) * slotSize - deltaY + for (j <- 0 to 3) { + val x = 170 + j * slotSize + val slot = new InventorySlot(this, otherInventory, slots.size, x, y, i >= scroll && i < scroll + 4) + val idx = 4 + j + 4 * i + if (slots.size() <= idx) addSlot(slot) + else slots.set(idx, slot) + } } } - for (i <- 16 until 64) { - addSlotToContainer(new InventorySlot(this, otherInventory, inventorySlots.size, -10000, -10000)) - } + generateSlotsFor(0) addPlayerInventorySlots(6, 174 - deltaY) @@ -44,51 +49,32 @@ class Robot(playerInventory: InventoryPlayer, robot: tileentity.Robot) extends P // values as shorts over the net (for whatever reason). private val factor = 100 - private var lastSentBuffer = -1 + addDataSlot(new IntReferenceHolder { + override def get(): Int = robot.globalBuffer.toInt / factor - private var lastSentBufferSize = -1 + override def set(value: Int): Unit = robot.globalBuffer = value * factor + }) - @SideOnly(Side.CLIENT) - override def updateProgressBar(id: Int, value: Int) { - super.updateProgressBar(id, value) - if (id == 0) { - robot.globalBuffer = value * factor - } - - if (id == 1) { - robot.globalBufferSize = value * factor - } - } + addDataSlot(new IntReferenceHolder { + override def get(): Int = robot.globalBufferSize.toInt / factor - override def detectAndSendChanges() { - super.detectAndSendChanges() - if (SideTracker.isServer) { - val currentBuffer = robot.globalBuffer.toInt / factor - if (currentBuffer != lastSentBuffer) { - lastSentBuffer = currentBuffer - sendWindowProperty(0, lastSentBuffer) - } + override def set(value: Int): Unit = robot.globalBufferSize = value * factor + }) - val currentBufferSize = robot.globalBufferSize.toInt / factor - if (currentBufferSize != lastSentBufferSize) { - lastSentBufferSize = currentBufferSize - sendWindowProperty(1, lastSentBufferSize) - } - } - } + class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, var enabled: Boolean) + extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { - class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { def isValid: Boolean = robot.isInventorySlot(getSlotIndex) - @SideOnly(Side.CLIENT) override - def isEnabled: Boolean = isValid && super.isEnabled + @OnlyIn(Dist.CLIENT) override + def isActive: Boolean = enabled && isValid && super.isActive override def getBackgroundLocation: ResourceLocation = if (isValid) super.getBackgroundLocation else Textures.Icons.get(common.Tier.None) - override def getStack: ItemStack = { - if (isValid) super.getStack + override def getItem: ItemStack = { + if (isValid) super.getItem else ItemStack.EMPTY } } diff --git a/src/main/scala/li/cil/oc/common/container/Server.scala b/src/main/scala/li/cil/oc/common/container/Server.scala index 19700b379f..a9e5d28953 100644 --- a/src/main/scala/li/cil/oc/common/container/Server.scala +++ b/src/main/scala/li/cil/oc/common/container/Server.scala @@ -3,64 +3,64 @@ package li.cil.oc.common.container import li.cil.oc.common.InventorySlots import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.server.component -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.nbt.CompoundNBT -class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory, val server: Option[component.Server] = None) extends Player(playerInventory, serverInventory) { +class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val server: Option[component.Server] = None) extends Player(null, id, playerInventory, serverInventory) { for (i <- 0 to 1) { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) } val verticalSlots = math.min(3, 1 + serverInventory.tier) for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(100, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(124, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(148, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 2 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.server(serverInventory.tier)(getInventory.size) + val slot = InventorySlots.server(serverInventory.tier)(getItems.size) addSlotToContainer(26, 34, slot.slot, slot.tier) } // Show the player's inventory. addPlayerInventorySlots(8, 84) - override def canInteractWith(player: EntityPlayer) = { - if (server.isDefined) super.canInteractWith(player) + override def stillValid(player: PlayerEntity) = { + if (server.isDefined) super.stillValid(player) else player == playerInventory.player } var isRunning = false var isItem = true - override def updateCustomData(nbt: NBTTagCompound): Unit = { + override def updateCustomData(nbt: CompoundNBT): Unit = { super.updateCustomData(nbt) isRunning = nbt.getBoolean("isRunning") isItem = nbt.getBoolean("isItem") } - override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { super.detectCustomDataChanges(nbt) server match { - case Some(s) => nbt.setBoolean("isRunning", s.machine.isRunning) - case _ => nbt.setBoolean("isItem", true) + case Some(s) => nbt.putBoolean("isRunning", s.machine.isRunning) + case _ => nbt.putBoolean("isItem", true) } } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala index 412c6dabed..ed32c3d708 100644 --- a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala @@ -3,17 +3,19 @@ package li.cil.oc.common.container import li.cil.oc.client.Textures import li.cil.oc.common import net.minecraft.inventory.IInventory +import net.minecraft.util.ResourceLocation +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class StaticComponentSlot(val container: Player, inventory: IInventory, index: Int, x: Int, y: Int, val slot: String, val tier: Int) extends ComponentSlot(inventory, index, x, y) { - if (container.playerInventory.player.getEntityWorld.isRemote) { - setBackgroundLocation(Textures.Icons.get(slot)) - } - +class StaticComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, val slot: String, val tier: Int) extends ComponentSlot(inventory, index, x, y) { val tierIcon = Textures.Icons.get(tier) - override def getSlotStackLimit = + @OnlyIn(Dist.CLIENT) + override def getBackgroundLocation: ResourceLocation = Textures.Icons.get(slot) + + override def getMaxStackSize = slot match { - case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getSlotStackLimit + case common.Slot.Tool | common.Slot.Any | common.Slot.Filtered => super.getMaxStackSize case common.Slot.None => 0 case _ => 1 } diff --git a/src/main/scala/li/cil/oc/common/container/Tablet.scala b/src/main/scala/li/cil/oc/common/container/Tablet.scala index 5e84bd3479..03a420827e 100644 --- a/src/main/scala/li/cil/oc/common/container/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/container/Tablet.scala @@ -1,13 +1,13 @@ package li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory -class Tablet(playerInventory: InventoryPlayer, tablet: TabletWrapper) extends Player(playerInventory, tablet) { - addSlotToContainer(new StaticComponentSlot(this, otherInventory, otherInventory.getSizeInventory - 1, 80, 35, tablet.containerSlotType, tablet.containerSlotTier)) +class Tablet(id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) extends Player(null, id, playerInventory, tablet) { + addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, tablet.containerSlotType, tablet.containerSlotTier)) addPlayerInventorySlots(8, 84) - override def canInteractWith(player: EntityPlayer) = player == playerInventory.player + override def stillValid(player: PlayerEntity) = player == playerInventory.player } diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 552fbd817d..de85bdf2c5 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -30,45 +30,52 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.InventoryUtils -import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.block.material.Material import net.minecraft.entity.Entity +import net.minecraft.entity.EntitySize import net.minecraft.entity.MoverType -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.Pose +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.network.datasync.DataParameter import net.minecraft.network.datasync.DataSerializers import net.minecraft.network.datasync.EntityDataManager -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.tags.FluidTags +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.text.ITextComponent import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld import net.minecraftforge.fluids.IFluidTank +import net.minecraftforge.fml.network.NetworkHooks import scala.collection.convert.WrapAsJava._ object Drone { - val DataRunning: DataParameter[lang.Boolean] = EntityDataManager.createKey(classOf[Drone], DataSerializers.BOOLEAN) - val DataTargetX: DataParameter[lang.Float] = EntityDataManager.createKey(classOf[Drone], DataSerializers.FLOAT) - val DataTargetY: DataParameter[lang.Float] = EntityDataManager.createKey(classOf[Drone], DataSerializers.FLOAT) - val DataTargetZ: DataParameter[lang.Float] = EntityDataManager.createKey(classOf[Drone], DataSerializers.FLOAT) - val DataMaxAcceleration: DataParameter[lang.Float] = EntityDataManager.createKey(classOf[Drone], DataSerializers.FLOAT) - val DataSelectedSlot: DataParameter[Integer] = EntityDataManager.createKey(classOf[Drone], DataSerializers.VARINT) - val DataCurrentEnergy: DataParameter[Integer] = EntityDataManager.createKey(classOf[Drone], DataSerializers.VARINT) - val DataMaxEnergy: DataParameter[Integer] = EntityDataManager.createKey(classOf[Drone], DataSerializers.VARINT) - val DataStatusText: DataParameter[String] = EntityDataManager.createKey(classOf[Drone], DataSerializers.STRING) - val DataInventorySize: DataParameter[Integer] = EntityDataManager.createKey(classOf[Drone], DataSerializers.VARINT) - val DataLightColor: DataParameter[Integer] = EntityDataManager.createKey(classOf[Drone], DataSerializers.VARINT) + val DataRunning: DataParameter[lang.Boolean] = EntityDataManager.defineId(classOf[Drone], DataSerializers.BOOLEAN) + val DataTargetX: DataParameter[lang.Float] = EntityDataManager.defineId(classOf[Drone], DataSerializers.FLOAT) + val DataTargetY: DataParameter[lang.Float] = EntityDataManager.defineId(classOf[Drone], DataSerializers.FLOAT) + val DataTargetZ: DataParameter[lang.Float] = EntityDataManager.defineId(classOf[Drone], DataSerializers.FLOAT) + val DataMaxAcceleration: DataParameter[lang.Float] = EntityDataManager.defineId(classOf[Drone], DataSerializers.FLOAT) + val DataSelectedSlot: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) + val DataCurrentEnergy: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) + val DataMaxEnergy: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) + val DataStatusText: DataParameter[String] = EntityDataManager.defineId(classOf[Drone], DataSerializers.STRING) + val DataInventorySize: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) + val DataLightColor: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) } // internal.Rotatable is also in internal.Drone, but it wasn't since the start // so this is to ensure it is implemented here, in the very unlikely case that // someone decides to ship that specific version of the API. -class Drone(world: World) extends Entity(world) with MachineHost with internal.Drone with internal.Rotatable with Analyzable with Context { - override def world: World = getEntityWorld +class Drone(world: World) extends Entity(null, world) with MachineHost with internal.Drone with internal.Rotatable with Analyzable with Context { + override def world: World = level // Some basic constants. val gravity = 0.05f @@ -77,8 +84,14 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D val maxAcceleration = 0.1f val maxVelocity = 0.4f val maxInventorySize = 8 - setSize(12 / 16f, 6 / 16f) - isImmuneToFire = true + + @Deprecated + private val size = EntitySize.fixed(12 / 16f, 6 / 16f) + @Deprecated + override def getDimensions(pose: Pose) = size + + @Deprecated + override def fireImmune = true // Rendering stuff, purely eyecandy. val targetFlapAngles: Array[Array[Float]] = Array.fill(4, 2)(0f) @@ -91,24 +104,24 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D // Logic stuff, components, machine and such. val info = new DroneData() - val machine: api.machine.Machine = if (!world.isRemote) { + val machine: api.machine.Machine = if (!world.isClientSide) { val m = Machine.create(this) m.node.asInstanceOf[Connector].setLocalBufferSize(0) m } else null - val control: component.Drone = if (!world.isRemote) new component.Drone(this) else null + val control: component.Drone = if (!world.isClientSide) new component.Drone(this) else null val components = new ComponentInventory { override def host: Drone = Drone.this override def items: Array[ItemStack] = info.components - override def getSizeInventory: Int = info.components.length + override def getContainerSize: Int = info.components.length - override def markDirty() {} + override def setChanged() {} - override def isItemValidForSlot(slot: Int, stack: ItemStack) = true + override def canPlaceItem(slot: Int, stack: ItemStack) = true - override def isUsableByPlayer(player: EntityPlayer) = true + override def stillValid(player: PlayerEntity) = true override def node: Node = Option(machine).map(_.node).orNull @@ -121,28 +134,28 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D val equipmentInventory = new Inventory { val items = Array.empty[ItemStack] - override def getSizeInventory = 0 + override def getContainerSize = 0 - override def getInventoryStackLimit = 0 + override def getMaxStackSize = 0 - override def markDirty(): Unit = {} + override def setChanged(): Unit = {} - override def isItemValidForSlot(slot: Int, stack: ItemStack) = false + override def canPlaceItem(slot: Int, stack: ItemStack) = false - override def isUsableByPlayer(player: EntityPlayer) = false + override def stillValid(player: PlayerEntity) = false } val mainInventory = new Inventory { val items: Array[ItemStack] = Array.fill[ItemStack](8)(ItemStack.EMPTY) - override def getSizeInventory: Int = inventorySize + override def getContainerSize: Int = inventorySize - override def getInventoryStackLimit = 64 + override def getMaxStackSize = 64 - override def markDirty() {} // TODO update client GUI? + override def setChanged() {} // TODO update client GUI? - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = slot >= 0 && slot < getSizeInventory + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = slot >= 0 && slot < getContainerSize - override def isUsableByPlayer(player: EntityPlayer): Boolean = player.getDistanceSq(Drone.this) < 64 + override def stillValid(player: PlayerEntity): Boolean = player.distanceToSqr(Drone.this) < 64 } val tank = new MultiTank { override def tankCount: Int = components.components.count { @@ -160,9 +173,9 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D override def tier: Int = info.tier - override def player(): EntityPlayer = { + override def player(): PlayerEntity = { agent.Player.updatePositionAndRotation(player_, facing, facing) - agent.Player.setInventoryPlayerItems(player_) + agent.Player.setPlayerInventoryItems(player_) player_ } @@ -187,7 +200,7 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D override def isPaused: Boolean = machine.isPaused override def start(): Boolean = { - if (world.isRemote || machine.isRunning) { + if (world.isClientSide || machine.isRunning) { return false } preparePowerUp() @@ -204,43 +217,52 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D // ----------------------------------------------------------------------- // - override def getTarget = new Vec3d(targetX.floatValue(), targetY.floatValue(), targetZ.floatValue()) + override def getTarget = new Vector3d(targetX.floatValue(), targetY.floatValue(), targetZ.floatValue()) - override def setTarget(value: Vec3d): Unit = { + override def setTarget(value: Vector3d): Unit = { targetX = value.x.toFloat targetY = value.y.toFloat targetZ = value.z.toFloat } - override def getVelocity = new Vec3d(motionX, motionY, motionZ) + @Deprecated + def motionX = getDeltaMovement.x + + @Deprecated + def motionY = getDeltaMovement.y + + @Deprecated + def motionZ = getDeltaMovement.z + + override def getVelocity = getDeltaMovement // ----------------------------------------------------------------------- // override def canBeCollidedWith = true - override def canBePushed = true + override def isPushable = true // ----------------------------------------------------------------------- // - override def xPosition: Double = posX + override def xPosition: Double = getX - override def yPosition: Double = posY + override def yPosition: Double = getY - override def zPosition: Double = posZ + override def zPosition: Double = getZ override def markChanged() {} // ----------------------------------------------------------------------- // - override def facing = EnumFacing.SOUTH + override def facing = Direction.SOUTH - override def toLocal(value: EnumFacing): EnumFacing = value + override def toLocal(value: Direction): Direction = value - override def toGlobal(value: EnumFacing): EnumFacing = value + override def toGlobal(value: Direction): Direction = value // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) // ----------------------------------------------------------------------- // @@ -262,32 +284,32 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D // ----------------------------------------------------------------------- // - override def entityInit() { - getDataManager.register(Drone.DataRunning, java.lang.Boolean.FALSE) - getDataManager.register(Drone.DataTargetX, Float.box(0f)) - getDataManager.register(Drone.DataTargetY, Float.box(0f)) - getDataManager.register(Drone.DataTargetZ, Float.box(0f)) - getDataManager.register(Drone.DataMaxAcceleration, Float.box(0f)) - getDataManager.register(Drone.DataSelectedSlot, Int.box(0)) - getDataManager.register(Drone.DataCurrentEnergy, Int.box(0)) - getDataManager.register(Drone.DataMaxEnergy, Int.box(100)) - getDataManager.register(Drone.DataStatusText, "") - getDataManager.register(Drone.DataInventorySize, Int.box(0)) - getDataManager.register(Drone.DataLightColor, Int.box(0x66DD55)) + override def defineSynchedData() { + entityData.define(Drone.DataRunning, java.lang.Boolean.FALSE) + entityData.define(Drone.DataTargetX, Float.box(0f)) + entityData.define(Drone.DataTargetY, Float.box(0f)) + entityData.define(Drone.DataTargetZ, Float.box(0f)) + entityData.define(Drone.DataMaxAcceleration, Float.box(0f)) + entityData.define(Drone.DataSelectedSlot, Int.box(0)) + entityData.define(Drone.DataCurrentEnergy, Int.box(0)) + entityData.define(Drone.DataMaxEnergy, Int.box(100)) + entityData.define(Drone.DataStatusText, "") + entityData.define(Drone.DataInventorySize, Int.box(0)) + entityData.define(Drone.DataLightColor, Int.box(0x66DD55)) } - def initializeAfterPlacement(stack: ItemStack, player: EntityPlayer, position: Vec3d) { - info.load(stack) + def initializeAfterPlacement(stack: ItemStack, player: PlayerEntity, position: Vector3d) { + info.loadData(stack) control.node.changeBuffer(info.storedEnergy - control.node.localBuffer) wireThingsTogether() inventorySize = computeInventorySize() - setPosition(position.x, position.y, position.z) + setPos(position.x, position.y, position.z) } def preparePowerUp() { - targetX = math.floor(posX).toFloat + 0.5f - targetY = math.round(posY).toFloat + 0.5f - targetZ = math.floor(posZ).toFloat + 0.5f + targetX = math.floor(getX).toFloat + 0.5f + targetY = math.round(getY).toFloat + 0.5f + targetZ = math.floor(getZ).toFloat + 0.5f targetAcceleration = maxAcceleration wireThingsTogether() @@ -300,57 +322,57 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D components.connectComponents() } - def isRunning: Boolean = getDataManager.get(Drone.DataRunning) + def isRunning: Boolean = entityData.get(Drone.DataRunning) - def targetX: lang.Float = getDataManager.get(Drone.DataTargetX) + def targetX: lang.Float = entityData.get(Drone.DataTargetX) - def targetY: lang.Float = getDataManager.get(Drone.DataTargetY) + def targetY: lang.Float = entityData.get(Drone.DataTargetY) - def targetZ: lang.Float = getDataManager.get(Drone.DataTargetZ) + def targetZ: lang.Float = entityData.get(Drone.DataTargetZ) - def targetAcceleration: lang.Float = getDataManager.get(Drone.DataMaxAcceleration) + def targetAcceleration: lang.Float = entityData.get(Drone.DataMaxAcceleration) - def selectedSlot: Int = getDataManager.get(Drone.DataSelectedSlot) & 0xFF + def selectedSlot: Int = entityData.get(Drone.DataSelectedSlot) & 0xFF - def globalBuffer: Integer = getDataManager.get(Drone.DataCurrentEnergy) + def globalBuffer: Integer = entityData.get(Drone.DataCurrentEnergy) - def globalBufferSize: Integer = getDataManager.get(Drone.DataMaxEnergy) + def globalBufferSize: Integer = entityData.get(Drone.DataMaxEnergy) - def statusText: String = getDataManager.get(Drone.DataStatusText) + def statusText: String = entityData.get(Drone.DataStatusText) - def inventorySize: Int = getDataManager.get(Drone.DataInventorySize) & 0xFF + def inventorySize: Int = entityData.get(Drone.DataInventorySize) & 0xFF - def lightColor: Integer = getDataManager.get(Drone.DataLightColor) + def lightColor: Integer = entityData.get(Drone.DataLightColor) - def setRunning(value: Boolean): Unit = getDataManager.set(Drone.DataRunning, Boolean.box(value)) + def setRunning(value: Boolean): Unit = entityData.set(Drone.DataRunning, Boolean.box(value)) // Round target values to low accuracy to avoid floating point errors accumulating. - def targetX_=(value: Float): Unit = getDataManager.set(Drone.DataTargetX, Float.box(math.round(value * 4) / 4f)) + def targetX_=(value: Float): Unit = entityData.set(Drone.DataTargetX, Float.box(math.round(value * 4) / 4f)) - def targetY_=(value: Float): Unit = getDataManager.set(Drone.DataTargetY, Float.box(math.round(value * 4) / 4f)) + def targetY_=(value: Float): Unit = entityData.set(Drone.DataTargetY, Float.box(math.round(value * 4) / 4f)) - def targetZ_=(value: Float): Unit = getDataManager.set(Drone.DataTargetZ, Float.box(math.round(value * 4) / 4f)) + def targetZ_=(value: Float): Unit = entityData.set(Drone.DataTargetZ, Float.box(math.round(value * 4) / 4f)) - def targetAcceleration_=(value: Float): Unit = getDataManager.set(Drone.DataMaxAcceleration, Float.box(math.max(0, math.min(maxAcceleration, value)))) + def targetAcceleration_=(value: Float): Unit = entityData.set(Drone.DataMaxAcceleration, Float.box(math.max(0, math.min(maxAcceleration, value)))) - def setSelectedSlot(value: Int): Unit = getDataManager.set(Drone.DataSelectedSlot, Int.box(value.toByte)) + def setSelectedSlot(value: Int): Unit = entityData.set(Drone.DataSelectedSlot, Int.box(value.toByte)) - def globalBuffer_=(value: Int): Unit = getDataManager.set(Drone.DataCurrentEnergy, Int.box(value)) + def globalBuffer_=(value: Int): Unit = entityData.set(Drone.DataCurrentEnergy, Int.box(value)) - def globalBufferSize_=(value: Int): Unit = getDataManager.set(Drone.DataMaxEnergy, Int.box(value)) + def globalBufferSize_=(value: Int): Unit = entityData.set(Drone.DataMaxEnergy, Int.box(value)) - def statusText_=(value: String): Unit = getDataManager.set(Drone.DataStatusText, Option(value).fold("")(_.lines.map(_.take(10)).take(2).mkString("\n"))) + def statusText_=(value: String): Unit = entityData.set(Drone.DataStatusText, Option(value).fold("")(_.lines.map(_.take(10)).take(2).mkString("\n"))) - def inventorySize_=(value: Int): Unit = getDataManager.set(Drone.DataInventorySize, Int.box(value.toByte)) + def inventorySize_=(value: Int): Unit = entityData.set(Drone.DataInventorySize, Int.box(value.toByte)) - def lightColor_=(value: Int): Unit = getDataManager.set(Drone.DataLightColor, Int.box(value)) + def lightColor_=(value: Int): Unit = entityData.set(Drone.DataLightColor, Int.box(value)) - override def setPositionAndRotationDirect(x: Double, y: Double, z: Double, yaw: Float, pitch: Float, posRotationIncrements: Int, teleport: Boolean): Unit = { + override def lerpTo(x: Double, y: Double, z: Double, yaw: Float, pitch: Float, posRotationIncrements: Int, teleport: Boolean): Unit = { // Only set exact position if we're too far away from the server's // position, otherwise keep interpolating. This removes jitter and // is good enough for drones. - if (!isRunning || getDistanceSq(x, y, z) > 1) { - super.setPositionAndRotation(x, y, z, yaw, pitch) + if (!isRunning || distanceToSqr(x, y, z) > 1) { + super.absMoveTo(x, y, z, yaw, pitch) } else { targetX = x.toFloat @@ -359,11 +381,11 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D } } - override def onUpdate() { - super.onUpdate() + override def tick() { + super.tick() - if (!world.isRemote) { - if (isInsideOfMaterial(Material.WATER) || isInsideOfMaterial(Material.LAVA)) { + if (!world.isClientSide) { + if (isInWater || isInLava) { // We're not water-proof! machine.stop() } @@ -372,7 +394,7 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D setRunning(machine.isRunning) val buffer = math.round(machine.node.asInstanceOf[Connector].globalBuffer).toInt - if (math.abs(lastEnergyUpdate - buffer) > 1 || world.getTotalWorldTime % 200 == 0) { + if (math.abs(lastEnergyUpdate - buffer) > 1 || world.getGameTime % 200 == 0) { lastEnergyUpdate = buffer globalBuffer = buffer globalBufferSize = machine.node.asInstanceOf[Connector].globalBufferSize.toInt @@ -382,7 +404,7 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D if (isRunning) { // Client side update; occasionally update wing pitch and rotation to // make the drones look a bit more dynamic. - val rng = world.rand + val rng = world.random nextFlapChange -= 1 nextAngularVelocityChange -= 1 @@ -417,137 +439,125 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D } } - prevPosX = posX - prevPosY = posY - prevPosZ = posZ - noClip = pushOutOfBlocks(posX, (getEntityBoundingBox.minY + getEntityBoundingBox.maxY) / 2, posZ) + xo = getX + yo = getY + zo = getZ + moveTowardsClosestSpace(getX, (getBoundingBox.minY + getBoundingBox.maxY) / 2, getZ) + noPhysics = true if (isRunning) { - val toTarget = new Vec3d(targetX - posX, targetY - posY, targetZ - posZ) - val distance = toTarget.lengthVector() - val velocity = new Vec3d(motionX, motionY, motionZ) - if (distance > 0 && (distance > 0.005f || velocity.dotProduct(velocity) > 0.005f)) { + val toTarget = new Vector3d(targetX - getX, targetY - getY, targetZ - getZ) + val distance = toTarget.length() + val velocity = new Vector3d(motionX, motionY, motionZ) + if (distance > 0 && (distance > 0.005f || velocity.dot(velocity) > 0.005f)) { val acceleration = math.min(targetAcceleration.floatValue(), distance) / distance val velocityX = velocity.x + toTarget.x * acceleration val velocityY = velocity.y + toTarget.y * acceleration val velocityZ = velocity.z + toTarget.z * acceleration - motionX = math.max(-maxVelocity, math.min(maxVelocity, velocityX)) - motionY = math.max(-maxVelocity, math.min(maxVelocity, velocityY)) - motionZ = math.max(-maxVelocity, math.min(maxVelocity, velocityZ)) + setDeltaMovement(new Vector3d(math.max(-maxVelocity, math.min(maxVelocity, velocityX)), + math.max(-maxVelocity, math.min(maxVelocity, velocityY)), + math.max(-maxVelocity, math.min(maxVelocity, velocityZ)))) } else { - motionX = 0 - motionY = 0 - motionZ = 0 - posX = targetX.floatValue() - posY = targetY.floatValue() - posZ = targetZ.floatValue() + setDeltaMovement(Vector3d.ZERO) + setPos(targetX.floatValue(), targetY.floatValue(), targetZ.floatValue()) } } else { // No power, free fall: engage! - motionY -= gravity + setDeltaMovement(getDeltaMovement.subtract(0, gravity, 0)) } - move(MoverType.SELF, motionX, motionY, motionZ) + move(MoverType.SELF, getDeltaMovement) // Make sure we don't get infinitely faster. if (isRunning) { - motionX *= drag - motionY *= drag - motionZ *= drag + setDeltaMovement(getDeltaMovement.scale(drag)) } else { - val groundDrag = world.getBlock(BlockPosition(this: Entity).offset(EnumFacing.DOWN)).slipperiness * drag - motionX *= groundDrag - motionY *= drag - motionZ *= groundDrag - if (onGround) { - motionY *= -0.5 - } + val groundDrag = world.getBlock(BlockPosition(this: Entity).offset(Direction.DOWN)).getFriction * drag + setDeltaMovement(getDeltaMovement.multiply(groundDrag, drag * (if (isOnGround) -0.5 else 1), groundDrag)) } } - override def hitByEntity(entity: Entity): Boolean = { + override def skipAttackInteraction(entity: Entity): Boolean = { if (isRunning) { - val direction = new Vec3d(entity.posX - posX, entity.posY + entity.getEyeHeight - posY, entity.posZ - posZ).normalize() - if (!world.isRemote) { + val direction = new Vector3d(entity.getX - getX, entity.getY + entity.getEyeHeight - getY, entity.getZ - getZ).normalize() + if (!world.isClientSide) { if (Settings.get.inputUsername) machine.signal("hit", Double.box(direction.x), Double.box(direction.z), Double.box(direction.y), entity.getName) else machine.signal("hit", Double.box(direction.x), Double.box(direction.z), Double.box(direction.y)) } - motionX = (motionX - direction.x) * 0.5f - motionY = (motionY - direction.y) * 0.5f - motionZ = (motionZ - direction.z) * 0.5f + setDeltaMovement(getDeltaMovement.subtract(direction).scale(0.5)) } - super.hitByEntity(entity) + super.skipAttackInteraction(entity) } - override def processInitialInteract(player: EntityPlayer, hand: EnumHand): Boolean = { - if (isDead) return false - if (player.isSneaking) { - if (Wrench.isWrench(player.getHeldItemMainhand)) { - if(!world.isRemote) { + override def interact(player: PlayerEntity, hand: Hand): ActionResultType = { + if (!isAlive) return ActionResultType.PASS + if (player.isCrouching) { + if (Wrench.isWrench(player.getItemInHand(Hand.MAIN_HAND))) { + if(!world.isClientSide) { outOfWorld() } } - else if (!world.isRemote && !machine.isRunning) { + else if (!world.isClientSide && !machine.isRunning) { start() } } - else if (!world.isRemote) { - player.openGui(OpenComputers, GuiType.Drone.id, world, getEntityId, 0, 0) + else if (!world.isClientSide) { + OpenComputers.openGui(player, GuiType.Drone.id, world, getId, 0, 0) } - true + ActionResultType.sidedSuccess(world.isClientSide) } // No step sounds. Except on that one day. - override def playStepSound(pos: BlockPos, block: Block): Unit = { - if (EventHandler.isItTime) super.playStepSound(pos, block) + override def playStepSound(pos: BlockPos, state: BlockState): Unit = { + if (EventHandler.isItTime) super.playStepSound(pos, state) } // ----------------------------------------------------------------------- // private var isChangingDimension = false - override def changeDimension(dimension: Int): Entity = { + override def changeDimension(dimension: ServerWorld): Entity = { // Store relative target as target, to allow adding that in our "new self" // (entities get re-created after changing dimension). - targetX = (targetX - posX).toFloat - targetY = (targetY - posY).toFloat - targetZ = (targetZ - posZ).toFloat + targetX = (targetX - getX).toFloat + targetY = (targetY - getY).toFloat + targetZ = (targetZ - getZ).toFloat try { isChangingDimension = true super.changeDimension(dimension) } finally { isChangingDimension = false - setDead() // Again, to actually close old machine state after copying it. + remove() // Again, to actually close old machine state after copying it. } } - override def copyDataFromOld(entity: Entity): Unit = { - super.copyDataFromOld(entity) + override def restoreFrom(entity: Entity): Unit = { + super.restoreFrom(entity) // Compute relative target based on old position and update, because our // frame of reference most certainly changed (i.e. we'll spawn at different // coordinates than the ones we started traveling from, e.g. when porting // to the nether it'll be oldpos / 8). entity match { case drone: Drone => - targetX = (posX + drone.targetX).toFloat - targetY = (posY + drone.targetY).toFloat - targetZ = (posZ + drone.targetZ).toFloat + targetX = (getX + drone.targetX).toFloat + targetY = (getY + drone.targetY).toFloat + targetZ = (getZ + drone.targetZ).toFloat case _ => - targetX = posX.toFloat - targetY = posY.toFloat - targetZ = posZ.toFloat + targetX = getX.toFloat + targetY = getY.toFloat + targetZ = getZ.toFloat } } - override def setDead() { - super.setDead() - if (!world.isRemote && !isChangingDimension) { + override def remove() { + super.remove() + if (!world.isClientSide && !isChangingDimension) { machine.stop() machine.node.remove() components.disconnectComponents() @@ -556,34 +566,31 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D } override def outOfWorld(): Unit = { - if (isDead) return + if (!isAlive) return super.outOfWorld() - if (!world.isRemote) { + if (!world.isClientSide) { val stack = api.Items.get(Constants.ItemName.Drone).createItemStack(1) info.storedEnergy = control.node.localBuffer.toInt - info.save(stack) - val entity = new EntityItem(world, posX, posY, posZ, stack) - entity.setPickupDelay(15) - world.spawnEntity(entity) + info.saveData(stack) + val entity = new ItemEntity(world, getX, getY, getZ, stack) + entity.setPickUpDelay(15) + world.addFreshEntity(entity) InventoryUtils.dropAllSlots(BlockPosition(this: Entity), mainInventory) } } - override def getName: String = Localization.localizeImmediately("entity.oc.Drone.name") + override def getName: ITextComponent = Localization.localizeLater("entity.oc.Drone.name") - override def handleWaterMovement(): Boolean = { - inWater = world.handleMaterialAcceleration(getEntityBoundingBox, Material.WATER, this) - inWater - } + override protected def getAddEntityPacket = NetworkHooks.getEntitySpawningPacket(this) - override def readEntityFromNBT(nbt: NBTTagCompound) { - info.load(nbt.getCompoundTag("info")) + override protected def readAdditionalSaveData(nbt: CompoundNBT) { + info.loadData(nbt.getCompound("info")) inventorySize = computeInventorySize() - if (!world.isRemote) { - machine.load(nbt.getCompoundTag("machine")) - control.load(nbt.getCompoundTag("control")) - components.load(nbt.getCompoundTag("components")) - mainInventory.load(nbt.getCompoundTag("inventory")) + if (!world.isClientSide) { + machine.loadData(nbt.getCompound("machine")) + control.loadData(nbt.getCompound("control")) + components.loadData(nbt.getCompound("components")) + mainInventory.loadData(nbt.getCompound("inventory")) wireThingsTogether() } @@ -594,35 +601,35 @@ class Drone(world: World) extends Entity(world) with MachineHost with internal.D setSelectedSlot(nbt.getByte("selectedSlot") & 0xFF) setSelectedTank(nbt.getByte("selectedTank") & 0xFF) statusText = nbt.getString("statusText") - lightColor = nbt.getInteger("lightColor") - if (nbt.hasKey("owner")) { + lightColor = nbt.getInt("lightColor") + if (nbt.contains("owner")) { ownerName = nbt.getString("owner") } - if (nbt.hasKey("ownerUuid")) { + if (nbt.contains("ownerUuid")) { ownerUUID = UUID.fromString(nbt.getString("ownerUuid")) } } - override def writeEntityToNBT(nbt: NBTTagCompound) { - if (world.isRemote) return + override protected def addAdditionalSaveData(nbt: CompoundNBT) { + if (world.isClientSide) return components.saveComponents() info.storedEnergy = globalBuffer.toInt - nbt.setNewCompoundTag("info", info.save) - if (!world.isRemote) { - nbt.setNewCompoundTag("machine", machine.save) - nbt.setNewCompoundTag("control", control.save) - nbt.setNewCompoundTag("components", components.save) - nbt.setNewCompoundTag("inventory", mainInventory.save) + nbt.setNewCompoundTag("info", info.saveData) + if (!world.isClientSide) { + nbt.setNewCompoundTag("machine", machine.saveData) + nbt.setNewCompoundTag("control", control.saveData) + nbt.setNewCompoundTag("components", components.saveData) + nbt.setNewCompoundTag("inventory", mainInventory.saveData) } - nbt.setFloat("targetX", targetX) - nbt.setFloat("targetY", targetY) - nbt.setFloat("targetZ", targetZ) - nbt.setFloat("targetAcceleration", targetAcceleration) - nbt.setByte("selectedSlot", selectedSlot.toByte) - nbt.setByte("selectedTank", selectedTank.toByte) - nbt.setString("statusText", statusText) - nbt.setInteger("lightColor", lightColor) - nbt.setString("owner", ownerName) - nbt.setString("ownerUuid", ownerUUID.toString) + nbt.putFloat("targetX", targetX) + nbt.putFloat("targetY", targetY) + nbt.putFloat("targetZ", targetZ) + nbt.putFloat("targetAcceleration", targetAcceleration) + nbt.putByte("selectedSlot", selectedSlot.toByte) + nbt.putByte("selectedTank", selectedTank.toByte) + nbt.putString("statusText", statusText) + nbt.putInt("lightColor", lightColor) + nbt.putString("owner", ownerName) + nbt.putString("ownerUuid", ownerUUID.toString) } } diff --git a/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala index bdb244e210..317296dc87 100644 --- a/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.event import li.cil.oc.api.event.RobotPlaceInAirEvent import li.cil.oc.api.network.Node import li.cil.oc.server.component.UpgradeAngel -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ diff --git a/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala b/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala index 1e52fe2538..fa9cc65c7c 100644 --- a/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala @@ -2,22 +2,22 @@ package li.cil.oc.common.event import li.cil.oc.common.EventHandler import li.cil.oc.util.BlockPosition -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.entity.Entity -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvent import net.minecraft.util.math.BlockPos -import net.minecraft.world.IWorldEventListener import net.minecraft.world.World import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.mutable /** * @author Vexatos */ +@Deprecated object BlockChangeHandler { def addListener(listener: ChangeListener, coord: BlockPosition) = { @@ -30,44 +30,8 @@ object BlockChangeHandler { private val changeListeners = mutable.WeakHashMap.empty[ChangeListener, BlockPosition] - @SubscribeEvent - def onWorldLoad(e: WorldEvent.Load) { - e.getWorld.addEventListener(new Listener(e.getWorld)) - } - trait ChangeListener { def onBlockChanged() } - private class Listener(world: World) extends IWorldEventListener { - override def notifyBlockUpdate(worldIn: World, pos: BlockPos, oldState: IBlockState, newState: IBlockState, flags: Int): Unit = { - val current = BlockPosition(pos, world) - for ((listener, coord) <- changeListeners) if (coord.equals(current)) { - listener.onBlockChanged() - } - } - - override def spawnParticle(id: Int, ignoreRange: Boolean, p_190570_3_ : Boolean, x: Double, y: Double, z: Double, xSpeed: Double, ySpeed: Double, zSpeed: Double, parameters: Int*): Unit = {} - - override def playRecord(soundIn: SoundEvent, pos: BlockPos): Unit = {} - - override def playEvent(player: EntityPlayer, `type`: Int, blockPosIn: BlockPos, data: Int): Unit = {} - - override def onEntityAdded(entityIn: Entity): Unit = {} - - override def spawnParticle(particleID: Int, ignoreRange: Boolean, xCoord: Double, yCoord: Double, zCoord: Double, xOffset: Double, yOffset: Double, zOffset: Double, parameters: Int*): Unit = {} - - override def onEntityRemoved(entityIn: Entity): Unit = {} - - override def broadcastSound(soundID: Int, pos: BlockPos, data: Int): Unit = {} - - override def playSoundToAllNearExcept(player: EntityPlayer, soundIn: SoundEvent, category: SoundCategory, x: Double, y: Double, z: Double, volume: Float, pitch: Float): Unit = {} - - override def markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int): Unit = {} - - override def sendBlockBreakProgress(breakerId: Int, pos: BlockPos, progress: Int): Unit = {} - - override def notifyLightSet(pos: BlockPos): Unit = {} - } - } diff --git a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala index 80395e3bd8..45b434bed2 100644 --- a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala @@ -1,6 +1,6 @@ package li.cil.oc.common.event -import java.util +import java.util.UUID import li.cil.oc.OpenComputers import li.cil.oc.api.event.RobotMoveEvent @@ -8,54 +8,86 @@ import li.cil.oc.server.component.UpgradeChunkloader import li.cil.oc.util.BlockPosition import net.minecraft.util.math.ChunkPos import net.minecraft.world.World -import net.minecraftforge.common.ForgeChunkManager -import net.minecraftforge.common.ForgeChunkManager.LoadingCallback -import net.minecraftforge.common.ForgeChunkManager.Ticket +import net.minecraft.world.ForcedChunksSaveData +import net.minecraft.world.server.ServerWorld +import net.minecraftforge.common.world.ForgeChunkManager +import net.minecraftforge.common.world.ForgeChunkManager.LoadingValidationCallback +import net.minecraftforge.common.world.ForgeChunkManager.TicketHelper import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraft.entity.Entity import scala.collection.convert.WrapAsScala._ +import scala.collection.immutable import scala.collection.mutable -object ChunkloaderUpgradeHandler extends LoadingCallback { - val restoredTickets = mutable.Map.empty[String, Ticket] +object ChunkloaderUpgradeHandler extends LoadingValidationCallback { + private val restoredTickets = mutable.Map.empty[UUID, ChunkPos] - override def ticketsLoaded(tickets: util.List[Ticket], world: World) { - for (ticket <- tickets) { - val data = ticket.getModData - val address = data.getString("address") - restoredTickets += address -> ticket - if (data.hasKey("x") && data.hasKey("z")) { - val x = data.getInteger("x") - val z = data.getInteger("z") - OpenComputers.log.info(s"Restoring chunk loader ticket for upgrade at chunk ($x, $z) with address $address.") + private def parseAddress(addr: String): Option[UUID] = try { + Some(UUID.fromString(addr)) + } + catch { + case _: RuntimeException => None + } - ForgeChunkManager.forceChunk(ticket, new ChunkPos(x, z)) + def claimTicket(addr: String) = parseAddress(addr).flatMap(restoredTickets.remove) + + override def validateTickets(world: ServerWorld, helper: TicketHelper) { + for ((owner, ticketsPair) <- helper.getEntityTickets) { + // This ensures that malformed tickets are also cleared on world save. + restoredTickets += owner -> null + // Chunkloaders use only ticking tickets. + val tickets = ticketsPair.getSecond + if (tickets.size == 9) { + var (minX, minZ, maxX, maxZ) = (0, 0, 0, 0) + for (combinedPos <- tickets) { + val x = ChunkPos.getX(combinedPos) + val z = ChunkPos.getZ(combinedPos) + minX = minX min x + maxX = maxX max x + minZ = minZ min z + maxZ = maxZ max z + } + if (minX + 2 == maxX && minZ + 2 == maxZ) { + val x = minX + 1 + val z = minZ + 1 + OpenComputers.log.info(s"Restoring chunk loader ticket for upgrade at chunk ($x, $z) with address ${owner}.") + restoredTickets += owner -> new ChunkPos(x, z) + } + else { + OpenComputers.log.warn(s"Chunk loader ticket for $owner loads an incorrect shape.") + helper.removeAllTickets(owner) + } + } + else { + OpenComputers.log.warn(s"Chunk loader ticket for $owner loads ${tickets.size} chunks.") + helper.removeAllTickets(owner) } } } @SubscribeEvent - def onWorldSave(e: WorldEvent.Save) { - // Any tickets that were not reassigned by the time the world gets saved - // again can be considered orphaned, so we release them. - // TODO figure out a better event *after* tile entities were restored - // but *before* the world is saved, because the tickets are saved first, - // so if the save is because the game is being quit the tickets aren't - // actually being cleared. This will *usually* not be a problem, but it - // has room for improvement. - restoredTickets.values.foreach(ticket => { - try{ - val data = ticket.getModData - OpenComputers.log.warn(s"A chunk loader ticket has been orphaned! Address: ${data.getString("address")}, position: (${data.getInteger("x")}, ${data.getInteger("z")}). Removing...") - ForgeChunkManager.releaseTicket(ticket) + def onWorldSave(e: WorldEvent.Save) = e.getWorld match { + case world: ServerWorld => { + // Any tickets that were not reassigned by the time the world gets saved + // again can be considered orphaned, so we release them. + // TODO figure out a better event *after* tile entities were restored + // but *before* the world is saved, because the tickets are saved first, + // so if the save is because the game is being quit the tickets aren't + // actually being cleared. This will *usually* not be a problem, but it + // has room for improvement. + for ((owner, pos) <- restoredTickets) { + try { + OpenComputers.log.warn(s"A chunk loader ticket has been orphaned! Address: ${owner}, position: (${pos.x}, ${pos.z}). Removing...") + releaseTicket(world, owner.toString, pos) + } + catch { + case err: Throwable => OpenComputers.log.error(err) + } } - catch { - case _: Throwable => // Ignored. - } - }) - restoredTickets.clear() + restoredTickets.clear() + } } // Note: it might be necessary to use pre move to force load the target chunk @@ -74,23 +106,36 @@ object ChunkloaderUpgradeHandler extends LoadingCallback { }) } - def updateLoadedChunk(loader: UpgradeChunkloader) { - val blockPos = BlockPosition(loader.host) - val centerChunk = new ChunkPos(blockPos.x >> 4, blockPos.z >> 4) - val robotChunks = (for (x <- -1 to 1; z <- -1 to 1) yield new ChunkPos(centerChunk.x + x, centerChunk.z + z)).toSet - - loader.ticket.foreach(ticket => { - ticket.getChunkList.collect { - case chunk: ChunkPos if !robotChunks.contains(chunk) => ForgeChunkManager.unforceChunk(ticket, chunk) + def releaseTicket(world: ServerWorld, addr: String, pos: ChunkPos): Unit = parseAddress(addr) match { + case Some(uuid) => { + for (x <- -1 to 1; z <- -1 to 1) { + ForgeChunkManager.forceChunk(world, OpenComputers.ID, uuid, pos.x + x, pos.z + z, false, true) } + } + case _ => OpenComputers.log.warn("Address '$addr' could not be parsed") + } - for (chunk <- robotChunks) { - ForgeChunkManager.forceChunk(ticket, chunk) + def updateLoadedChunk(loader: UpgradeChunkloader) { + (loader.host.world, parseAddress(loader.node.address)) match { + // If loader.ticket is None that means we shouldn't load anything (as did the old ticketing system). + case (world: ServerWorld, Some(owner)) if loader.ticket.isDefined => { + val blockPos = BlockPosition(loader.host) + val centerChunk = new ChunkPos(blockPos.x >> 4, blockPos.z >> 4) + if (centerChunk != loader.ticket.get) { + val robotChunks = (for (x <- -1 to 1; z <- -1 to 1) yield new ChunkPos(centerChunk.x + x, centerChunk.z + z)).toSet + val existingChunks = loader.ticket match { + case Some(currPos) => (for (x <- -1 to 1; z <- -1 to 1) yield new ChunkPos(currPos.x + x, currPos.z + z)).toSet + case None => immutable.Set.empty[ChunkPos] + } + for (toRemove <- existingChunks if !robotChunks.contains(toRemove)) { + ForgeChunkManager.forceChunk(world, OpenComputers.ID, owner, toRemove.x, toRemove.z, false, true) + } + for (toAdd <- robotChunks if !existingChunks.contains(toAdd)) { + ForgeChunkManager.forceChunk(world, OpenComputers.ID, owner, toAdd.x, toAdd.z, true, true) + } + loader.ticket = Some(centerChunk) + } } - - ticket.getModData.setString("address", loader.node.address) - ticket.getModData.setInteger("x", centerChunk.x) - ticket.getModData.setInteger("z", centerChunk.z) - }) + } } } diff --git a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala index 7e61d8735a..1c712ced30 100644 --- a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.event +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api.event._ @@ -7,8 +8,8 @@ import li.cil.oc.api.internal.Agent import li.cil.oc.api.internal.Robot import li.cil.oc.api.network.Node import li.cil.oc.server.component -import net.minecraft.client.renderer.GlStateManager -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraft.util.Util +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ @@ -18,7 +19,7 @@ object ExperienceUpgradeHandler { val (level, experience) = getLevelAndExperience(e.agent) // This is basically a 'does it have an experience upgrade' check. if (experience != 0.0) { - e.player.sendMessage(Localization.Analyzer.RobotXp(experience, level)) + e.player.sendMessage(Localization.Analyzer.RobotXp(experience, level), Util.NIL_UUID) } } @@ -37,7 +38,7 @@ object ExperienceUpgradeHandler { def onRobotAttackEntityPost(e: RobotAttackEntityEvent.Post) { e.agent match { case robot: Robot => - if (robot.equipmentInventory.getStackInSlot(0) != null && e.target.isDead) { + if (robot.equipmentInventory.getItem(0) != null && !e.target.isAlive) { addExperience(robot, Settings.get.robotActionXp) } case _ => @@ -69,7 +70,7 @@ object ExperienceUpgradeHandler { val level = e.agent match { case robot: Robot => var acc = 0 - for (index <- 0 until robot.getSizeInventory) { + for (index <- 0 until robot.getContainerSize) { robot.getComponentInSlot(index) match { case upgrade: component.UpgradeExperience => acc += upgrade.level @@ -80,13 +81,13 @@ object ExperienceUpgradeHandler { case _ => 0 } if (level > 19) { - GlStateManager.color(0.4f, 1, 1) + RenderSystem.color3f(0.4f, 1, 1) } else if (level > 9) { - GlStateManager.color(1, 1, 0.4f) + RenderSystem.color3f(1, 1, 0.4f) } else { - GlStateManager.color(0.5f, 0.5f, 0.5f) + RenderSystem.color3f(0.5f, 0.5f, 0.5f) } } diff --git a/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala b/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala index a4f2ddb37a..1c9dbcacde 100644 --- a/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala @@ -11,14 +11,14 @@ import li.cil.oc.server.component.Server import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent object FileSystemAccessHandler { @SubscribeEvent def onFileSystemAccess(e: FileSystemAccessEvent.Server) { - e.getTileEntity match { + e.getBlockEntity match { case t: Rack => - for (slot <- 0 until t.getSizeInventory) { + for (slot <- 0 until t.getContainerSize) { t.getMountable(slot) match { case server: Server => val containsNode = server.componentSlot(e.getNode.address) >= 0 @@ -43,8 +43,8 @@ object FileSystemAccessHandler { def onFileSystemAccess(e: FileSystemAccessEvent.Client) { val volume = Settings.get.soundVolume val sound = new SoundEvent(new ResourceLocation(e.getSound)) - e.getWorld.playSound(e.getX, e.getY, e.getZ, sound, SoundCategory.BLOCKS, volume, 1, false) - e.getTileEntity match { + e.getWorld.playLocalSound(e.getX, e.getY, e.getZ, sound, SoundCategory.BLOCKS, volume, 1, false) + e.getBlockEntity match { case t: DiskDrive => t.lastAccess = System.currentTimeMillis() case t: Case => t.lastFileSystemAccess = System.currentTimeMillis() case t: Raid => t.lastAccess = System.currentTimeMillis() diff --git a/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala b/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala index d990c8f341..79abab6235 100644 --- a/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala @@ -1,27 +1,28 @@ package li.cil.oc.common.event +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.Settings import li.cil.oc.common.item.HoverBoots -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.entity.living.LivingEvent.LivingJumpEvent import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent import net.minecraftforge.event.entity.living.LivingFallEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ object HoverBootsHandler { @SubscribeEvent def onLivingUpdate(e: LivingUpdateEvent): Unit = e.getEntity match { - case player: EntityPlayer if !player.isInstanceOf[FakePlayer] => - val nbt = player.getEntityData + case player: PlayerEntity if !player.isInstanceOf[FakePlayer] => + val nbt = player.getPersistentData val hadHoverBoots = nbt.getBoolean(Settings.namespace + "hasHoverBoots") - val hasHoverBoots = !player.isSneaking && equippedArmor(player).exists(stack => stack.getItem match { + val hasHoverBoots = !player.isCrouching && equippedArmor(player).exists(stack => stack.getItem match { case boots: HoverBoots => Settings.get.ignorePower || { - if (player.onGround && !player.capabilities.isCreativeMode && player.world.getTotalWorldTime % Settings.get.tickFrequency == 0) { - val velocity = player.motionX * player.motionX + player.motionY * player.motionY + player.motionZ * player.motionZ + if (player.isOnGround && !player.isCreative && player.level.getGameTime % Settings.get.tickFrequency == 0) { + val velocity = player.getDeltaMovement.lengthSqr if (velocity > 0.015f) { boots.charge(stack, -Settings.get.hoverBootMove, simulate = false) } @@ -31,29 +32,30 @@ object HoverBootsHandler { case _ => false }) if (hasHoverBoots != hadHoverBoots) { - nbt.setBoolean(Settings.namespace + "hasHoverBoots", hasHoverBoots) - player.stepHeight = if (hasHoverBoots) 1f else 0.5f + nbt.putBoolean(Settings.namespace + "hasHoverBoots", hasHoverBoots) + player.maxUpStep = if (hasHoverBoots) 1f else 0.5f } - if (hasHoverBoots && !player.onGround && player.fallDistance < 5 && player.motionY < 0) { - player.motionY *= 0.9f + if (hasHoverBoots && !player.isOnGround && player.fallDistance < 5 && player.getDeltaMovement.y < 0) { + player.setDeltaMovement(player.getDeltaMovement.multiply(1, 0.9, 1)) } case _ => // Ignore. } @SubscribeEvent def onLivingJump(e: LivingJumpEvent): Unit = e.getEntity match { - case player: EntityPlayer if !player.isInstanceOf[FakePlayer] && !player.isSneaking => + case player: PlayerEntity if !player.isInstanceOf[FakePlayer] && !player.isCrouching => equippedArmor(player).collectFirst { case stack if stack.getItem.isInstanceOf[HoverBoots] => val boots = stack.getItem.asInstanceOf[HoverBoots] val hoverJumpCost = -Settings.get.hoverBootJump - val isCreative = Settings.get.ignorePower || player.capabilities.isCreativeMode + val isCreative = Settings.get.ignorePower || player.isCreative if (isCreative || boots.charge(stack, hoverJumpCost, simulate = true) == 0) { if (!isCreative) boots.charge(stack, hoverJumpCost, simulate = false) + val motion = player.getDeltaMovement if (player.isSprinting) - player.addVelocity(player.motionX * 0.5, 0.4, player.motionZ * 0.5) + player.push(motion.x * 0.5, 0.4, motion.z * 0.5) else - player.addVelocity(0, 0.4, 0) + player.push(0, 0.4, 0) } } case _ => // Ignore. @@ -61,12 +63,12 @@ object HoverBootsHandler { @SubscribeEvent def onLivingFall(e: LivingFallEvent): Unit = if (e.getDistance > 3) e.getEntity match { - case player: EntityPlayer if !player.isInstanceOf[FakePlayer] => + case player: PlayerEntity if !player.isInstanceOf[FakePlayer] => equippedArmor(player).collectFirst { case stack if stack.getItem.isInstanceOf[HoverBoots] => val boots = stack.getItem.asInstanceOf[HoverBoots] val hoverFallCost = -Settings.get.hoverBootAbsorb - val isCreative = Settings.get.ignorePower || player.capabilities.isCreativeMode + val isCreative = Settings.get.ignorePower || player.isCreative if (isCreative || boots.charge(stack, hoverFallCost, simulate = true) == 0) { if (!isCreative) boots.charge(stack, hoverFallCost, simulate = false) e.setDistance(e.getDistance * 0.3f) @@ -75,5 +77,5 @@ object HoverBootsHandler { case _ => // Ignore. } - private def equippedArmor(player: EntityPlayer) = player.getArmorInventoryList.filter(!_.isEmpty) + private def equippedArmor(player: PlayerEntity) = player.inventory.armor.filter(!_.isEmpty) } diff --git a/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala index 05238940cf..941a310d41 100644 --- a/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.event import java.io.FileInputStream import java.io.FileOutputStream +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api @@ -11,18 +12,17 @@ import li.cil.oc.client.Textures import li.cil.oc.common.EventHandler import li.cil.oc.common.nanomachines.ControllerImpl import net.minecraft.client.Minecraft -import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.client.event.RenderGameOverlayEvent import net.minecraftforge.event.entity.living.LivingEvent import net.minecraftforge.event.entity.player.PlayerEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent -import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerRespawnEvent import org.lwjgl.opengl.GL11 object NanomachinesHandler { @@ -31,14 +31,15 @@ object NanomachinesHandler { @SubscribeEvent def onRenderGameOverlay(e: RenderGameOverlayEvent.Post): Unit = { if (e.getType == RenderGameOverlayEvent.ElementType.TEXT) { - val mc = Minecraft.getMinecraft + val mc = Minecraft.getInstance api.Nanomachines.getController(mc.player) match { case controller: Controller => - val res = new ScaledResolution(mc) + val stack = e.getMatrixStack + val window = mc.getWindow val sizeX = 8 val sizeY = 12 - val width = res.getScaledWidth - val height = res.getScaledHeight + val width = window.getGuiScaledWidth + val height = window.getGuiScaledHeight val (x, y) = Settings.get.nanomachineHudPos val left = math.min(width - sizeX, @@ -51,33 +52,33 @@ object NanomachinesHandler { else if (y < 1) y * height else y) val fill = controller.getLocalBuffer / controller.getLocalBufferSize - Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.GUI.Nanomachines) - drawRect(left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY) - Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.GUI.NanomachinesBar) - drawRect(left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY, fill) + Minecraft.getInstance.getTextureManager.bind(Textures.GUI.Nanomachines) + drawRect(stack, left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY) + Minecraft.getInstance.getTextureManager.bind(Textures.GUI.NanomachinesBar) + drawRect(stack, left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY, fill.toFloat) case _ => // Nothing to show. } } } - private def drawRect(x: Int, y: Int, w: Int, h: Int, tw: Int, th: Int, fill: Double = 1) { + private def drawRect(stack: MatrixStack, x: Int, y: Int, w: Int, h: Int, tw: Int, th: Int, fill: Float = 1) { val sx = 1f / tw val sy = 1f / th val t = Tessellator.getInstance - val r = t.getBuffer + val r = t.getBuilder r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.pos(x, y + h, 0).tex(0, h * sy).endVertex() - r.pos(x + w, y + h, 0).tex(w * sx, h * sy).endVertex() - r.pos(x + w, y + h * (1 - fill), 0).tex(w * sx, 1 - fill).endVertex() - r.pos(x, y + h * (1 - fill), 0).tex(0, 1 - fill).endVertex() - t.draw() + r.vertex(stack.last.pose, x, y + h, 0).uv(0, h * sy).endVertex() + r.vertex(stack.last.pose, x + w, y + h, 0).uv(w * sx, h * sy).endVertex() + r.vertex(stack.last.pose, x + w, y + h * (1 - fill), 0).uv(w * sx, 1 - fill).endVertex() + r.vertex(stack.last.pose, x, y + h * (1 - fill), 0).uv(0, 1 - fill).endVertex() + t.end() } } object Common { @SubscribeEvent def onPlayerRespawn(e: PlayerRespawnEvent): Unit = { - api.Nanomachines.getController(e.player) match { + api.Nanomachines.getController(e.getPlayer) match { case controller: Controller => controller.changeBuffer(-controller.getLocalBuffer) case _ => // Not a player with nanomachines. } @@ -86,19 +87,19 @@ object NanomachinesHandler { @SubscribeEvent def onLivingUpdate(e: LivingEvent.LivingUpdateEvent): Unit = { e.getEntity match { - case player: EntityPlayer => api.Nanomachines.getController(player) match { + case player: PlayerEntity => api.Nanomachines.getController(player) match { case controller: ControllerImpl => if (controller.player eq player) { controller.update() } else { // Player entity instance changed (e.g. respawn), recreate the controller. - val nbt = new NBTTagCompound() - controller.save(nbt) + val nbt = new CompoundNBT() + controller.saveData(nbt) api.Nanomachines.uninstallController(controller.player) api.Nanomachines.installController(player) match { case newController: ControllerImpl => - newController.load(nbt) + newController.loadData(nbt) newController.reset() case _ => // Eh? } @@ -112,11 +113,11 @@ object NanomachinesHandler { @SubscribeEvent def onPlayerSave(e: PlayerEvent.SaveToFile): Unit = { val file = e.getPlayerFile("ocnm") - api.Nanomachines.getController(e.getEntityPlayer) match { + api.Nanomachines.getController(e.getPlayer) match { case controller: ControllerImpl => try { - val nbt = new NBTTagCompound() - controller.save(nbt) + val nbt = new CompoundNBT() + controller.saveData(nbt) val fos = new FileOutputStream(file) try CompressedStreamTools.writeCompressed(nbt, fos) catch { case t: Throwable => @@ -136,11 +137,11 @@ object NanomachinesHandler { def onPlayerLoad(e: PlayerEvent.LoadFromFile): Unit = { val file = e.getPlayerFile("ocnm") if (file.exists()) { - api.Nanomachines.getController(e.getEntityPlayer) match { + api.Nanomachines.getController(e.getPlayer) match { case controller: ControllerImpl => try { val fis = new FileInputStream(file) - try controller.load(CompressedStreamTools.readCompressed(fis)) catch { + try controller.loadData(CompressedStreamTools.readCompressed(fis)) catch { case t: Throwable => OpenComputers.log.warn("Error loading nanomachine state.", t) } @@ -157,10 +158,10 @@ object NanomachinesHandler { @SubscribeEvent def onPlayerDisconnect(e: PlayerLoggedOutEvent): Unit = { - api.Nanomachines.getController(e.player) match { + api.Nanomachines.getController(e.getPlayer) match { case controller: ControllerImpl => // Wait a tick because saving is done after this event. - EventHandler.scheduleServer(() => api.Nanomachines.uninstallController(e.player)) + EventHandler.scheduleServer(() => api.Nanomachines.uninstallController(e.getPlayer)) case _ => // Not a player with nanomachines. } } diff --git a/src/main/scala/li/cil/oc/common/event/NetworkActivityHandler.scala b/src/main/scala/li/cil/oc/common/event/NetworkActivityHandler.scala index 934b851ce8..6a10705bf1 100644 --- a/src/main/scala/li/cil/oc/common/event/NetworkActivityHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/NetworkActivityHandler.scala @@ -4,14 +4,14 @@ import li.cil.oc.api.event.NetworkActivityEvent import li.cil.oc.api.internal.Rack import li.cil.oc.common.tileentity.Case import li.cil.oc.server.component.Server -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent object NetworkActivityHandler { @SubscribeEvent def onNetworkActivity(e: NetworkActivityEvent.Server) { - e.getTileEntity match { + e.getBlockEntity match { case t: Rack => - for (slot <- 0 until t.getSizeInventory) { + for (slot <- 0 until t.getContainerSize) { t.getMountable(slot) match { case server: Server => val containsNode = server.componentSlot(e.getNode.address) >= 0 @@ -28,7 +28,7 @@ object NetworkActivityHandler { @SubscribeEvent def onNetworkActivity(e: NetworkActivityEvent.Client) { - e.getTileEntity match { + e.getBlockEntity match { case t: Case => t.lastNetworkActivity = System.currentTimeMillis(); case _ => } diff --git a/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala b/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala index 033172a3b4..bf7a11cb7f 100644 --- a/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.event +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Constants import li.cil.oc.api import li.cil.oc.api.event.RackMountableRenderEvent @@ -9,14 +10,14 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.OpenGlHelper -import net.minecraft.client.renderer.block.model.ItemCameraTransforms -import net.minecraft.entity.item.EntityItem +import net.minecraft.client.renderer.model.ItemCameraTransforms +import net.minecraft.entity.item.ItemEntity import net.minecraft.item.ItemStack +import net.minecraft.util.math.vector.Vector3f import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL13 object RackMountableRenderHandler { lazy val DiskDriveMountable = api.Items.get(Constants.ItemName.DiskDriveMountable) @@ -32,30 +33,25 @@ object RackMountableRenderHandler { @SubscribeEvent def onRackMountableRendering(e: RackMountableRenderEvent.TileEntity): Unit = { - if (e.data != null && DiskDriveMountable == api.Items.get(e.rack.getStackInSlot(e.mountable))) { + if (e.data != null && DiskDriveMountable == api.Items.get(e.rack.getItem(e.mountable))) { // Disk drive. - if (e.data.hasKey("disk")) { - val stack = new ItemStack(e.data.getCompoundTag("disk")) + if (e.data.contains("disk")) { + val stack = ItemStack.of(e.data.getCompound("disk")) if (!stack.isEmpty) { - GlStateManager.pushMatrix() - GlStateManager.scale(1, -1, 1) - GlStateManager.translate(10 / 16f, -(3.5f + e.mountable * 3f) / 16f, -2 / 16f) - GlStateManager.rotate(90, -1, 0, 0) - GlStateManager.scale(0.5f, 0.5f, 0.5f) - - val brightness = e.rack.world.getLightBrightnessForSkyBlocks(BlockPosition(e.rack).offset(e.rack.facing), 0) - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness % 65536, brightness / 65536) - - // This is very 'meh', but item frames do it like this, too! - val entity = new EntityItem(e.rack.world, 0, 0, 0, stack) - entity.hoverStart = 0 - Minecraft.getMinecraft.getRenderItem.renderItem(entity.getItem, ItemCameraTransforms.TransformType.FIXED) - GlStateManager.popMatrix() + val matrix = e.stack + matrix.pushPose() + matrix.scale(1, -1, 1) + matrix.translate(10 / 16f, -(3.5f + e.mountable * 3f) / 16f, -2 / 16f) + matrix.mulPose(Vector3f.XN.rotationDegrees(90)) + matrix.scale(0.5f, 0.5f, 0.5f) + + Minecraft.getInstance.getItemRenderer.renderStatic(stack, ItemCameraTransforms.TransformType.FIXED, e.light, e.overlay, matrix, e.typeBuffer) + matrix.popPose() } } - if (System.currentTimeMillis() - e.data.getLong("lastAccess") < 400 && e.rack.world.rand.nextDouble() > 0.1) { + if (System.currentTimeMillis() - e.data.getLong("lastAccess") < 400 && e.rack.world.random.nextDouble() > 0.1) { RenderState.disableEntityLighting() RenderState.makeItBlend() @@ -65,7 +61,7 @@ object RackMountableRenderHandler { RenderState.enableEntityLighting() } } - else if (e.data != null && Servers.contains(api.Items.get(e.rack.getStackInSlot(e.mountable)))) { + else if (e.data != null && Servers.contains(api.Items.get(e.rack.getItem(e.mountable)))) { // Server. RenderState.disableEntityLighting() RenderState.makeItBlend() @@ -76,7 +72,7 @@ object RackMountableRenderHandler { if (e.data.getBoolean("hasErrored") && RenderUtil.shouldShowErrorLight(e.rack.hashCode * (e.mountable + 1))) { e.renderOverlayFromAtlas(Textures.Block.RackServerError) } - if (System.currentTimeMillis() - e.data.getLong("lastFileSystemAccess") < 400 && e.rack.world.rand.nextDouble() > 0.1) { + if (System.currentTimeMillis() - e.data.getLong("lastFileSystemAccess") < 400 && e.rack.world.random.nextDouble() > 0.1) { e.renderOverlayFromAtlas(Textures.Block.RackServerActivity) } if ((System.currentTimeMillis() - e.data.getLong("lastNetworkActivity") < 300 && System.currentTimeMillis() % 200 > 100) && e.data.getBoolean("isRunning")) { @@ -86,13 +82,13 @@ object RackMountableRenderHandler { RenderState.disableBlend() RenderState.enableEntityLighting() } - else if (e.data != null && TerminalServer == api.Items.get(e.rack.getStackInSlot(e.mountable))) { + else if (e.data != null && TerminalServer == api.Items.get(e.rack.getItem(e.mountable))) { // Terminal server. RenderState.disableEntityLighting() RenderState.makeItBlend() e.renderOverlayFromAtlas(Textures.Block.RackTerminalServerOn) - val countConnected = e.data.getTagList("keys", NBT.TAG_STRING).tagCount() + val countConnected = e.data.getList("keys", NBT.TAG_STRING).size() if (countConnected > 0) { val u0 = 7 / 16f @@ -107,15 +103,15 @@ object RackMountableRenderHandler { @SubscribeEvent def onRackMountableRendering(e: RackMountableRenderEvent.Block): Unit = { - if (DiskDriveMountable == api.Items.get(e.rack.getStackInSlot(e.mountable))) { + if (DiskDriveMountable == api.Items.get(e.rack.getItem(e.mountable))) { // Disk drive. e.setFrontTextureOverride(Textures.getSprite(Textures.Block.RackDiskDrive)) } - else if (Servers.contains(api.Items.get(e.rack.getStackInSlot(e.mountable)))) { + else if (Servers.contains(api.Items.get(e.rack.getItem(e.mountable)))) { // Server. e.setFrontTextureOverride(Textures.getSprite(Textures.Block.RackServer)) } - else if (TerminalServer == api.Items.get(e.rack.getStackInSlot(e.mountable))) { + else if (TerminalServer == api.Items.get(e.rack.getItem(e.mountable))) { // Terminal server. e.setFrontTextureOverride(Textures.getSprite(Textures.Block.RackTerminalServer)) } diff --git a/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala b/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala index ed0485591e..16ec084d34 100644 --- a/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala @@ -9,20 +9,20 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.UpgradeHover import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraft.util.Direction +import net.minecraftforge.eventbus.api.SubscribeEvent object RobotCommonHandler { @SubscribeEvent def onRobotApplyDamageRate(e: RobotUsedToolEvent.ApplyDamageRate) { e.agent match { case robot: internal.Robot => - if (e.toolAfterUse.isItemStackDamageable) { - val damage = e.toolAfterUse.getItemDamage - e.toolBeforeUse.getItemDamage + if (e.toolAfterUse.isDamageableItem) { + val damage = e.toolAfterUse.getDamageValue - e.toolBeforeUse.getDamageValue if (damage > 0) { val actualDamage = damage * e.getDamageRate - val repairedDamage = if (e.agent.player.getRNG.nextDouble() > 0.5) damage - math.floor(actualDamage).toInt else damage - math.ceil(actualDamage).toInt - e.toolAfterUse.setItemDamage(e.toolAfterUse.getItemDamage - repairedDamage) + val repairedDamage = if (e.agent.player.getRandom.nextDouble() > 0.5) damage - math.floor(actualDamage).toInt else damage - math.ceil(actualDamage).toInt + e.toolAfterUse.setDamageValue(e.toolAfterUse.getDamageValue - repairedDamage) } } case _ => @@ -36,20 +36,23 @@ object RobotCommonHandler { val world = robot.world var maxFlyingHeight = Settings.get.limitFlightHeight - (0 until robot.equipmentInventory.getSizeInventory). - map(robot.equipmentInventory.getStackInSlot). + (0 until robot.equipmentInventory.getContainerSize). + map(robot.equipmentInventory.getItem). map(Delegator.subItem). collect { case Some(item: UpgradeHover) => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } (0 until robot.componentCount). - map(_ + robot.mainInventory.getSizeInventory + robot.equipmentInventory.getSizeInventory). - map(robot.getStackInSlot). - map(Delegator.subItem). + map(_ + robot.mainInventory.getContainerSize + robot.equipmentInventory.getContainerSize). + map(robot.getItem(_)). + map(Delegator.subItem(_)). collect { case Some(item: UpgradeHover) => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } - def isMovingDown = e.direction == EnumFacing.DOWN - def hasAdjacentBlock(pos: BlockPosition) = EnumFacing.values.exists(side => world.isSideSolid(pos.offset(side), side.getOpposite)) - def isWithinFlyingHeight(pos: BlockPosition) = maxFlyingHeight >= world.getHeight || (1 to maxFlyingHeight).exists(n => !world.isAirBlock(pos.offset(EnumFacing.DOWN, n))) + def isMovingDown = e.direction == Direction.DOWN + def hasAdjacentBlock(pos: BlockPosition) = Direction.values.exists(side => { + val sidePos = pos.offset(side).toBlockPos + world.getBlockState(sidePos).isFaceSturdy(world, sidePos, side.getOpposite) + }) + def isWithinFlyingHeight(pos: BlockPosition) = maxFlyingHeight >= world.getHeight() || (1 to maxFlyingHeight).exists(n => !world.isAirBlock(pos.offset(Direction.DOWN, n))) val startPos = BlockPosition(robot) val targetPos = startPos.offset(e.direction) // New movement rules as of 1.5: diff --git a/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala b/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala index 1075e28949..43dbe8a1d4 100644 --- a/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.event import li.cil.oc.api import li.cil.oc.api.event.RobotMoveEvent import li.cil.oc.server.component.WirelessNetworkCard -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index c37b790b08..f16fff3197 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -4,68 +4,39 @@ import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.common.block._ -import li.cil.oc.common.recipe.Recipes import li.cil.oc.common.tileentity import net.minecraft.tileentity.TileEntity import net.minecraftforge.fml.common.registry.GameRegistry object Blocks { def init() { - registerTileEntity(classOf[tileentity.Adapter], Settings.namespace + "adapter") - registerTileEntity(classOf[tileentity.Assembler], Settings.namespace + "assembler") - registerTileEntity(classOf[tileentity.Cable], Settings.namespace + "cable") - registerTileEntity(classOf[tileentity.Capacitor], Settings.namespace + "capacitor") - registerTileEntity(classOf[tileentity.CarpetedCapacitor], Settings.namespace + "carpetedCapacitor") - registerTileEntity(classOf[tileentity.Case], Settings.namespace + "case") - registerTileEntity(classOf[tileentity.Charger], Settings.namespace + "charger") - registerTileEntity(classOf[tileentity.DiskDrive], Settings.namespace + "diskDrive") - registerTileEntity(classOf[tileentity.Disassembler], Settings.namespace + "disassembler") - registerTileEntity(classOf[tileentity.Keyboard], Settings.namespace + "keyboard") - registerTileEntity(classOf[tileentity.Hologram], Settings.namespace + "hologram") - registerTileEntity(classOf[tileentity.Geolyzer], Settings.namespace + "geolyzer") - registerTileEntity(classOf[tileentity.Microcontroller], Settings.namespace + "microcontroller") - registerTileEntity(classOf[tileentity.MotionSensor], Settings.namespace + "motionSensor") - registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter") - registerTileEntity(classOf[tileentity.PowerConverter], Settings.namespace + "powerConverter") - registerTileEntity(classOf[tileentity.PowerDistributor], Settings.namespace + "powerDistributor") - registerTileEntity(classOf[tileentity.Print], Settings.namespace + "print") - registerTileEntity(classOf[tileentity.Printer], Settings.namespace + "printer") - registerTileEntity(classOf[tileentity.Raid], Settings.namespace + "raid") - registerTileEntity(classOf[tileentity.Redstone], Settings.namespace + "redstone") - registerTileEntity(classOf[tileentity.Relay], Settings.namespace + "relay") - registerTileEntity(classOf[tileentity.RobotProxy], Settings.namespace + "robot") - registerTileEntity(classOf[tileentity.Screen], Settings.namespace + "screen") - registerTileEntity(classOf[tileentity.Rack], Settings.namespace + "rack") - registerTileEntity(classOf[tileentity.Transposer], Settings.namespace + "transposer") - registerTileEntity(classOf[tileentity.Waypoint], Settings.namespace + "waypoint") - - Recipes.addBlock(new Adapter(), Constants.BlockName.Adapter, "oc:adapter") - Recipes.addBlock(new Assembler(), Constants.BlockName.Assembler, "oc:assembler") - Recipes.addBlock(new Cable(), Constants.BlockName.Cable, "oc:cable") - Recipes.addBlock(new Capacitor(), Constants.BlockName.Capacitor, "oc:capacitor") - Recipes.addBlock(new Case(Tier.One), Constants.BlockName.CaseTier1, "oc:case1") - Recipes.addBlock(new Case(Tier.Three), Constants.BlockName.CaseTier3, "oc:case3") - Recipes.addBlock(new Case(Tier.Two), Constants.BlockName.CaseTier2, "oc:case2") - Recipes.addBlock(new ChameliumBlock(), Constants.BlockName.ChameliumBlock, "oc:chameliumBlock") - Recipes.addBlock(new Charger(), Constants.BlockName.Charger, "oc:charger") - Recipes.addBlock(new Disassembler(), Constants.BlockName.Disassembler, "oc:disassembler") - Recipes.addBlock(new DiskDrive(), Constants.BlockName.DiskDrive, "oc:diskDrive") - Recipes.addBlock(new Geolyzer(), Constants.BlockName.Geolyzer, "oc:geolyzer") - Recipes.addBlock(new Hologram(Tier.One), Constants.BlockName.HologramTier1, "oc:hologram1") - Recipes.addBlock(new Hologram(Tier.Two), Constants.BlockName.HologramTier2, "oc:hologram2") - Recipes.addBlock(new Keyboard(), Constants.BlockName.Keyboard, "oc:keyboard") - Recipes.addBlock(new MotionSensor(), Constants.BlockName.MotionSensor, "oc:motionSensor") - Recipes.addBlock(new PowerConverter(), Constants.BlockName.PowerConverter, "oc:powerConverter") - Recipes.addBlock(new PowerDistributor(), Constants.BlockName.PowerDistributor, "oc:powerDistributor") - Recipes.addBlock(new Printer(), Constants.BlockName.Printer, "oc:printer") - Recipes.addBlock(new Raid(), Constants.BlockName.Raid, "oc:raid") - Recipes.addBlock(new Redstone(), Constants.BlockName.Redstone, "oc:redstone") - Recipes.addBlock(new Relay(), Constants.BlockName.Relay, "oc:relay") - Recipes.addBlock(new Screen(Tier.One), Constants.BlockName.ScreenTier1, "oc:screen1") - Recipes.addBlock(new Screen(Tier.Three), Constants.BlockName.ScreenTier3, "oc:screen3") - Recipes.addBlock(new Screen(Tier.Two), Constants.BlockName.ScreenTier2, "oc:screen2") - Recipes.addBlock(new Rack(), Constants.BlockName.Rack, "oc:rack", "oc:rack") - Recipes.addBlock(new Waypoint(), Constants.BlockName.Waypoint, "oc:waypoint") + Items.registerBlock(new Adapter(), Constants.BlockName.Adapter) + Items.registerBlock(new Assembler(), Constants.BlockName.Assembler) + Items.registerBlock(new Cable(), Constants.BlockName.Cable) + Items.registerBlock(new Capacitor(), Constants.BlockName.Capacitor) + Items.registerBlock(new Case(Tier.One), Constants.BlockName.CaseTier1) + Items.registerBlock(new Case(Tier.Three), Constants.BlockName.CaseTier3) + Items.registerBlock(new Case(Tier.Two), Constants.BlockName.CaseTier2) + Items.registerBlock(new ChameliumBlock(), Constants.BlockName.ChameliumBlock) + Items.registerBlock(new Charger(), Constants.BlockName.Charger) + Items.registerBlock(new Disassembler(), Constants.BlockName.Disassembler) + Items.registerBlock(new DiskDrive(), Constants.BlockName.DiskDrive) + Items.registerBlock(new Geolyzer(), Constants.BlockName.Geolyzer) + Items.registerBlock(new Hologram(Tier.One), Constants.BlockName.HologramTier1) + Items.registerBlock(new Hologram(Tier.Two), Constants.BlockName.HologramTier2) + Items.registerBlock(new Keyboard(), Constants.BlockName.Keyboard) + Items.registerBlock(new MotionSensor(), Constants.BlockName.MotionSensor) + Items.registerBlock(new PowerConverter(), Constants.BlockName.PowerConverter) + Items.registerBlock(new PowerDistributor(), Constants.BlockName.PowerDistributor) + Items.registerBlock(new Printer(), Constants.BlockName.Printer) + Items.registerBlock(new Raid(), Constants.BlockName.Raid) + Items.registerBlock(new Redstone(), Constants.BlockName.Redstone) + Items.registerBlock(new Relay(), Constants.BlockName.Relay) + Items.registerBlock(new Screen(Tier.One), Constants.BlockName.ScreenTier1) + Items.registerBlock(new Screen(Tier.Three), Constants.BlockName.ScreenTier3) + Items.registerBlock(new Screen(Tier.Two), Constants.BlockName.ScreenTier2) + Items.registerBlock(new Rack(), Constants.BlockName.Rack) + Items.registerBlock(new Waypoint(), Constants.BlockName.Waypoint) Items.registerBlock(new Case(Tier.Four), Constants.BlockName.CaseCreative) Items.registerBlock(new Microcontroller(), Constants.BlockName.Microcontroller) @@ -74,19 +45,15 @@ object Blocks { Items.registerBlock(new RobotProxy(), Constants.BlockName.Robot) // v1.5.10 - Recipes.addBlock(new FakeEndstone(), Constants.BlockName.Endstone, "oc:stoneEndstone") + Items.registerBlock(new FakeEndstone(), Constants.BlockName.Endstone) // v1.5.14 - Recipes.addBlock(new NetSplitter(), Constants.BlockName.NetSplitter, "oc:netSplitter") + Items.registerBlock(new NetSplitter(), Constants.BlockName.NetSplitter) // v1.5.16 - Recipes.addBlock(new Transposer(), Constants.BlockName.Transposer, "oc:transposer") + Items.registerBlock(new Transposer(), Constants.BlockName.Transposer) // v1.7.2 - Recipes.addBlock(new CarpetedCapacitor(), Constants.BlockName.CarpetedCapacitor, "oc:carpetedCapacitor") - } - - private def registerTileEntity(tileEntityClass: Class[_ <: TileEntity], key: String): Unit = { - GameRegistry.registerTileEntity(tileEntityClass, key) + Items.registerBlock(new CarpetedCapacitor(), Constants.BlockName.CarpetedCapacitor) } } diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index a7e8c5cc7f..c627455c79 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -21,15 +21,14 @@ import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.item.data.TabletData import li.cil.oc.common.item.traits.Delegate import li.cil.oc.common.item.traits.SimpleItem -import li.cil.oc.common.recipe.Recipes import li.cil.oc.server.machine.luac.LuaStateFactory import net.minecraft.block.Block -import net.minecraft.creativetab.CreativeTabs -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.ItemGroup +import net.minecraft.item.DyeColor import net.minecraft.item.Item -import net.minecraft.item.ItemBlock +import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.util.NonNullList import net.minecraft.util.ResourceLocation import net.minecraftforge.registries.{GameData} @@ -58,14 +57,13 @@ object Items extends ItemAPI { if (!descriptors.contains(id)) { instance match { case simple: SimpleBlock => - instance.setUnlocalizedName("oc." + id) - instance.setRegistryName(id) - GameData.register_impl(instance) - OpenComputers.proxy.registerModel(instance, id) + simple.setUnlocalizedName("oc." + id) + simple.setRegistryName(OpenComputers.ID, id) + GameData.register_impl[Block](simple) + OpenComputers.proxy.registerModel(simple, id) - val item : Item = new common.block.Item(instance) - item.setUnlocalizedName("oc." + id) - item.setRegistryName(id) + val item : Item = new common.block.Item(simple) + item.setRegistryName(OpenComputers.ID, id) GameData.register_impl(item) OpenComputers.proxy.registerModel(item, id) case _ => @@ -151,7 +149,7 @@ object Items extends ItemAPI { private def getBlockOrItem(stack: ItemStack): Any = if (stack.isEmpty) null else Delegator.subItem(stack).getOrElse(stack.getItem match { - case block: ItemBlock => block.getBlock + case block: BlockItem => block.getBlock case item => item }) @@ -159,7 +157,7 @@ object Items extends ItemAPI { val registeredItems: ArrayBuffer[ItemStack] = mutable.ArrayBuffer.empty[ItemStack] - override def registerFloppy(name: String, color: EnumDyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { + override def registerFloppy(name: String, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { val stack = Loot.registerLootDisk(name, color, factory, doRecipeCycling) registeredItems += stack @@ -168,23 +166,18 @@ object Items extends ItemAPI { } override def registerEEPROM(name: String, code: Array[Byte], data: Array[Byte], readonly: Boolean): ItemStack = { - val nbt = new NBTTagCompound() + val stack = get(Constants.ItemName.EEPROM).createItemStack(1) + val nbt = stack.getOrCreateTagElement(Settings.namespace + "data") if (name != null) { - nbt.setString(Settings.namespace + "label", name.trim.take(24)) + nbt.putString(Settings.namespace + "label", name.trim.take(24)) } if (code != null) { - nbt.setByteArray(Settings.namespace + "eeprom", code.take(Settings.get.eepromSize)) + nbt.putByteArray(Settings.namespace + "eeprom", code.take(Settings.get.eepromSize)) } if (data != null) { - nbt.setByteArray(Settings.namespace + "userdata", data.take(Settings.get.eepromDataSize)) + nbt.putByteArray(Settings.namespace + "userdata", data.take(Settings.get.eepromDataSize)) } - nbt.setBoolean(Settings.namespace + "readonly", readonly) - - val stackNbt = new NBTTagCompound() - stackNbt.setTag(Settings.namespace + "data", nbt) - - val stack = get(Constants.ItemName.EEPROM).createItemStack(1) - stack.setTagCompound(stackNbt) + nbt.putBoolean(Settings.namespace + "readonly", readonly) registeredItems += stack @@ -347,37 +340,37 @@ object Items extends ItemAPI { private def initMaterials(): Unit = { val materials = newItem(new item.Delegator(), "material") - Recipes.addSubItem(new item.CuttingWire(materials), Constants.ItemName.CuttingWire, "oc:materialCuttingWire") - Recipes.addSubItem(new item.Acid(materials), Constants.ItemName.Acid, "oc:materialAcid") - Recipes.addSubItem(new item.RawCircuitBoard(materials), Constants.ItemName.RawCircuitBoard, "oc:materialCircuitBoardRaw") - Recipes.addSubItem(new item.CircuitBoard(materials), Constants.ItemName.CircuitBoard, "oc:materialCircuitBoard") - Recipes.addSubItem(new item.PrintedCircuitBoard(materials), Constants.ItemName.PrintedCircuitBoard, "oc:materialCircuitBoardPrinted") - Recipes.addSubItem(new item.CardBase(materials), Constants.ItemName.Card, "oc:materialCard") - Recipes.addSubItem(new item.Transistor(materials), Constants.ItemName.Transistor, "oc:materialTransistor") - Recipes.addSubItem(new item.Microchip(materials, Tier.One), Constants.ItemName.ChipTier1, "oc:circuitChip1") - Recipes.addSubItem(new item.Microchip(materials, Tier.Two), Constants.ItemName.ChipTier2, "oc:circuitChip2") - Recipes.addSubItem(new item.Microchip(materials, Tier.Three), Constants.ItemName.ChipTier3, "oc:circuitChip3") - Recipes.addSubItem(new item.ALU(materials), Constants.ItemName.Alu, "oc:materialALU") - Recipes.addSubItem(new item.ControlUnit(materials), Constants.ItemName.ControlUnit, "oc:materialCU") - Recipes.addSubItem(new item.Disk(materials), Constants.ItemName.Disk, "oc:materialDisk") - Recipes.addSubItem(new item.Interweb(materials), Constants.ItemName.Interweb, "oc:materialInterweb") - Recipes.addSubItem(new item.ButtonGroup(materials), Constants.ItemName.ButtonGroup, "oc:materialButtonGroup") - Recipes.addSubItem(new item.ArrowKeys(materials), Constants.ItemName.ArrowKeys, "oc:materialArrowKey") - Recipes.addSubItem(new item.NumPad(materials), Constants.ItemName.NumPad, "oc:materialNumPad") - - Recipes.addSubItem(new item.TabletCase(materials, Tier.One), Constants.ItemName.TabletCaseTier1, "oc:tabletCase1") - Recipes.addSubItem(new item.TabletCase(materials, Tier.Two), Constants.ItemName.TabletCaseTier2, "oc:tabletCase2") + registerItem(new item.CuttingWire(materials), Constants.ItemName.CuttingWire) + registerItem(new item.Acid(materials), Constants.ItemName.Acid) + registerItem(new item.RawCircuitBoard(materials), Constants.ItemName.RawCircuitBoard) + registerItem(new item.CircuitBoard(materials), Constants.ItemName.CircuitBoard) + registerItem(new item.PrintedCircuitBoard(materials), Constants.ItemName.PrintedCircuitBoard) + registerItem(new item.CardBase(materials), Constants.ItemName.Card) + registerItem(new item.Transistor(materials), Constants.ItemName.Transistor) + registerItem(new item.Microchip(materials, Tier.One), Constants.ItemName.ChipTier1) + registerItem(new item.Microchip(materials, Tier.Two), Constants.ItemName.ChipTier2) + registerItem(new item.Microchip(materials, Tier.Three), Constants.ItemName.ChipTier3) + registerItem(new item.ALU(materials), Constants.ItemName.Alu) + registerItem(new item.ControlUnit(materials), Constants.ItemName.ControlUnit) + registerItem(new item.Disk(materials), Constants.ItemName.Disk) + registerItem(new item.Interweb(materials), Constants.ItemName.Interweb) + registerItem(new item.ButtonGroup(materials), Constants.ItemName.ButtonGroup) + registerItem(new item.ArrowKeys(materials), Constants.ItemName.ArrowKeys) + registerItem(new item.NumPad(materials), Constants.ItemName.NumPad) + + registerItem(new item.TabletCase(materials, Tier.One), Constants.ItemName.TabletCaseTier1) + registerItem(new item.TabletCase(materials, Tier.Two), Constants.ItemName.TabletCaseTier2) registerItem(new item.TabletCase(materials, Tier.Four), Constants.ItemName.TabletCaseCreative) - Recipes.addSubItem(new item.MicrocontrollerCase(materials, Tier.One), Constants.ItemName.MicrocontrollerCaseTier1, "oc:microcontrollerCase1") - Recipes.addSubItem(new item.MicrocontrollerCase(materials, Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2, "oc:microcontrollerCase2") + registerItem(new item.MicrocontrollerCase(materials, Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) + registerItem(new item.MicrocontrollerCase(materials, Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) registerItem(new item.MicrocontrollerCase(materials, Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) - Recipes.addSubItem(new item.DroneCase(materials, Tier.One), Constants.ItemName.DroneCaseTier1, "oc:droneCase1") - Recipes.addSubItem(new item.DroneCase(materials, Tier.Two), Constants.ItemName.DroneCaseTier2, "oc:droneCase2") + registerItem(new item.DroneCase(materials, Tier.One), Constants.ItemName.DroneCaseTier1) + registerItem(new item.DroneCase(materials, Tier.Two), Constants.ItemName.DroneCaseTier2) registerItem(new item.DroneCase(materials, Tier.Four), Constants.ItemName.DroneCaseCreative) - Recipes.addSubItem(new item.InkCartridgeEmpty(materials), Constants.ItemName.InkCartridgeEmpty, "oc:inkCartridgeEmpty") - Recipes.addSubItem(new item.InkCartridge(materials), Constants.ItemName.InkCartridge, "oc:inkCartridge") - Recipes.addSubItem(new item.Chamelium(materials), Constants.ItemName.Chamelium, "oc:chamelium") + registerItem(new item.InkCartridgeEmpty(materials), Constants.ItemName.InkCartridgeEmpty) + registerItem(new item.InkCartridge(materials), Constants.ItemName.InkCartridge) + registerItem(new item.Chamelium(materials), Constants.ItemName.Chamelium) registerItem(new item.DiamondChip(materials), Constants.ItemName.DiamondChip) } @@ -386,54 +379,54 @@ object Items extends ItemAPI { private def initTools(): Unit = { val tools = newItem(new item.Delegator(), "tool") - Recipes.addSubItem(new item.Analyzer(tools), Constants.ItemName.Analyzer, "oc:analyzer") + registerItem(new item.Analyzer(tools), Constants.ItemName.Analyzer) registerItem(new item.Debugger(tools), Constants.ItemName.Debugger) - Recipes.addSubItem(new item.Terminal(tools), Constants.ItemName.Terminal, "oc:terminal") - Recipes.addSubItem(new item.TexturePicker(tools), Constants.ItemName.TexturePicker, "oc:texturePicker") - Recipes.addSubItem(new item.Manual(tools), Constants.ItemName.Manual, "oc:manual") - Recipes.addItem(new item.Wrench(), Constants.ItemName.Wrench, "oc:wrench") + registerItem(new item.Terminal(tools), Constants.ItemName.Terminal) + registerItem(new item.TexturePicker(tools), Constants.ItemName.TexturePicker) + registerItem(new item.Manual(tools), Constants.ItemName.Manual) + registerItem(new item.Wrench(), Constants.ItemName.Wrench) // 1.5.11 - Recipes.addItem(new item.HoverBoots(), Constants.ItemName.HoverBoots, "oc:hoverBoots") + registerItem(new item.HoverBoots(), Constants.ItemName.HoverBoots) // 1.5.18 - Recipes.addSubItem(new item.Nanomachines(tools), Constants.ItemName.Nanomachines, "oc:nanomachines") + registerItem(new item.Nanomachines(tools), Constants.ItemName.Nanomachines) } // General purpose components. private def initComponents(): Unit = { val components = newItem(new item.Delegator(), "component") - Recipes.addSubItem(new item.CPU(components, Tier.One), Constants.ItemName.CPUTier1, "oc:cpu1") - Recipes.addSubItem(new item.CPU(components, Tier.Two), Constants.ItemName.CPUTier2, "oc:cpu2") - Recipes.addSubItem(new item.CPU(components, Tier.Three), Constants.ItemName.CPUTier3, "oc:cpu3") + registerItem(new item.CPU(components, Tier.One), Constants.ItemName.CPUTier1) + registerItem(new item.CPU(components, Tier.Two), Constants.ItemName.CPUTier2) + registerItem(new item.CPU(components, Tier.Three), Constants.ItemName.CPUTier3) - Recipes.addSubItem(new item.ComponentBus(components, Tier.One), Constants.ItemName.ComponentBusTier1, "oc:componentBus1") - Recipes.addSubItem(new item.ComponentBus(components, Tier.Two), Constants.ItemName.ComponentBusTier2, "oc:componentBus2") - Recipes.addSubItem(new item.ComponentBus(components, Tier.Three), Constants.ItemName.ComponentBusTier3, "oc:componentBus3") + registerItem(new item.ComponentBus(components, Tier.One), Constants.ItemName.ComponentBusTier1) + registerItem(new item.ComponentBus(components, Tier.Two), Constants.ItemName.ComponentBusTier2) + registerItem(new item.ComponentBus(components, Tier.Three), Constants.ItemName.ComponentBusTier3) - Recipes.addSubItem(new item.Memory(components, Tier.One), Constants.ItemName.RAMTier1, "oc:ram1") - Recipes.addSubItem(new item.Memory(components, Tier.Two), Constants.ItemName.RAMTier2, "oc:ram2") - Recipes.addSubItem(new item.Memory(components, Tier.Three), Constants.ItemName.RAMTier3, "oc:ram3") - Recipes.addSubItem(new item.Memory(components, Tier.Four), Constants.ItemName.RAMTier4, "oc:ram4") - Recipes.addSubItem(new item.Memory(components, Tier.Five), Constants.ItemName.RAMTier5, "oc:ram5") - Recipes.addSubItem(new item.Memory(components, Tier.Six), Constants.ItemName.RAMTier6, "oc:ram6") + registerItem(new item.Memory(components, Tier.One), Constants.ItemName.RAMTier1) + registerItem(new item.Memory(components, Tier.Two), Constants.ItemName.RAMTier2) + registerItem(new item.Memory(components, Tier.Three), Constants.ItemName.RAMTier3) + registerItem(new item.Memory(components, Tier.Four), Constants.ItemName.RAMTier4) + registerItem(new item.Memory(components, Tier.Five), Constants.ItemName.RAMTier5) + registerItem(new item.Memory(components, Tier.Six), Constants.ItemName.RAMTier6) registerItem(new item.Server(components, Tier.Four), Constants.ItemName.ServerCreative) - Recipes.addSubItem(new item.Server(components, Tier.One), Constants.ItemName.ServerTier1, "oc:server1") - Recipes.addSubItem(new item.Server(components, Tier.Two), Constants.ItemName.ServerTier2, "oc:server2") - Recipes.addSubItem(new item.Server(components, Tier.Three), Constants.ItemName.ServerTier3, "oc:server3") + registerItem(new item.Server(components, Tier.One), Constants.ItemName.ServerTier1) + registerItem(new item.Server(components, Tier.Two), Constants.ItemName.ServerTier2) + registerItem(new item.Server(components, Tier.Three), Constants.ItemName.ServerTier3) // 1.5.10 - Recipes.addSubItem(new item.APU(components, Tier.One), Constants.ItemName.APUTier1, "oc:apu1") - Recipes.addSubItem(new item.APU(components, Tier.Two), Constants.ItemName.APUTier2, "oc:apu2") + registerItem(new item.APU(components, Tier.One), Constants.ItemName.APUTier1) + registerItem(new item.APU(components, Tier.Two), Constants.ItemName.APUTier2) // 1.5.12 registerItem(new item.APU(components, Tier.Three), Constants.ItemName.APUCreative) // 1.6 - Recipes.addSubItem(new item.TerminalServer(components), Constants.ItemName.TerminalServer, "oc:terminalServer") - Recipes.addSubItem(new item.DiskDriveMountable(components), Constants.ItemName.DiskDriveMountable, "oc:diskDriveMountable") + registerItem(new item.TerminalServer(components), Constants.ItemName.TerminalServer) + registerItem(new item.DiskDriveMountable(components), Constants.ItemName.DiskDriveMountable) } // Card components. @@ -441,88 +434,88 @@ object Items extends ItemAPI { val cards = newItem(new item.Delegator(), "card") registerItem(new item.DebugCard(cards), Constants.ItemName.DebugCard) - Recipes.addSubItem(new item.GraphicsCard(cards, Tier.One), Constants.ItemName.GraphicsCardTier1, "oc:graphicsCard1") - Recipes.addSubItem(new item.GraphicsCard(cards, Tier.Two), Constants.ItemName.GraphicsCardTier2, "oc:graphicsCard2") - Recipes.addSubItem(new item.GraphicsCard(cards, Tier.Three), Constants.ItemName.GraphicsCardTier3, "oc:graphicsCard3") - Recipes.addSubItem(new item.RedstoneCard(cards, Tier.One), Constants.ItemName.RedstoneCardTier1, "oc:redstoneCard1") - Recipes.addSubItem(new item.RedstoneCard(cards, Tier.Two), Constants.ItemName.RedstoneCardTier2, "oc:redstoneCard2") - Recipes.addSubItem(new item.NetworkCard(cards), Constants.ItemName.NetworkCard, "oc:lanCard") - Recipes.addSubItem(new item.WirelessNetworkCard(cards, Tier.Two), Constants.ItemName.WirelessNetworkCardTier2, "oc:wlanCard2") - Recipes.addSubItem(new item.InternetCard(cards), Constants.ItemName.InternetCard, "oc:internetCard") - Recipes.addSubItem(new item.LinkedCard(cards), Constants.ItemName.LinkedCard, "oc:linkedCard") + registerItem(new item.GraphicsCard(cards, Tier.One), Constants.ItemName.GraphicsCardTier1) + registerItem(new item.GraphicsCard(cards, Tier.Two), Constants.ItemName.GraphicsCardTier2) + registerItem(new item.GraphicsCard(cards, Tier.Three), Constants.ItemName.GraphicsCardTier3) + registerItem(new item.RedstoneCard(cards, Tier.One), Constants.ItemName.RedstoneCardTier1) + registerItem(new item.RedstoneCard(cards, Tier.Two), Constants.ItemName.RedstoneCardTier2) + registerItem(new item.NetworkCard(cards), Constants.ItemName.NetworkCard) + registerItem(new item.WirelessNetworkCard(cards, Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) + registerItem(new item.InternetCard(cards), Constants.ItemName.InternetCard) + registerItem(new item.LinkedCard(cards), Constants.ItemName.LinkedCard) // 1.5.13 - Recipes.addSubItem(new item.DataCard(cards, Tier.One), Constants.ItemName.DataCardTier1, "oc:dataCard1") + registerItem(new item.DataCard(cards, Tier.One), Constants.ItemName.DataCardTier1) // 1.5.15 - Recipes.addSubItem(new item.DataCard(cards, Tier.Two), Constants.ItemName.DataCardTier2, "oc:dataCard2") - Recipes.addSubItem(new item.DataCard(cards, Tier.Three), Constants.ItemName.DataCardTier3, "oc:dataCard3") + registerItem(new item.DataCard(cards, Tier.Two), Constants.ItemName.DataCardTier2) + registerItem(new item.DataCard(cards, Tier.Three), Constants.ItemName.DataCardTier3) } // Upgrade components. private def initUpgrades(): Unit = { val upgrades = newItem(new item.Delegator(), "upgrade") - Recipes.addSubItem(new item.UpgradeAngel(upgrades), Constants.ItemName.AngelUpgrade, "oc:angelUpgrade") - Recipes.addSubItem(new item.UpgradeBattery(upgrades, Tier.One), Constants.ItemName.BatteryUpgradeTier1, "oc:batteryUpgrade1") - Recipes.addSubItem(new item.UpgradeBattery(upgrades, Tier.Two), Constants.ItemName.BatteryUpgradeTier2, "oc:batteryUpgrade2") - Recipes.addSubItem(new item.UpgradeBattery(upgrades, Tier.Three), Constants.ItemName.BatteryUpgradeTier3, "oc:batteryUpgrade3") - Recipes.addSubItem(new item.UpgradeChunkloader(upgrades), Constants.ItemName.ChunkloaderUpgrade, "oc:chunkloaderUpgrade") - Recipes.addSubItem(new item.UpgradeContainerCard(upgrades, Tier.One), Constants.ItemName.CardContainerTier1, "oc:cardContainer1") - Recipes.addSubItem(new item.UpgradeContainerCard(upgrades, Tier.Two), Constants.ItemName.CardContainerTier2, "oc:cardContainer2") - Recipes.addSubItem(new item.UpgradeContainerCard(upgrades, Tier.Three), Constants.ItemName.CardContainerTier3, "oc:cardContainer3") - Recipes.addSubItem(new item.UpgradeContainerUpgrade(upgrades, Tier.One), Constants.ItemName.UpgradeContainerTier1, "oc:upgradeContainer1") - Recipes.addSubItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Two), Constants.ItemName.UpgradeContainerTier2, "oc:upgradeContainer2") - Recipes.addSubItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Three), Constants.ItemName.UpgradeContainerTier3, "oc:upgradeContainer3") - Recipes.addSubItem(new item.UpgradeCrafting(upgrades), Constants.ItemName.CraftingUpgrade, "oc:craftingUpgrade") - Recipes.addSubItem(new item.UpgradeDatabase(upgrades, Tier.One), Constants.ItemName.DatabaseUpgradeTier1, "oc:databaseUpgrade1") - Recipes.addSubItem(new item.UpgradeDatabase(upgrades, Tier.Two), Constants.ItemName.DatabaseUpgradeTier2, "oc:databaseUpgrade2") - Recipes.addSubItem(new item.UpgradeDatabase(upgrades, Tier.Three), Constants.ItemName.DatabaseUpgradeTier3, "oc:databaseUpgrade3") - Recipes.addSubItem(new item.UpgradeExperience(upgrades), Constants.ItemName.ExperienceUpgrade, "oc:experienceUpgrade") - Recipes.addSubItem(new item.UpgradeGenerator(upgrades), Constants.ItemName.GeneratorUpgrade, "oc:generatorUpgrade") - Recipes.addSubItem(new item.UpgradeInventory(upgrades), Constants.ItemName.InventoryUpgrade, "oc:inventoryUpgrade") - Recipes.addSubItem(new item.UpgradeInventoryController(upgrades), Constants.ItemName.InventoryControllerUpgrade, "oc:inventoryControllerUpgrade") - Recipes.addSubItem(new item.UpgradeNavigation(upgrades), Constants.ItemName.NavigationUpgrade, "oc:navigationUpgrade") - Recipes.addSubItem(new item.UpgradePiston(upgrades), Constants.ItemName.PistonUpgrade, "oc:pistonUpgrade") - Recipes.addSubItem(new item.UpgradeSign(upgrades), Constants.ItemName.SignUpgrade, "oc:signUpgrade") - Recipes.addSubItem(new item.UpgradeSolarGenerator(upgrades), Constants.ItemName.SolarGeneratorUpgrade, "oc:solarGeneratorUpgrade") - Recipes.addSubItem(new item.UpgradeTank(upgrades), Constants.ItemName.TankUpgrade, "oc:tankUpgrade") - Recipes.addSubItem(new item.UpgradeTankController(upgrades), Constants.ItemName.TankControllerUpgrade, "oc:tankControllerUpgrade") - Recipes.addSubItem(new item.UpgradeTractorBeam(upgrades), Constants.ItemName.TractorBeamUpgrade, "oc:tractorBeamUpgrade") - Recipes.addSubItem(new item.UpgradeLeash(upgrades), Constants.ItemName.LeashUpgrade, "oc:leashUpgrade") + registerItem(new item.UpgradeAngel(upgrades), Constants.ItemName.AngelUpgrade) + registerItem(new item.UpgradeBattery(upgrades, Tier.One), Constants.ItemName.BatteryUpgradeTier1) + registerItem(new item.UpgradeBattery(upgrades, Tier.Two), Constants.ItemName.BatteryUpgradeTier2) + registerItem(new item.UpgradeBattery(upgrades, Tier.Three), Constants.ItemName.BatteryUpgradeTier3) + registerItem(new item.UpgradeChunkloader(upgrades), Constants.ItemName.ChunkloaderUpgrade) + registerItem(new item.UpgradeContainerCard(upgrades, Tier.One), Constants.ItemName.CardContainerTier1) + registerItem(new item.UpgradeContainerCard(upgrades, Tier.Two), Constants.ItemName.CardContainerTier2) + registerItem(new item.UpgradeContainerCard(upgrades, Tier.Three), Constants.ItemName.CardContainerTier3) + registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.One), Constants.ItemName.UpgradeContainerTier1) + registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Two), Constants.ItemName.UpgradeContainerTier2) + registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Three), Constants.ItemName.UpgradeContainerTier3) + registerItem(new item.UpgradeCrafting(upgrades), Constants.ItemName.CraftingUpgrade) + registerItem(new item.UpgradeDatabase(upgrades, Tier.One), Constants.ItemName.DatabaseUpgradeTier1) + registerItem(new item.UpgradeDatabase(upgrades, Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) + registerItem(new item.UpgradeDatabase(upgrades, Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) + registerItem(new item.UpgradeExperience(upgrades), Constants.ItemName.ExperienceUpgrade) + registerItem(new item.UpgradeGenerator(upgrades), Constants.ItemName.GeneratorUpgrade) + registerItem(new item.UpgradeInventory(upgrades), Constants.ItemName.InventoryUpgrade) + registerItem(new item.UpgradeInventoryController(upgrades), Constants.ItemName.InventoryControllerUpgrade) + registerItem(new item.UpgradeNavigation(upgrades), Constants.ItemName.NavigationUpgrade) + registerItem(new item.UpgradePiston(upgrades), Constants.ItemName.PistonUpgrade) + registerItem(new item.UpgradeSign(upgrades), Constants.ItemName.SignUpgrade) + registerItem(new item.UpgradeSolarGenerator(upgrades), Constants.ItemName.SolarGeneratorUpgrade) + registerItem(new item.UpgradeTank(upgrades), Constants.ItemName.TankUpgrade) + registerItem(new item.UpgradeTankController(upgrades), Constants.ItemName.TankControllerUpgrade) + registerItem(new item.UpgradeTractorBeam(upgrades), Constants.ItemName.TractorBeamUpgrade) + registerItem(new item.UpgradeLeash(upgrades), Constants.ItemName.LeashUpgrade) // 1.5.8 - Recipes.addSubItem(new item.UpgradeHover(upgrades, Tier.One), Constants.ItemName.HoverUpgradeTier1, "oc:hoverUpgrade1") - Recipes.addSubItem(new item.UpgradeHover(upgrades, Tier.Two), Constants.ItemName.HoverUpgradeTier2, "oc:hoverUpgrade2") + registerItem(new item.UpgradeHover(upgrades, Tier.One), Constants.ItemName.HoverUpgradeTier1) + registerItem(new item.UpgradeHover(upgrades, Tier.Two), Constants.ItemName.HoverUpgradeTier2) // 1.6 - Recipes.addSubItem(new item.UpgradeTrading(upgrades), Constants.ItemName.TradingUpgrade, "oc:tradingUpgrade") - Recipes.addSubItem(new item.UpgradeMF(upgrades), Constants.ItemName.MFU, "oc:mfu") + registerItem(new item.UpgradeTrading(upgrades), Constants.ItemName.TradingUpgrade) + registerItem(new item.UpgradeMF(upgrades), Constants.ItemName.MFU) // 1.7.2 - Recipes.addSubItem(new item.WirelessNetworkCard(upgrades, Tier.One), Constants.ItemName.WirelessNetworkCardTier1, "oc:wlanCard1") + registerItem(new item.WirelessNetworkCard(upgrades, Tier.One), Constants.ItemName.WirelessNetworkCardTier1) registerItem(new item.ComponentBus(upgrades, Tier.Four), Constants.ItemName.ComponentBusCreative) // 1.8 - Recipes.addSubItem(new item.UpgradeStickyPiston(upgrades), Constants.ItemName.StickyPistonUpgrade, "oc:stickyPistonUpgrade") + registerItem(new item.UpgradeStickyPiston(upgrades), Constants.ItemName.StickyPistonUpgrade) } // Storage media of all kinds. private def initStorage(): Unit = { val storage = newItem(new item.Delegator(), "storage") - Recipes.addSubItem(new item.EEPROM(storage), Constants.ItemName.EEPROM, "oc:eeprom") - Recipes.addSubItem(new item.FloppyDisk(storage), Constants.ItemName.Floppy, "oc:floppy") - Recipes.addSubItem(new item.HardDiskDrive(storage, Tier.One), Constants.ItemName.HDDTier1, "oc:hdd1") - Recipes.addSubItem(new item.HardDiskDrive(storage, Tier.Two), Constants.ItemName.HDDTier2, "oc:hdd2") - Recipes.addSubItem(new item.HardDiskDrive(storage, Tier.Three), Constants.ItemName.HDDTier3, "oc:hdd3") + registerItem(new item.EEPROM(storage), Constants.ItemName.EEPROM) + registerItem(new item.FloppyDisk(storage), Constants.ItemName.Floppy) + registerItem(new item.HardDiskDrive(storage, Tier.One), Constants.ItemName.HDDTier1) + registerItem(new item.HardDiskDrive(storage, Tier.Two), Constants.ItemName.HDDTier2) + registerItem(new item.HardDiskDrive(storage, Tier.Three), Constants.ItemName.HDDTier3) val luaBios = { val code = new Array[Byte](4 * 1024) val count = OpenComputers.getClass.getResourceAsStream(Settings.scriptPath + "bios.lua").read(code) registerEEPROM("EEPROM (Lua BIOS)", code.take(count), null, readonly = false) } - Recipes.addStack(luaBios, Constants.ItemName.LuaBios) + registerStack(luaBios, Constants.ItemName.LuaBios) } @@ -537,9 +530,9 @@ object Items extends ItemAPI { Items.createChargedHoverBoots() ) ++ Loot.disksForClient ++ registeredItems - override def getSubItems(tab: CreativeTabs, list: NonNullList[ItemStack]): Unit = { - super.getSubItems(tab, list) - if(isInCreativeTab(tab)){ + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]): Unit = { + super.fillItemCategory(tab, list) + if(this.allowdedIn(tab)){ configuredItems.foreach(list.add) } } @@ -550,9 +543,9 @@ object Items extends ItemAPI { registerItem(new item.Present(misc), Constants.ItemName.Present) } - private def newItem[T <: Item](item: T, name: String): T = { - item.setUnlocalizedName("oc." + name) - GameData.register_impl(item.setRegistryName(new ResourceLocation(Settings.resourceDomain, name))) - item + private def newItem[T <: item.Delegator](delegator: T, name: String): T = { + delegator.setUnlocalizedName("oc." + name) + GameData.register_impl(delegator.setRegistryName(new ResourceLocation(Settings.resourceDomain, name))) + delegator } } diff --git a/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala b/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala index 7fbdf4be94..a425e554ce 100644 --- a/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala @@ -11,7 +11,7 @@ import li.cil.oc.api.network.Node import li.cil.oc.api.util.Lifecycle import li.cil.oc.integration.opencomputers.Item import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -22,7 +22,7 @@ trait ComponentInventory extends Inventory with network.Environment { def components: Array[Option[ManagedEnvironment]] = { if (_components == null && isSizeInventoryReady) { - _components = Array.fill[Option[ManagedEnvironment]](getSizeInventory)(None) + _components = Array.fill[Option[ManagedEnvironment]](getContainerSize)(None) } if (_components == null) Array[Option[ManagedEnvironment]]() else _components } @@ -53,8 +53,8 @@ trait ComponentInventory extends Inventory with network.Environment { // ----------------------------------------------------------------------- // def connectComponents() { - for (slot <- 0 until getSizeInventory if slot >= 0 && slot < components.length) { - val stack = getStackInSlot(slot) + for (slot <- 0 until getContainerSize if slot >= 0 && slot < components.length) { + val stack = getItem(slot) if (!stack.isEmpty && components(slot).isEmpty && isComponentSlot(slot, stack)) { components(slot) = Option(Driver.driverFor(stack)) match { case Some(driver) => @@ -62,7 +62,7 @@ trait ComponentInventory extends Inventory with network.Environment { case Some(component) => applyLifecycleState(component, Lifecycle.LifecycleState.Constructing) try { - component.load(dataTag(driver, stack)) + component.loadData(dataTag(driver, stack)) } catch { case e: Throwable => OpenComputers.log.warn(s"An item component of type '${component.getClass.getName}' (provided by driver '${driver.getClass.getName}') threw an error while loading.", e) @@ -99,20 +99,20 @@ trait ComponentInventory extends Inventory with network.Environment { // ----------------------------------------------------------------------- // - override def save(nbt: NBTTagCompound) = { + override def saveData(nbt: CompoundNBT) { saveComponents() - super.save(nbt) // Save items after updating their tags. + super.saveData(nbt) // Save items after updating their tags. } def saveComponents() { - for (slot <- 0 until getSizeInventory) { - val stack = getStackInSlot(slot) + for (slot <- 0 until getContainerSize) { + val stack = getItem(slot) if (!stack.isEmpty) { if (slot >= components.length) { // isSizeInventoryReady was added to resolve issues where an inventory was used before its // nbt data had been parsed. See https://github.com/MightyPirates/OpenComputers/issues/2522 // If this error is hit again, perhaps another subtype needs to handle nbt loading like Case does - OpenComputers.log.error(s"ComponentInventory components length ${components.length} does not accommodate inventory size ${getSizeInventory}") + OpenComputers.log.error(s"ComponentInventory components length ${components.length} does not accommodate inventory size ${getContainerSize}") return } else { components(slot) match { @@ -128,7 +128,7 @@ trait ComponentInventory extends Inventory with network.Environment { // ----------------------------------------------------------------------- // - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 override protected def onItemAdded(slot: Int, stack: ItemStack) = if (slot >= 0 && slot < components.length && isComponentSlot(slot, stack)) { Option(Driver.driverFor(stack)).foreach(driver => @@ -137,7 +137,7 @@ trait ComponentInventory extends Inventory with network.Environment { components(slot) = Some(component) applyLifecycleState(component, Lifecycle.LifecycleState.Constructing) try { - component.load(dataTag(driver, stack)) + component.loadData(dataTag(driver, stack)) } catch { case e: Throwable => OpenComputers.log.warn(s"An item component of type '${component.getClass.getName}' (provided by driver '${driver.getClass.getName}') threw an error while loading.", e) } @@ -194,10 +194,10 @@ trait ComponentInventory extends Inventory with network.Environment { val tag = dataTag(driver, stack) // Clear the tag compound before saving to get the same behavior as // in tile entities (otherwise entries have to be cleared manually). - for (key <- tag.getKeySet.map(_.asInstanceOf[String])) { - tag.removeTag(key) + for (key <- tag.getAllKeys.map(_.asInstanceOf[String])) { + tag.remove(key) } - component.save(tag) + component.saveData(tag) } catch { case e: Throwable => OpenComputers.log.warn(s"An item component of type '${component.getClass.getName}' (provided by driver '${driver.getClass.getName}') threw an error while saving.", e) } diff --git a/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala b/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala index 086ab83424..ecb94770fc 100644 --- a/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala @@ -7,13 +7,13 @@ import net.minecraft.item.ItemStack trait DatabaseInventory extends ItemStackInventory { def tier: Int = DriverUpgradeDatabase.tier(container) - override def getSizeInventory = Settings.get.databaseEntriesPerTier(tier) + override def getContainerSize = Settings.get.databaseEntriesPerTier(tier) override protected def inventoryName = "database" - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 override def getInventoryStackRequired = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack) = stack != container + override def canPlaceItem(slot: Int, stack: ItemStack) = stack != container } diff --git a/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala b/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala index 58a00ca8da..e448316100 100644 --- a/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala @@ -8,13 +8,13 @@ import net.minecraft.item.ItemStack trait DiskDriveMountableInventory extends ItemStackInventory { def tier: Int = 1 - override def getSizeInventory = 1 + override def getContainerSize = 1 override protected def inventoryName = "diskdrive" - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, classOf[tileentity.DiskDrive]))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, classOf[tileentity.DiskDrive]))) match { case (0, Some(driver)) => driver.slot(stack) == Slot.Floppy case _ => false } diff --git a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala index 74fd0e8bd0..174f2efbb8 100644 --- a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala @@ -4,7 +4,9 @@ import li.cil.oc.Settings import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.TranslationTextComponent import net.minecraftforge.common.util.Constants.NBT trait Inventory extends SimpleInventory { @@ -14,12 +16,12 @@ trait Inventory extends SimpleInventory { // ----------------------------------------------------------------------- // - override def getStackInSlot(slot: Int): ItemStack = - if (slot >= 0 && slot < getSizeInventory) items(slot) + override def getItem(slot: Int): ItemStack = + if (slot >= 0 && slot < getContainerSize) items(slot) else ItemStack.EMPTY - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { - if (slot >= 0 && slot < getSizeInventory) { + override def setItem(slot: Int, stack: ItemStack): Unit = { + if (slot >= 0 && slot < getContainerSize) { if (stack.isEmpty && items(slot).isEmpty) { return } @@ -33,8 +35,8 @@ trait Inventory extends SimpleInventory { onItemRemoved(slot, oldStack) } if (!stack.isEmpty && stack.getCount >= getInventoryStackRequired) { - if (stack.getCount > getInventoryStackLimit) { - stack.setCount(getInventoryStackLimit) + if (stack.getCount > getMaxStackSize) { + stack.setCount(getMaxStackSize) } updateItems(slot, stack) } @@ -43,11 +45,11 @@ trait Inventory extends SimpleInventory { onItemAdded(slot, items(slot)) } - markDirty() + setChanged() } } - override def getName: String = Settings.namespace + "container." + inventoryName + override def getName: ITextComponent = new TranslationTextComponent(Settings.namespace + "container." + inventoryName) protected def inventoryName: String = getClass.getSimpleName.toLowerCase @@ -59,26 +61,26 @@ trait Inventory extends SimpleInventory { private final val SlotTag = "slot" private final val ItemTag = "item" - def load(nbt: NBTTagCompound) { - nbt.getTagList(ItemsTag, NBT.TAG_COMPOUND).foreach((tag: NBTTagCompound) => { - if (tag.hasKey(SlotTag)) { + def loadData(nbt: CompoundNBT) { + nbt.getList(ItemsTag, NBT.TAG_COMPOUND).foreach((tag: CompoundNBT) => { + if (tag.contains(SlotTag)) { val slot = tag.getByte(SlotTag) if (slot >= 0 && slot < items.length) { - updateItems(slot, new ItemStack(tag.getCompoundTag(ItemTag))) + updateItems(slot, ItemStack.of(tag.getCompound(ItemTag))) } } }) } - def save(nbt: NBTTagCompound) { + def saveData(nbt: CompoundNBT) { nbt.setNewTagList(ItemsTag, items.zipWithIndex collect { case (stack, slot) if !stack.isEmpty => (stack, slot) } map { case (stack, slot) => - val slotNbt = new NBTTagCompound() - slotNbt.setByte(SlotTag, slot.toByte) - slotNbt.setNewCompoundTag(ItemTag, stack.writeToNBT) + val slotNbt = new CompoundNBT() + slotNbt.putByte(SlotTag, slot.toByte) + slotNbt.setNewCompoundTag(ItemTag, stack.save) }) } diff --git a/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala b/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala index 020f62ec59..dd036ce1fc 100644 --- a/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala +++ b/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala @@ -1,6 +1,6 @@ package li.cil.oc.common.inventory -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.util.text.ITextComponent @@ -12,59 +12,47 @@ trait InventoryProxy extends IInventory { override def isEmpty: Boolean = inventory.isEmpty - override def getSizeInventory: Int = inventory.getSizeInventory + override def getContainerSize: Int = inventory.getContainerSize - override def getInventoryStackLimit: Int = inventory.getInventoryStackLimit + override def getMaxStackSize: Int = inventory.getMaxStackSize - override def getName: String = inventory.getName + override def stillValid(player: PlayerEntity): Boolean = inventory.stillValid(player) - override def getDisplayName: ITextComponent = inventory.getDisplayName - - override def hasCustomName: Boolean = inventory.hasCustomName - - override def isUsableByPlayer(player: EntityPlayer): Boolean = inventory.isUsableByPlayer(player) - - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = { val offsetSlot = slot + offset - isValidSlot(offsetSlot) && inventory.isItemValidForSlot(offsetSlot, stack) + isValidSlot(offsetSlot) && inventory.canPlaceItem(offsetSlot, stack) } - override def getStackInSlot(slot: Int): ItemStack = { + override def getItem(slot: Int): ItemStack = { val offsetSlot = slot + offset - if (isValidSlot(offsetSlot)) inventory.getStackInSlot(offsetSlot) + if (isValidSlot(offsetSlot)) inventory.getItem(offsetSlot) else ItemStack.EMPTY } - override def decrStackSize(slot: Int, amount: Int): ItemStack = { + override def removeItem(slot: Int, amount: Int): ItemStack = { val offsetSlot = slot + offset - if (isValidSlot(offsetSlot)) inventory.decrStackSize(offsetSlot, amount) + if (isValidSlot(offsetSlot)) inventory.removeItem(offsetSlot, amount) else ItemStack.EMPTY } - override def removeStackFromSlot(slot: Int): ItemStack = { + override def removeItemNoUpdate(slot: Int): ItemStack = { val offsetSlot = slot + offset - if (isValidSlot(offsetSlot)) inventory.removeStackFromSlot(offsetSlot) + if (isValidSlot(offsetSlot)) inventory.removeItemNoUpdate(offsetSlot) else ItemStack.EMPTY } - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { + override def setItem(slot: Int, stack: ItemStack): Unit = { val offsetSlot = slot + offset - if (isValidSlot(offsetSlot)) inventory.setInventorySlotContents(offsetSlot, stack) + if (isValidSlot(offsetSlot)) inventory.setItem(offsetSlot, stack) } - override def markDirty(): Unit = inventory.markDirty() - - override def openInventory(player: EntityPlayer): Unit = inventory.openInventory(player) - - override def closeInventory(player: EntityPlayer): Unit = inventory.closeInventory(player) - - override def setField(id: Int, value: Int): Unit = inventory.setField(id, value) + override def setChanged(): Unit = inventory.setChanged() - override def clear(): Unit = inventory.clear() + override def startOpen(player: PlayerEntity): Unit = inventory.startOpen(player) - override def getFieldCount: Int = inventory.getFieldCount + override def stopOpen(player: PlayerEntity): Unit = inventory.stopOpen(player) - override def getField(id: Int): Int = inventory.getField(id) + override def clearContent(): Unit = inventory.clearContent() - private def isValidSlot(slot: Int) = slot >= offset && slot < getSizeInventory + offset + private def isValidSlot(slot: Int) = slot >= offset && slot < getContainerSize + offset } diff --git a/src/main/scala/li/cil/oc/common/inventory/ItemStackInventory.scala b/src/main/scala/li/cil/oc/common/inventory/ItemStackInventory.scala index 1c04f22fde..9c621952e7 100644 --- a/src/main/scala/li/cil/oc/common/inventory/ItemStackInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/ItemStackInventory.scala @@ -1,13 +1,13 @@ package li.cil.oc.common.inventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT trait ItemStackInventory extends Inventory { // The item stack that provides the inventory. def container: ItemStack - private lazy val inventory = Array.fill[ItemStack](getSizeInventory)(ItemStack.EMPTY) + private lazy val inventory = Array.fill[ItemStack](getContainerSize)(ItemStack.EMPTY) override def items = inventory @@ -24,17 +24,11 @@ trait ItemStackInventory extends Inventory { for (i <- items.indices) { updateItems(i, ItemStack.EMPTY) } - if (!container.hasTagCompound) { - container.setTagCompound(new NBTTagCompound()) - } - load(container.getTagCompound) + loadData(container.getOrCreateTag) } // Write items back to tag. - override def markDirty() { - if (!container.hasTagCompound) { - container.setTagCompound(new NBTTagCompound()) - } - save(container.getTagCompound) + override def setChanged() { + saveData(container.getOrCreateTag) } } diff --git a/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala b/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala index ec03c0faaf..775cfd4130 100644 --- a/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala @@ -4,21 +4,21 @@ import li.cil.oc.api.Driver import li.cil.oc.api.internal import li.cil.oc.common.InventorySlots import li.cil.oc.util.ItemUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack trait ServerInventory extends ItemStackInventory { def tier: Int = ItemUtils.caseTier(container) max 0 - override def getSizeInventory = InventorySlots.server(tier).length + override def getContainerSize = InventorySlots.server(tier).length override protected def inventoryName = "server" - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 - override def isUsableByPlayer(player: EntityPlayer) = false + override def stillValid(player: PlayerEntity) = false - override def isItemValidForSlot(slot: Int, stack: ItemStack) = + override def canPlaceItem(slot: Int, stack: ItemStack) = Option(Driver.driverFor(stack, classOf[internal.Server])).fold(false)(driver => { val provided = InventorySlots.server(tier)(slot) driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier diff --git a/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala b/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala index 34113b7288..36b67e46d6 100644 --- a/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala @@ -1,34 +1,35 @@ package li.cil.oc.common.inventory import li.cil.oc.Localization -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack +import net.minecraft.util.INameable import net.minecraft.util.text.ITextComponent -trait SimpleInventory extends IInventory { +trait SimpleInventory extends IInventory with INameable { override def hasCustomName = false - override def getDisplayName: ITextComponent = Localization.localizeLater(getName) + override def getDisplayName: ITextComponent = getName - override def getInventoryStackLimit = 64 + override def getMaxStackSize = 64 // Items required in a slot before it's set to null (for ghost stacks). def getInventoryStackRequired = 1 - override def openInventory(player: EntityPlayer): Unit = {} + override def startOpen(player: PlayerEntity): Unit = {} - override def closeInventory(player: EntityPlayer): Unit = {} + override def stopOpen(player: PlayerEntity): Unit = {} - override def decrStackSize(slot: Int, amount: Int): ItemStack = { - if (slot >= 0 && slot < getSizeInventory) { - (getStackInSlot(slot) match { + override def removeItem(slot: Int, amount: Int): ItemStack = { + if (slot >= 0 && slot < getContainerSize) { + (getItem(slot) match { case stack: ItemStack if stack.getCount - amount < getInventoryStackRequired => - setInventorySlotContents(slot, ItemStack.EMPTY) + setItem(slot, ItemStack.EMPTY) stack case stack: ItemStack => - val result = stack.splitStack(amount) - markDirty() + val result = stack.split(amount) + setChanged() result case _ => ItemStack.EMPTY }) match { @@ -39,24 +40,18 @@ trait SimpleInventory extends IInventory { else ItemStack.EMPTY } - override def removeStackFromSlot(slot: Int) = { - if (slot >= 0 && slot < getSizeInventory) { - val stack = getStackInSlot(slot) - setInventorySlotContents(slot, ItemStack.EMPTY) + override def removeItemNoUpdate(slot: Int) = { + if (slot >= 0 && slot < getContainerSize) { + val stack = getItem(slot) + setItem(slot, ItemStack.EMPTY) stack } else ItemStack.EMPTY } - override def clear(): Unit = { - for (slot <- 0 until getSizeInventory) { - setInventorySlotContents(slot, ItemStack.EMPTY) + override def clearContent(): Unit = { + for (slot <- 0 until getContainerSize) { + setItem(slot, ItemStack.EMPTY) } } - - override def getField(id: Int) = 0 - - override def setField(id: Int, value: Int) {} - - override def getFieldCount = 0 } diff --git a/src/main/scala/li/cil/oc/common/item/APU.scala b/src/main/scala/li/cil/oc/common/item/APU.scala index 57835d6871..303c18b69a 100644 --- a/src/main/scala/li/cil/oc/common/item/APU.scala +++ b/src/main/scala/li/cil/oc/common/item/APU.scala @@ -2,7 +2,7 @@ package li.cil.oc.common.item import li.cil.oc.common.Tier import li.cil.oc.util.Rarity -import net.minecraft.item.EnumRarity +import net.minecraft.item // Rarity import net.minecraft.item.ItemStack import scala.language.existentials @@ -10,7 +10,7 @@ import scala.language.existentials class APU(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.CPULike with traits.GPULike { override val unlocalizedName = super[Delegate].unlocalizedName + tier - override def rarity(stack: ItemStack): EnumRarity = + override def rarity(stack: ItemStack): item.Rarity = if (tier == Tier.Three) Rarity.byTier(Tier.Four) else super.rarity(stack) diff --git a/src/main/scala/li/cil/oc/common/item/Acid.scala b/src/main/scala/li/cil/oc/common/item/Acid.scala index 0ba9912f2e..19d49be75d 100644 --- a/src/main/scala/li/cil/oc/common/item/Acid.scala +++ b/src/main/scala/li/cil/oc/common/item/Acid.scala @@ -3,36 +3,37 @@ package li.cil.oc.common.item import javax.annotation.Nonnull import li.cil.oc.api -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumAction +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.UseAction import net.minecraft.item.ItemStack -import net.minecraft.potion.Potion -import net.minecraft.potion.PotionEffect +import net.minecraft.potion.Effect +import net.minecraft.potion.Effects +import net.minecraft.potion.EffectInstance import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand import net.minecraft.world.World class Acid(val parent: Delegator) extends traits.Delegate { - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - player.setActiveHand(if (player.getHeldItemMainhand == stack) EnumHand.MAIN_HAND else EnumHand.OFF_HAND) - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } - override def getItemUseAction(stack: ItemStack): EnumAction = EnumAction.DRINK + override def getUseAnimation(stack: ItemStack): UseAction = UseAction.DRINK override def getMaxItemUseDuration(stack: ItemStack): Int = 32 - override def onItemUseFinish(stack: ItemStack, world: World, entity: EntityLivingBase): ItemStack = { + override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = { entity match { - case player: EntityPlayer => - if (!world.isRemote) { - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("blindness"), 200)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("poison"), 100)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("slowness"), 600)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("nausea"), 1200)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("saturation"), 2000)) + case player: PlayerEntity => + if (!world.isClientSide) { + player.addEffect(new EffectInstance(Effects.BLINDNESS, 200)) + player.addEffect(new EffectInstance(Effects.POISON, 100)) + player.addEffect(new EffectInstance(Effects.MOVEMENT_SLOWDOWN, 600)) + player.addEffect(new EffectInstance(Effects.CONFUSION, 1200)) + player.addEffect(new EffectInstance(Effects.SATURATION, 2000)) // Remove nanomachines if installed. api.Nanomachines.uninstallController(player) diff --git a/src/main/scala/li/cil/oc/common/item/Analyzer.scala b/src/main/scala/li/cil/oc/common/item/Analyzer.scala index 47935d6506..f8da846226 100644 --- a/src/main/scala/li/cil/oc/common/item/Analyzer.scala +++ b/src/main/scala/li/cil/oc/common/item/Analyzer.scala @@ -11,46 +11,47 @@ import li.cil.oc.common.tileentity import li.cil.oc.server.PacketSender import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.Util import net.minecraft.world.World import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.entity.player.PlayerInteractEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent object Analyzer { private lazy val analyzer = api.Items.get(Constants.ItemName.Analyzer) @SubscribeEvent def onInteract(e: PlayerInteractEvent.EntityInteract): Unit = { - val player = e.getEntityPlayer - val held = player.getHeldItem(e.getHand) + val player = e.getPlayer + val held = player.getItemInHand(e.getHand) if (api.Items.get(held) == analyzer) { - if (analyze(e.getTarget, player, EnumFacing.DOWN, 0, 0, 0)) { - player.swingArm(e.getHand) + if (analyze(e.getTarget, player, Direction.DOWN, 0, 0, 0)) { + player.swing(e.getHand) e.setCanceled(true) } } } - def analyze(thing: AnyRef, player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - val world = player.world + def analyze(thing: AnyRef, player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + val world = player.level thing match { case analyzable: Analyzable => - if (!world.isRemote) { + if (!world.isClientSide) { analyzeNodes(analyzable.onAnalyze(player, side, hitX, hitY, hitZ), player) } true case host: SidedEnvironment => - if (!world.isRemote) { + if (!world.isClientSide) { analyzeNodes(Array(host.sidedNode(side)), player) } true case host: Environment => - if (!world.isRemote) { + if (!world.isClientSide) { analyzeNodes(Array(host.node), player) } true @@ -59,20 +60,20 @@ object Analyzer { } } - private def analyzeNodes(nodes: Array[Node], player: EntityPlayer) = if (nodes != null) for (node <- nodes if node != null) { + private def analyzeNodes(nodes: Array[Node], player: PlayerEntity) = if (nodes != null) for (node <- nodes if node != null) { player match { case _: FakePlayer => // Nope - case playerMP: EntityPlayerMP => + case playerMP: ServerPlayerEntity => if (node != null) node.host match { case machine: Machine => if (machine != null) { if (machine.lastError != null) { - playerMP.sendMessage(Localization.Analyzer.LastError(machine.lastError)) + playerMP.sendMessage(Localization.Analyzer.LastError(machine.lastError), Util.NIL_UUID) } - playerMP.sendMessage(Localization.Analyzer.Components(machine.componentCount, machine.maxComponents)) + playerMP.sendMessage(Localization.Analyzer.Components(machine.componentCount, machine.maxComponents), Util.NIL_UUID) val list = machine.users if (list.nonEmpty) { - playerMP.sendMessage(Localization.Analyzer.Users(list)) + playerMP.sendMessage(Localization.Analyzer.Users(list), Util.NIL_UUID) } } case _ => @@ -80,19 +81,19 @@ object Analyzer { node match { case connector: Connector => if (connector.localBufferSize > 0) { - playerMP.sendMessage(Localization.Analyzer.StoredEnergy(f"${connector.localBuffer}%.2f/${connector.localBufferSize}%.2f")) + playerMP.sendMessage(Localization.Analyzer.StoredEnergy(f"${connector.localBuffer}%.2f/${connector.localBufferSize}%.2f"), Util.NIL_UUID) } - playerMP.sendMessage(Localization.Analyzer.TotalEnergy(f"${connector.globalBuffer}%.2f/${connector.globalBufferSize}%.2f")) + playerMP.sendMessage(Localization.Analyzer.TotalEnergy(f"${connector.globalBuffer}%.2f/${connector.globalBufferSize}%.2f"), Util.NIL_UUID) case _ => } node match { case component: Component => - playerMP.sendMessage(Localization.Analyzer.ComponentName(component.name)) + playerMP.sendMessage(Localization.Analyzer.ComponentName(component.name), Util.NIL_UUID) case _ => } val address = node.address() if (address != null && !address.isEmpty) { - playerMP.sendMessage(Localization.Analyzer.Address(address)) + playerMP.sendMessage(Localization.Analyzer.Address(address), Util.NIL_UUID) PacketSender.sendAnalyze(address, playerMP) } case _ => @@ -101,31 +102,28 @@ object Analyzer { } class Analyzer(val parent: Delegator) extends traits.Delegate { - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (player.isSneaking && stack.hasTagCompound) { - stack.getTagCompound.removeTag(Settings.namespace + "clipboard") - if (stack.getTagCompound.hasNoTags) { - stack.setTagCompound(null) - } + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (player.isCrouching && stack.hasTag) { + stack.removeTagKey(Settings.namespace + "clipboard") } - super.onItemRightClick(stack, world, player) + super.use(stack, world, player) } - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { - val world = player.getEntityWorld - world.getTileEntity(position) match { + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { + val world = player.level + world.getBlockEntity(position) match { case screen: tileentity.Screen if side == screen.facing => - if (player.isSneaking) { + if (player.isCrouching) { screen.copyToAnalyzer(hitX, hitY, hitZ) } - else if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "clipboard")) { - if (!world.isRemote) { - screen.origin.buffer.clipboard(stack.getTagCompound.getString(Settings.namespace + "clipboard"), player) + else if (stack.hasTag && stack.getTag.contains(Settings.namespace + "clipboard")) { + if (!world.isClientSide) { + screen.origin.buffer.clipboard(stack.getTag.getString(Settings.namespace + "clipboard"), player) } true } else false - case _ => Analyzer.analyze(position.world.get.getTileEntity(position), player, side, hitX, hitY, hitZ) + case _ => Analyzer.analyze(position.world.get.getBlockEntity(position), player, side, hitX, hitY, hitZ) } } } diff --git a/src/main/scala/li/cil/oc/common/item/Chamelium.scala b/src/main/scala/li/cil/oc/common/item/Chamelium.scala index 7486f2fac4..70bce72f40 100644 --- a/src/main/scala/li/cil/oc/common/item/Chamelium.scala +++ b/src/main/scala/li/cil/oc/common/item/Chamelium.scala @@ -1,33 +1,34 @@ package li.cil.oc.common.item import li.cil.oc.Settings -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumAction +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.UseAction import net.minecraft.item.ItemStack -import net.minecraft.potion.Potion -import net.minecraft.potion.PotionEffect +import net.minecraft.potion.Effect +import net.minecraft.potion.Effects +import net.minecraft.potion.EffectInstance import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand import net.minecraft.world.World class Chamelium(val parent: Delegator) extends traits.Delegate { - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (Settings.get.chameliumEdible) { - player.setActiveHand(if (player.getHeldItemMainhand == stack) EnumHand.MAIN_HAND else EnumHand.OFF_HAND) + player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } - override def getItemUseAction(stack: ItemStack): EnumAction = EnumAction.EAT + override def getUseAnimation(stack: ItemStack): UseAction = UseAction.EAT override def getMaxItemUseDuration(stack: ItemStack): Int = 32 - override def onItemUseFinish(stack: ItemStack, world: World, player: EntityLivingBase): ItemStack = { - if (!world.isRemote) { - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("invisibility"), 100, 0)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("blindness"), 200, 0)) + override def finishUsingItem(stack: ItemStack, world: World, player: LivingEntity): ItemStack = { + if (!world.isClientSide) { + player.addEffect(new EffectInstance(Effects.INVISIBILITY, 100, 0)) + player.addEffect(new EffectInstance(Effects.BLINDNESS, 200, 0)) } stack.shrink(1) if (stack.getCount > 0) stack diff --git a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala index 867fe47fe6..458a30df61 100644 --- a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala +++ b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.item import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.util.Rarity -import net.minecraft.item.EnumRarity +import net.minecraft.item // Rarity import net.minecraft.item.ItemStack class ComponentBus(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { @@ -11,7 +11,7 @@ class ComponentBus(val parent: Delegator, val tier: Int) extends traits.Delegate // Because the driver considers the creative bus to be tier 3, the superclass // will believe it has T3 rarity. We override that here. - override def rarity(stack: ItemStack): EnumRarity = + override def rarity(stack: ItemStack): item.Rarity = if (tier == Tier.Four) Rarity.byTier(Tier.Four) else super.rarity(stack) diff --git a/src/main/scala/li/cil/oc/common/item/CustomModel.scala b/src/main/scala/li/cil/oc/common/item/CustomModel.scala index 33a52b337a..e8a114d05c 100644 --- a/src/main/scala/li/cil/oc/common/item/CustomModel.scala +++ b/src/main/scala/li/cil/oc/common/item/CustomModel.scala @@ -1,18 +1,18 @@ package li.cil.oc.common.item -import net.minecraft.client.renderer.block.model.ModelResourceLocation +import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.item.ItemStack import net.minecraftforge.client.event.ModelBakeEvent -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait CustomModel { - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) def getModelLocation(stack: ItemStack): ModelResourceLocation - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) def registerModelLocations(): Unit = {} - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) def bakeModels(bakeEvent: ModelBakeEvent): Unit = {} } diff --git a/src/main/scala/li/cil/oc/common/item/DebugCard.scala b/src/main/scala/li/cil/oc/common/item/DebugCard.scala index 278f1009d7..2e325b28f1 100644 --- a/src/main/scala/li/cil/oc/common/item/DebugCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DebugCard.scala @@ -6,43 +6,45 @@ import li.cil.oc.Settings import li.cil.oc.Settings.DebugCardAccess import li.cil.oc.common.item.data.DebugCardData import li.cil.oc.server.component.{DebugCard => CDebugCard} -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand -import net.minecraft.util.text.TextComponentString +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand +import net.minecraft.util.Util +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World class DebugCard(val parent: Delegator) extends traits.Delegate { - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { super.tooltipExtended(stack, tooltip) val data = new DebugCardData(stack) - data.access.foreach(access => tooltip.add(s"§8${access.player}§r")) + data.access.foreach(access => tooltip.add(new StringTextComponent(s"§8${access.player}§r"))) } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (!world.isRemote && player.isSneaking) { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (!world.isClientSide && player.isCrouching) { val data = new DebugCardData(stack) val name = player.getName if (data.access.exists(_.player == name)) data.access = None else data.access = - Some(CDebugCard.AccessContext(name, Settings.get.debugCardAccess match { - case wl: DebugCardAccess.Whitelist => wl.nonce(name) match { + Some(CDebugCard.AccessContext(name.getString, Settings.get.debugCardAccess match { + case wl: DebugCardAccess.Whitelist => wl.nonce(name.getString) match { case Some(n) => n case None => - player.sendMessage(new TextComponentString("§cYou are not whitelisted to use debug card")) - player.swingArm(EnumHand.MAIN_HAND) - return new ActionResult[ItemStack](EnumActionResult.FAIL, stack) + player.sendMessage(new StringTextComponent("§cYou are not whitelisted to use debug card"), Util.NIL_UUID) + player.swing(Hand.MAIN_HAND) + return new ActionResult[ItemStack](ActionResultType.FAIL, stack) } case _ => "" })) - data.save(stack) - player.swingArm(EnumHand.MAIN_HAND) + data.saveData(stack) + player.swing(Hand.MAIN_HAND) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/Debugger.scala b/src/main/scala/li/cil/oc/common/item/Debugger.scala index 7b20cdba63..c1265b495d 100644 --- a/src/main/scala/li/cil/oc/common/item/Debugger.scala +++ b/src/main/scala/li/cil/oc/common/item/Debugger.scala @@ -5,31 +5,31 @@ import li.cil.oc.api import li.cil.oc.api.network._ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.common.util.FakePlayer class Debugger(val parent: Delegator) extends traits.Delegate { - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get player match { case _: FakePlayer => false // Nope - case realPlayer: EntityPlayerMP => - world.getTileEntity(position) match { + case realPlayer: ServerPlayerEntity => + world.getBlockEntity(position) match { case host: SidedEnvironment => - if (!world.isRemote) { + if (!world.isClientSide) { Debugger.reconnect(Array(host.sidedNode(side))) } true case host: Environment => - if (!world.isRemote) { + if (!world.isClientSide) { Debugger.reconnect(Array(host.node)) } true case _ => - if (!world.isRemote) { + if (!world.isClientSide) { Debugger.node.remove() } true diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala index fbbdca01f9..3562836b3f 100644 --- a/src/main/scala/li/cil/oc/common/item/Delegator.scala +++ b/src/main/scala/li/cil/oc/common/item/Delegator.scala @@ -1,6 +1,8 @@ package li.cil.oc.common.item import java.util + +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api.driver @@ -12,24 +14,29 @@ import li.cil.oc.common.item.traits.Delegate import li.cil.oc.integration.opencomputers.{Item => OpenComputersItem} import li.cil.oc.util.BlockPosition import net.minecraft.client.util.ITooltipFlag -import net.minecraft.creativetab.CreativeTabs +import net.minecraft.item.ItemGroup import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumAction -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.BlockItemUseContext import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.item.ItemUseContext +import net.minecraft.item.Rarity +import net.minecraft.item.UseAction import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.NonNullList import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IWorldReader import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -37,27 +44,26 @@ import scala.collection.mutable.ArrayBuffer object Delegator { def subItem(stack: ItemStack): Option[Delegate] = if (!stack.isEmpty) stack.getItem match { - case delegator: Delegator => delegator.subItem(stack.getItemDamage) + case delegator: Delegator => delegator.subItem(stack.getDamageValue) case _ => None } else None } -class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { - setHasSubtypes(true) - setCreativeTab(CreativeTab) +class Delegator extends Item(new Properties().tab(CreativeTab)) with driver.item.UpgradeRenderer with Chargeable { // ----------------------------------------------------------------------- // // SubItem // ----------------------------------------------------------------------- // + @Deprecated override def getItemStackLimit(stack: ItemStack): Int = Delegator.subItem(stack) match { case Some(subItem) => OpenComputersItem.address(stack) match { case Some(address) => 1 case _ => subItem.maxStackSize } - case _ => maxStackSize + case _ => super.getItemStackLimit(stack) } val subItems: ArrayBuffer[Delegate] = mutable.ArrayBuffer.empty[traits.Delegate] @@ -74,12 +80,12 @@ class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { case _ => None } - override def getSubItems(tab: CreativeTabs, list: NonNullList[ItemStack]) { + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) { // Workaround for MC's untyped lists... - if(isInCreativeTab(tab)){ + if(allowdedIn(tab)){ subItems.indices.filter(subItems(_).showInItemList). map(subItems(_).createItemStack()). - sortBy(_.getUnlocalizedName). + sortBy(_.getDescriptionId). foreach(list.add) } } @@ -88,18 +94,23 @@ class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { // Item // ----------------------------------------------------------------------- // - override def getUnlocalizedName(stack: ItemStack): String = + @Deprecated + private var unlocalizedName = super.getDescriptionId() + + @Deprecated + private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name + + @Deprecated + override def getDescriptionId(stack: ItemStack): String = Delegator.subItem(stack) match { case Some(subItem) => "item.oc." + subItem.unlocalizedName - case _ => getUnlocalizedName + case _ => unlocalizedName } - override def isBookEnchantable(itemA: ItemStack, itemB: ItemStack): Boolean = false - - override def getRarity(stack: ItemStack): EnumRarity = + override def getRarity(stack: ItemStack): Rarity = Delegator.subItem(stack) match { case Some(subItem) => subItem.rarity(stack) - case _ => EnumRarity.COMMON + case _ => Rarity.COMMON } // override def getColorFromItemStack(stack: ItemStack, pass: Int) = @@ -108,93 +119,99 @@ class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { // case _ => super.getColorFromItemStack(stack, pass) // } - override def getContainerItem(stack: ItemStack): ItemStack = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.getContainerItem(stack) - case _ => super.getContainerItem(stack) - } - - override def hasContainerItem(stack: ItemStack): Boolean = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.hasContainerItem(stack) - case _ => super.hasContainerItem(stack) - } - // ----------------------------------------------------------------------- // - override def doesSneakBypassUse(stack: ItemStack, world: IBlockAccess, pos: BlockPos, player: EntityPlayer): Boolean = + override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = Delegator.subItem(stack) match { case Some(subItem) => subItem.doesSneakBypassUse(world, pos, player) case _ => super.doesSneakBypassUse(stack, world, pos, player) } - override def onItemUseFirst(player: EntityPlayer, world: World, pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, hand: EnumHand): EnumActionResult = - player.getHeldItem(hand) match { - case stack:ItemStack => Delegator.subItem(stack) match { - case Some(subItem) => subItem.onItemUseFirst(stack, player, BlockPosition(pos, world), side, hitX, hitY, hitZ) - case _ => super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) + @Deprecated + override def onItemUseFirst(stack: ItemStack, ctx: ItemUseContext): ActionResultType = + if (stack != null) { + Delegator.subItem(stack) match { + case Some(subItem) => { + val pos = BlockPosition(ctx.getClickedPos, ctx.getLevel) + val hitPos = ctx.getClickLocation + subItem.onItemUseFirst(stack, ctx.getPlayer, pos, ctx.getClickedFace, + (hitPos.x - pos.x).toFloat, (hitPos.y - pos.y).toFloat, (hitPos.z - pos.z).toFloat) + } + case _ => super.onItemUseFirst(stack, ctx) } - case _ => super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) - } + } + else super.onItemUseFirst(stack, ctx) + + @Deprecated + def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand) = ActionResultType.PASS - override def onItemUse(player: EntityPlayer, world: World, pos: BlockPos, hand: EnumHand, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): EnumActionResult = - player.getHeldItem(hand) match { + override def useOn(ctx: ItemUseContext): ActionResultType = + ctx.getItemInHand match { case stack: ItemStack => Delegator.subItem(stack) match { - case Some(subItem) => if (subItem.onItemUse(stack, player, BlockPosition(pos, world), side, hitX, hitY, hitZ)) EnumActionResult.SUCCESS else EnumActionResult.PASS - case _ => super.onItemUse(player, world, pos, hand, side, hitX, hitY, hitZ) + case Some(subItem) => { + val world = ctx.getLevel + val pos = BlockPosition(ctx.getClickedPos, world) + val hitPos = ctx.getClickLocation + val success = subItem.onItemUse(ctx.getItemInHand, ctx.getPlayer, pos, ctx.getClickedFace, + (hitPos.x - pos.x).toFloat, (hitPos.y - pos.y).toFloat, (hitPos.z - pos.z).toFloat) + if (success) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + } + case _ => super.useOn(ctx) } - case _ => super.onItemUse(player, world, pos, hand, side, hitX, hitY, hitZ) + case _ => super.useOn(ctx) } - override def onItemRightClick(world: World, player: EntityPlayer, hand: EnumHand): ActionResult[ItemStack] = - player.getHeldItem(hand) match { + override def use(world: World, player: PlayerEntity, hand: Hand): ActionResult[ItemStack] = + player.getItemInHand(hand) match { case stack: ItemStack => Delegator.subItem(stack) match { - case Some(subItem) => subItem.onItemRightClick(stack, world, player) - case _ => super.onItemRightClick(world, player, hand) + case Some(subItem) => subItem.use(stack, world, player) + case _ => super.use(world, player, hand) } - case _ => super.onItemRightClick(world, player, hand) + case _ => super.use(world, player, hand) } // ----------------------------------------------------------------------- // - override def onItemUseFinish(stack: ItemStack, world: World, entity: EntityLivingBase): ItemStack = + override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = Delegator.subItem(stack) match { - case Some(subItem) => subItem.onItemUseFinish(stack, world, entity) - case _ => super.onItemUseFinish(stack, world, entity) + case Some(subItem) => subItem.finishUsingItem(stack, world, entity) + case _ => super.finishUsingItem(stack, world, entity) } - override def getItemUseAction(stack: ItemStack): EnumAction = + override def getUseAnimation(stack: ItemStack): UseAction = Delegator.subItem(stack) match { - case Some(subItem) => subItem.getItemUseAction(stack) - case _ => super.getItemUseAction(stack) + case Some(subItem) => subItem.getUseAnimation(stack) + case _ => super.getUseAnimation(stack) } - override def getMaxItemUseDuration(stack: ItemStack): Int = + override def getUseDuration(stack: ItemStack): Int = Delegator.subItem(stack) match { case Some(subItem) => subItem.getMaxItemUseDuration(stack) - case _ => super.getMaxItemUseDuration(stack) + case _ => super.getUseDuration(stack) } - override def onPlayerStoppedUsing(stack: ItemStack, world: World, entity: EntityLivingBase, timeLeft: Int): Unit = + override def releaseUsing(stack: ItemStack, world: World, entity: LivingEntity, timeLeft: Int): Unit = Delegator.subItem(stack) match { case Some(subItem) => subItem.onPlayerStoppedUsing(stack, entity, timeLeft) - case _ => super.onPlayerStoppedUsing(stack, world, entity, timeLeft) + case _ => super.releaseUsing(stack, world, entity, timeLeft) } - def internalGetItemStackDisplayName(stack: ItemStack): String = super.getItemStackDisplayName(stack) + @Deprecated + def internalGetItemStackDisplayName(stack: ItemStack): String = super.getName(stack).getString - override def getItemStackDisplayName(stack: ItemStack): String = + @Deprecated + override def getName(stack: ItemStack): ITextComponent = Delegator.subItem(stack) match { case Some(subItem) => subItem.displayName(stack) match { - case Some(name) => name - case _ => super.getItemStackDisplayName(stack) + case Some(name) => new StringTextComponent(name) + case _ => super.getName(stack) } - case _ => super.getItemStackDisplayName(stack) + case _ => super.getName(stack) } - @SideOnly(Side.CLIENT) - override def addInformation(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - super.addInformation(stack, world, tooltip, flag) + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) Delegator.subItem(stack) match { case Some(subItem) => try subItem.tooltipLines(stack, world, tooltip, flag) catch { case t: Throwable => OpenComputers.log.warn("Error in item tooltip.", t) @@ -215,13 +232,13 @@ class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { case _ => super.showDurabilityBar(stack) } - override def onUpdate(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean): Unit = + override def inventoryTick(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean): Unit = Delegator.subItem(stack) match { case Some(subItem) => subItem.update(stack, world, player, slot, selected) - case _ => super.onUpdate(stack, world, player, slot, selected) + case _ => super.inventoryTick(stack, world, player, slot, selected) } - override def toString: String = getUnlocalizedName + override def toString: String = getDescriptionId // ----------------------------------------------------------------------- // @@ -241,5 +258,5 @@ class Delegator extends Item with driver.item.UpgradeRenderer with Chargeable { override def computePreferredMountPoint(stack: ItemStack, robot: Robot, availableMountPoints: util.Set[String]): String = UpgradeRenderer.preferredMountPoint(stack, availableMountPoints) - override def render(stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(stack, mountPoint) + override def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(matrix, stack, mountPoint) } diff --git a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala index 5f275155a9..2157349be3 100644 --- a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala @@ -2,20 +2,20 @@ package li.cil.oc.common.item import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.{ActionResult, EnumActionResult, EnumHand} +import net.minecraft.util.{ActionResult, ActionResultType, Hand} import net.minecraft.world.World class DiskDriveMountable(val parent: Delegator) extends traits.Delegate { override def maxStackSize = 1 - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = { + override def use(stack: ItemStack, world: World, player: PlayerEntity) = { // Open the GUI immediately on the client, too, to avoid the player // changing the current slot before it actually opens, which can lead to // desynchronization of the player inventory. - player.openGui(OpenComputers, GuiType.DiskDriveMountable.id, world, 0, 0, 0) - player.swingArm(EnumHand.MAIN_HAND) - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + OpenComputers.openGui(player, GuiType.DiskDriveMountable.id, world, 0, 0, 0) + player.swing(Hand.MAIN_HAND) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index e6df199904..8318885708 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -12,32 +12,34 @@ import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.agent import li.cil.oc.util.BlockPosition import li.cil.oc.util.Rarity -import net.minecraft.client.renderer.block.model.ModelResourceLocation -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.client.renderer.model.ModelResourceLocation +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraftforge.client.event.ModelBakeEvent -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class Drone(val parent: Delegator) extends traits.Delegate with CustomModel { ItemBlacklist.hide(this) showInItemList = false - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack) = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Drone, "inventory") - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def bakeModels(bakeEvent: ModelBakeEvent): Unit = { - bakeEvent.getModelRegistry.putObject(getModelLocation(createItemStack()), DroneModel) + bakeEvent.getModelRegistry.put(getModelLocation(createItemStack()), DroneModel) } - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { if (KeyBindings.showExtendedTooltips) { val info = new DroneData(stack) for (component <- info.components if !component.isEmpty) { - tooltip.add("- " + component.getDisplayName) + tooltip.add(new StringTextComponent("- " + component.getDisplayName)) } } } @@ -47,20 +49,20 @@ class Drone(val parent: Delegator) extends traits.Delegate with CustomModel { Rarity.byTier(data.tier) } - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get - if (!world.isRemote) { + if (!world.isClientSide) { val drone = new entity.Drone(world) player match { case fakePlayer: agent.Player => drone.ownerName = fakePlayer.agent.ownerName drone.ownerUUID = fakePlayer.agent.ownerUUID case _ => - drone.ownerName = player.getName + drone.ownerName = player.getName.getString drone.ownerUUID = player.getGameProfile.getId } drone.initializeAfterPlacement(stack, player, position.offset(hitX * 1.1f, hitY * 1.1f, hitZ * 1.1f)) - world.spawnEntity(drone) + world.addFreshEntity(drone) } stack.shrink(1) true diff --git a/src/main/scala/li/cil/oc/common/item/EEPROM.scala b/src/main/scala/li/cil/oc/common/item/EEPROM.scala index b39cd96913..bc48baaaa7 100644 --- a/src/main/scala/li/cil/oc/common/item/EEPROM.scala +++ b/src/main/scala/li/cil/oc/common/item/EEPROM.scala @@ -2,18 +2,18 @@ package li.cil.oc.common.item import li.cil.oc.Settings import li.cil.oc.util.BlockPosition -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader class EEPROM(val parent: Delegator) extends traits.Delegate { override def displayName(stack: ItemStack): Option[String] = { - if (stack.hasTagCompound) { - val tag = stack.getTagCompound - if (tag.hasKey(Settings.namespace + "data")) { - val data = tag.getCompoundTag(Settings.namespace + "data") - if (data.hasKey(Settings.namespace + "label")) { + if (stack.hasTag) { + val tag = stack.getTag + if (tag.contains(Settings.namespace + "data")) { + val data = tag.getCompound(Settings.namespace + "data") + if (data.contains(Settings.namespace + "label")) { return Some(data.getString(Settings.namespace + "label")) } } @@ -21,5 +21,5 @@ class EEPROM(val parent: Delegator) extends traits.Delegate { super.displayName(stack) } - override def doesSneakBypassUse(world: IBlockAccess, pos: BlockPos, player: EntityPlayer): Boolean = true + override def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index c122216b9b..56144d8628 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -3,15 +3,16 @@ package li.cil.oc.common.item import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.util.Color -import net.minecraft.client.renderer.block.model.ModelBakery -import net.minecraft.client.renderer.block.model.ModelResourceLocation -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.client.renderer.model.ModelResourceLocation +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.world.IBlockReader +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.client.model.ModelLoader class FloppyDisk(val parent: Delegator) extends traits.Delegate with CustomModel with traits.FileSystemLike { // Necessary for anonymous subclasses used for loot disks. @@ -19,28 +20,28 @@ class FloppyDisk(val parent: Delegator) extends traits.Delegate with CustomModel val kiloBytes = Settings.get.floppySize - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) private def modelLocationFromDyeName(name: String) = { new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Floppy + "_" + name, "inventory") } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack): ModelResourceLocation = { val dyeIndex = - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "color")) - stack.getTagCompound.getInteger(Settings.namespace + "color") + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "color")) + stack.getTag.getInt(Settings.namespace + "color") else 8 modelLocationFromDyeName(Color.dyes(dyeIndex max 0 min 15)) } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { for (dyeName <- Color.dyes) { val location = modelLocationFromDyeName(dyeName) - ModelBakery.registerItemVariants(parent, new ResourceLocation(location.getResourceDomain + ":" + location.getResourcePath)) + ModelLoader.addSpecialModel(location) } } - override def doesSneakBypassUse(world: IBlockAccess, pos: BlockPos, player: EntityPlayer): Boolean = true + override def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index 722a44059d..3305f8649e 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -1,30 +1,34 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.client.renderer.item.HoverBootRenderer import li.cil.oc.common.item.data.HoverBootsData import li.cil.oc.util.ItemColorizer -import net.minecraft.block.BlockCauldron -import net.minecraft.client.model.ModelBiped +import net.minecraft.block.CauldronBlock +import net.minecraft.client.renderer.entity.model.BipedModel import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.Blocks -import net.minecraft.inventory.EntityEquipmentSlot -import net.minecraft.item.EnumRarity -import net.minecraft.item.ItemArmor +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.block.Blocks +import net.minecraft.inventory.EquipmentSlotType +import net.minecraft.item.ArmorItem +import net.minecraft.item.ArmorMaterial +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack -import net.minecraft.potion.Potion -import net.minecraft.potion.PotionEffect +import net.minecraft.item.Rarity +import net.minecraft.potion.Effect +import net.minecraft.potion.Effects +import net.minecraft.potion.EffectInstance import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, EntityEquipmentSlot.FEET) with traits.SimpleItem with traits.Chargeable { - setNoRepair() +class HoverBoots(props: Properties = new Properties().tab(CreativeTab).setNoRepair()) + extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with traits.SimpleItem with traits.Chargeable { - override def getRarity(stack: ItemStack): EnumRarity = EnumRarity.UNCOMMON + override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON override def maxCharge(stack: ItemStack): Double = Settings.get.bufferHoverBoots @@ -34,7 +38,7 @@ class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, EntityEqu override def setCharge(stack: ItemStack, amount: Double): Unit = { val data = new HoverBootsData(stack) data.charge = math.min(maxCharge(stack), math.max(0, amount)) - data.save(stack) + data.saveData(stack) } override def canCharge(stack: ItemStack): Boolean = true @@ -43,45 +47,45 @@ class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, EntityEqu val data = new HoverBootsData(stack) traits.Chargeable.applyCharge(amount, data.charge, Settings.get.bufferHoverBoots, used => if (!simulate) { data.charge += used - data.save(stack) + data.saveData(stack) }) } - @SideOnly(Side.CLIENT) - override def getArmorModel(entityLiving: EntityLivingBase, itemStack: ItemStack, armorSlot: EntityEquipmentSlot, _default: ModelBiped): ModelBiped = { - if (armorSlot == armorType) { + @OnlyIn(Dist.CLIENT) + override def getArmorModel[A <: BipedModel[_]](entityLiving: LivingEntity, itemStack: ItemStack, armorSlot: EquipmentSlotType, _default: A): A = { + if (armorSlot == slot) { HoverBootRenderer.lightColor = if (ItemColorizer.hasColor(itemStack)) ItemColorizer.getColor(itemStack) else 0x66DD55 - HoverBootRenderer + HoverBootRenderer.asInstanceOf[A] } else super.getArmorModel(entityLiving, itemStack, armorSlot, _default) } - override def getArmorTexture(stack: ItemStack, entity: Entity, slot: EntityEquipmentSlot, subType: String): String = { - if (entity.world.isRemote) HoverBootRenderer.texture.toString + override def getArmorTexture(stack: ItemStack, entity: Entity, slot: EquipmentSlotType, subType: String): String = { + if (entity.level.isClientSide) HoverBootRenderer.texture.toString else null } - override def onArmorTick(world: World, player: EntityPlayer, stack: ItemStack): Unit = { - super.onArmorTick(world, player, stack) - if (!Settings.get.ignorePower && player.getActivePotionEffect(Potion.getPotionFromResourceLocation("slowness")) == null && getCharge(stack) == 0) { - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("slowness"), 20, 1)) + override def onArmorTick(stack: ItemStack, world: World, player: PlayerEntity): Unit = { + super.onArmorTick(stack, world, player) + if (!Settings.get.ignorePower && player.getEffect(Effects.MOVEMENT_SLOWDOWN) == null && getCharge(stack) == 0) { + player.addEffect(new EffectInstance(Effects.MOVEMENT_SLOWDOWN, 20, 1)) } } - override def onEntityItemUpdate(entity: EntityItem): Boolean = { - if (entity != null && entity.world != null && !entity.world.isRemote && ItemColorizer.hasColor(entity.getItem)) { - val pos = entity.getPosition - val state = entity.world.getBlockState(pos) + override def onEntityItemUpdate(stack: ItemStack, entity: ItemEntity): Boolean = { + if (entity != null && entity.level != null && !entity.level.isClientSide && ItemColorizer.hasColor(stack)) { + val pos = entity.blockPosition + val state = entity.level.getBlockState(pos) if (state.getBlock == Blocks.CAULDRON) { - val level = state.getValue(BlockCauldron.LEVEL).toInt + val level = state.getValue(CauldronBlock.LEVEL).toInt if (level > 0) { - ItemColorizer.removeColor(entity.getItem) - entity.world.setBlockState(pos, state.withProperty(BlockCauldron.LEVEL, Int.box(level - 1)), 3) + ItemColorizer.removeColor(stack) + entity.level.setBlock(pos, state.setValue(CauldronBlock.LEVEL, Int.box(level - 1)), 3) return true } } } - super.onEntityItemUpdate(entity) + super.onEntityItemUpdate(stack, entity) } override def showDurabilityBar(stack: ItemStack): Boolean = true @@ -97,7 +101,7 @@ class HoverBoots extends ItemArmor(ItemArmor.ArmorMaterial.DIAMOND, 0, EntityEqu override def isDamaged(stack: ItemStack): Boolean = true // Contradictory as it may seem with the above, this avoids actual damage value changing. - override def isDamageable: Boolean = false + override def canBeDepleted: Boolean = false override def setDamage(stack: ItemStack, damage: Int): Unit = { // Subtract energy when taking damage instead of actually damaging the item. diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala index 1477984fae..9a231ae89f 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala @@ -2,17 +2,13 @@ package li.cil.oc.common.item import li.cil.oc.Constants import li.cil.oc.api +import net.minecraft.item.Item import net.minecraft.item.ItemStack class InkCartridge(val parent: Delegator) extends traits.Delegate { override def maxStackSize = 1 - override def getContainerItem(stack: ItemStack): ItemStack = { - if (api.Items.get(stack) == api.Items.get(Constants.ItemName.InkCartridge)) - api.Items.get(Constants.ItemName.InkCartridgeEmpty).createItemStack(1) - else - super.getContainerItem(stack) - } + override def getCraftingRemainingItem(): Item = api.Items.get(Constants.ItemName.InkCartridgeEmpty).item - override def hasContainerItem(stack: ItemStack): Boolean = true + override def hasCraftingRemainingItem(): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala index 11a24cccec..8f48a9d4dd 100644 --- a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala @@ -5,21 +5,29 @@ import java.util import li.cil.oc.Settings import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import scala.collection.convert.WrapAsScala._ + class LinkedCard(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "data")) { - val data = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") - if (data.hasKey(Settings.namespace + "tunnel")) { + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { + val data = stack.getTag.getCompound(Settings.namespace + "data") + if (data.contains(Settings.namespace + "tunnel")) { val channel = data.getString(Settings.namespace + "tunnel") if (channel.length > 13) { - tooltip.addAll(Tooltip.get(unlocalizedName + "_channel", channel.substring(0, 13) + "...")) + for (curr <- Tooltip.get(unlocalizedName + "_channel", channel.substring(0, 13) + "...")) { + tooltip.add(new StringTextComponent(curr)) + } } else { - tooltip.addAll(Tooltip.get(unlocalizedName + "_channel", channel)) + for (curr <- Tooltip.get(unlocalizedName + "_channel", channel)) { + tooltip.add(new StringTextComponent(curr)) + } } } } diff --git a/src/main/scala/li/cil/oc/common/item/Manual.scala b/src/main/scala/li/cil/oc/common/item/Manual.scala index bafecea267..497b26235b 100644 --- a/src/main/scala/li/cil/oc/common/item/Manual.scala +++ b/src/main/scala/li/cil/oc/common/item/Manual.scala @@ -6,38 +6,40 @@ import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.BlockPosition import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.TextFormatting import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class Manual(val parent: Delegator) extends traits.Delegate { - @SideOnly(Side.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag): Unit = { - tooltip.add(TextFormatting.DARK_GRAY.toString + "v" + OpenComputers.Version) + @OnlyIn(Dist.CLIENT) + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + tooltip.add(new StringTextComponent(TextFormatting.DARK_GRAY.toString + "v" + OpenComputers.Version)) super.tooltipLines(stack, world, tooltip, flag) } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (world.isRemote) { - if (player.isSneaking) { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (world.isClientSide) { + if (player.isCrouching) { api.Manual.reset() } api.Manual.openFor(player) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - val world = player.getEntityWorld + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + val world = player.level api.Manual.pathFor(world, position.toBlockPos) match { case path: String => - if (world.isRemote) { + if (world.isClientSide) { api.Manual.openFor(player) api.Manual.reset() api.Manual.navigate(path) diff --git a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala index f7e179bd8a..0da52e65b7 100644 --- a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala @@ -7,45 +7,47 @@ import li.cil.oc.api import li.cil.oc.common.item.data.NanomachineData import li.cil.oc.common.nanomachines.ControllerImpl import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumAction -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.UseAction +import net.minecraft.item.Rarity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class Nanomachines(val parent: Delegator) extends traits.Delegate { - override def rarity(stack: ItemStack): EnumRarity = EnumRarity.UNCOMMON + override def rarity(stack: ItemStack): Rarity = Rarity.UNCOMMON - @SideOnly(Side.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag): Unit = { + @OnlyIn(Dist.CLIENT) + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { super.tooltipLines(stack, world, tooltip, flag) - if (stack.hasTagCompound) { + if (stack.hasTag) { val data = new NanomachineData(stack) if (!Strings.isNullOrEmpty(data.uuid)) { - tooltip.add("§8" + data.uuid.substring(0, 13) + "...§7") + tooltip.add(new StringTextComponent("§8" + data.uuid.substring(0, 13) + "...§7")) } } } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - player.setActiveHand(if (player.getHeldItemMainhand == stack) EnumHand.MAIN_HAND else EnumHand.OFF_HAND) - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } - override def getItemUseAction(stack: ItemStack): EnumAction = EnumAction.EAT + override def getUseAnimation(stack: ItemStack): UseAction = UseAction.EAT override def getMaxItemUseDuration(stack: ItemStack): Int = 32 - override def onItemUseFinish(stack: ItemStack, world: World, entity: EntityLivingBase): ItemStack = { + override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = { entity match { - case player: EntityPlayer => - if (!world.isRemote) { + case player: PlayerEntity => + if (!world.isClientSide) { val data = new NanomachineData(stack) // Re-install to get new address, make sure we're configured. @@ -57,7 +59,7 @@ class Nanomachines(val parent: Delegator) extends traits.Delegate { if (!Strings.isNullOrEmpty(data.uuid)) { controller.uuid = data.uuid } - controller.configuration.load(nbt) + controller.configuration.loadData(nbt) case _ => controller.reconfigure() } case controller => controller.reconfigure() // Huh. diff --git a/src/main/scala/li/cil/oc/common/item/Present.scala b/src/main/scala/li/cil/oc/common/item/Present.scala index 62684d1c5f..35025bf2db 100644 --- a/src/main/scala/li/cil/oc/common/item/Present.scala +++ b/src/main/scala/li/cil/oc/common/item/Present.scala @@ -7,11 +7,12 @@ import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ItemUtils -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.SoundEvents +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.SoundEvents import net.minecraft.item.ItemStack +import net.minecraft.item.crafting.RecipeManager import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult +import net.minecraft.util.ActionResultType import net.minecraft.util.SoundCategory import net.minecraft.world.World @@ -20,20 +21,23 @@ import scala.collection.mutable class Present(val parent: Delegator) extends traits.Delegate { showInItemList = false - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (stack.getCount > 0) { stack.shrink(1) - if (!world.isRemote) { - world.playSound(player, player.posX, player.posY, player.posZ, SoundEvents.ENTITY_PLAYER_LEVELUP, SoundCategory.MASTER, 0.2f, 1f) + if (!world.isClientSide) { + world.playSound(player, player.getX, player.getY, player.getZ, SoundEvents.PLAYER_LEVELUP, SoundCategory.MASTER, 0.2f, 1f) + Present.recipeManager = world.getRecipeManager val present = Present.nextPresent() InventoryUtils.addToPlayerInventory(present, player) } } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } object Present { + private var recipeManager: RecipeManager = null + private lazy val Presents = { val result = mutable.ArrayBuffer.empty[ItemStack] @@ -42,7 +46,7 @@ object Present { if (item != null) { val stack = item.createItemStack(1) // Only if it can be crafted (wasn't disabled in the config). - if (ItemUtils.getIngredients(stack).nonEmpty) { + if (ItemUtils.getIngredients(recipeManager, stack).nonEmpty) { for (i <- 0 until weight) result += stack } } @@ -134,5 +138,5 @@ object Present { private val rng = new Random() - def nextPresent(): ItemStack = Presents(rng.nextInt(Presents.length)).copy() + private def nextPresent(): ItemStack = Presents(rng.nextInt(Presents.length)).copy() } diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index 953fa1001d..a08a0f6c53 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -8,22 +8,25 @@ import li.cil.oc.common.GuiType import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumRarity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item // Rarity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import scala.collection.mutable +import scala.collection.convert.WrapAsScala._ class Server(val parent: Delegator, val tier: Int) extends traits.Delegate { override val unlocalizedName: String = super.unlocalizedName + tier override protected def tooltipName = Option(super.unlocalizedName) - override def rarity(stack: ItemStack): EnumRarity = Rarity.byTier(tier) + override def rarity(stack: ItemStack): item.Rarity = Rarity.byTier(tier) override def maxStackSize = 1 @@ -31,34 +34,36 @@ class Server(val parent: Delegator, val tier: Int) extends traits.Delegate { var container = ItemStack.EMPTY } - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) { + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { super.tooltipExtended(stack, tooltip) if (KeyBindings.showExtendedTooltips) { HelperInventory.container = stack HelperInventory.reinitialize() val stacks = mutable.Map.empty[String, Int] - for (aStack <- (0 until HelperInventory.getSizeInventory).map(HelperInventory.getStackInSlot) if !aStack.isEmpty) { - val displayName = aStack.getDisplayName + for (aStack <- (0 until HelperInventory.getContainerSize).map(HelperInventory.getItem) if !aStack.isEmpty) { + val displayName = aStack.getDisplayName.getString stacks += displayName -> (if (stacks.contains(displayName)) stacks(displayName) + 1 else 1) } if (stacks.nonEmpty) { - tooltip.addAll(Tooltip.get("server.Components")) + for (curr <- Tooltip.get("server.Components")) { + tooltip.add(new StringTextComponent(curr)) + } for (itemName <- stacks.keys.toArray.sorted) { - tooltip.add("- " + stacks(itemName) + "x " + itemName) + tooltip.add(new StringTextComponent("- " + stacks(itemName) + "x " + itemName)) } } } } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (!player.isSneaking) { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (!player.isCrouching) { // Open the GUI immediately on the client, too, to avoid the player // changing the current slot before it actually opens, which can lead to // desynchronization of the player inventory. - player.openGui(OpenComputers, GuiType.Server.id, world, 0, 0, 0) - player.swingArm(EnumHand.MAIN_HAND) + OpenComputers.openGui(player, GuiType.Server.id, world, 0, 0, 0) + player.swing(Hand.MAIN_HAND) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index fd690ae722..64a38428c6 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -37,24 +37,32 @@ import li.cil.oc.util.Rarity import li.cil.oc.util.RotationHelper import li.cil.oc.util.Tooltip import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.block.model.ModelBakery -import net.minecraft.client.renderer.block.model.ModelResourceLocation +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.{EntityPlayer, EntityPlayerMP} -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.{PlayerEntity, ServerPlayerEntity} +import net.minecraft.item // Rarity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.server.integrated.IntegratedServer -import net.minecraft.util._ +import net.minecraft.util.ActionResult +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.ResourceLocation +import net.minecraft.util.Util +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.FMLCommonHandler -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.event.TickEvent.ClientTickEvent +import net.minecraftforge.event.TickEvent.ServerTickEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -69,21 +77,23 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit // ----------------------------------------------------------------------- // - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = { + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { if (KeyBindings.showExtendedTooltips) { val info = new TabletData(stack) // Ignore/hide the screen. val components = info.items.drop(1) if (components.length > 1) { - tooltip.addAll(Tooltip.get("server.Components")) + for (curr <- Tooltip.get("server.Components")) { + tooltip.add(new StringTextComponent(curr)) + } components.collect { - case component if !component.isEmpty => tooltip.add("- " + component.getDisplayName) + case component if !component.isEmpty => tooltip.add(new StringTextComponent("- " + component.getDisplayName)) } } } } - override def rarity(stack: ItemStack): EnumRarity = { + override def rarity(stack: ItemStack): item.Rarity = { val data = new TabletData(stack) Rarity.byTier(data.tier) } @@ -91,7 +101,7 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit override def showDurabilityBar(stack: ItemStack) = true override def durability(stack: ItemStack): Double = { - if (stack.hasTagCompound) { + if (stack.hasTag) { val data = Tablet.Client.getWeak(stack) match { case Some(wrapper) => wrapper.data case _ => new TabletData(stack) @@ -103,7 +113,7 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) private def modelLocationFromState(running: Option[Boolean]) = { val suffix = running match { case Some(state) => if (state) "_on" else "_off" @@ -112,7 +122,7 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Tablet + suffix, "inventory") } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack): ModelResourceLocation = { modelLocationFromState(Tablet.Client.getWeak(stack) match { case Some(tablet: TabletWrapper) => Some(tablet.data.isRunning) @@ -120,11 +130,11 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit }) } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { for (state <- Seq(None, Some(true), Some(false))) { val location = modelLocationFromState(state) - ModelBakery.registerItemVariants(parent, new ResourceLocation(location.getResourceDomain + ":" + location.getResourcePath)) + ModelLoader.addSpecialModel(new ResourceLocation(location.getNamespace + ":" + location.getPath)) } } @@ -135,7 +145,7 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit val data = new TabletData(stack) traits.Chargeable.applyCharge(amount, data.energy, data.maxEnergy, used => if (!simulate) { data.energy += used - data.save(stack) + data.saveData(stack) }) } @@ -143,46 +153,46 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit override def update(stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean): Unit = entity match { - case player: EntityPlayer => + case player: PlayerEntity => // Play an audio cue to let players know when they finished analyzing a block. - if (world.isRemote && player.getItemInUseCount == TimeToAnalyze && api.Items.get(player.getActiveItemStack) == api.Items.get(Constants.ItemName.Tablet)) { - Audio.play(player.posX.toFloat, player.posY.toFloat + 2, player.posZ.toFloat, ".") + if (world.isClientSide && player.getUseItemRemainingTicks == TimeToAnalyze && api.Items.get(player.getUseItem) == api.Items.get(Constants.ItemName.Tablet)) { + Audio.play(player.getX.toFloat, player.getY.toFloat + 2, player.getZ.toFloat, ".") } Tablet.get(stack, player).update(world, player, slot, selected) case _ => } - override def onItemUseFirst(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): EnumActionResult = { + override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = { Tablet.currentlyAnalyzing = Some((position, side, hitX, hitY, hitZ)) super.onItemUseFirst(stack, player, position, side, hitX, hitY, hitZ) } - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - player.setActiveHand(if (player.getHeldItemMainhand == stack) EnumHand.MAIN_HAND else EnumHand.OFF_HAND) + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) true } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - player.setActiveHand(if (player.getHeldItemMainhand == stack) EnumHand.MAIN_HAND else EnumHand.OFF_HAND) - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } override def getMaxItemUseDuration(stack: ItemStack): Int = 72000 - override def onPlayerStoppedUsing(stack: ItemStack, entity: EntityLivingBase, duration: Int): Unit = { + override def onPlayerStoppedUsing(stack: ItemStack, entity: LivingEntity, duration: Int): Unit = { entity match { - case player: EntityPlayer => - val world = player.getEntityWorld + case player: PlayerEntity => + val world = player.level val didAnalyze = getMaxItemUseDuration(stack) - duration >= TimeToAnalyze if (didAnalyze) { - if (!world.isRemote) { + if (!world.isClientSide) { Tablet.currentlyAnalyzing match { case Some((position, side, hitX, hitY, hitZ)) => try { val computer = Tablet.get(stack, player).machine if (computer.isRunning) { - val data = new NBTTagCompound() + val data = new CompoundNBT() computer.node.sendToReachable("tablet.use", data, stack, player, position, side, Float.box(hitX), Float.box(hitY), Float.box(hitZ)) - if (!data.hasNoTags) { + if (!data.isEmpty) { computer.signal("tablet_use", data) } } @@ -195,26 +205,26 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit } } else { - if (player.isSneaking) { - if (!world.isRemote) { + if (player.isCrouching) { + if (!world.isClientSide) { val tablet = Tablet.Server.get(stack, player) tablet.machine.stop() if (tablet.data.tier > Tier.One) { - player.openGui(OpenComputers, GuiType.TabletInner.id, world, 0, 0, 0) + OpenComputers.openGui(player, GuiType.TabletInner.id, world, 0, 0, 0) } } } else { - if (!world.isRemote) { + if (!world.isClientSide) { val computer = Tablet.get(stack, player).machine computer.start() computer.lastError match { - case message if message != null => player.sendMessage(Localization.Analyzer.LastError(message)) + case message if message != null => player.sendMessage(Localization.Analyzer.LastError(message), Util.NIL_UUID) case _ => } } else { - player.openGui(OpenComputers, GuiType.Tablet.id, world, 0, 0, 0) + OpenComputers.openGui(player, GuiType.Tablet.id, world, 0, 0, 0) } } } @@ -229,24 +239,24 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit override def setCharge(stack: ItemStack, amount: Double): Unit = { val data = new TabletData(stack) data.energy = (0.0 max amount) min maxCharge(stack) - data.save(stack) + data.saveData(stack) } } -class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends ComponentInventory with MachineHost with internal.Tablet { +class TabletWrapper(var stack: ItemStack, var player: PlayerEntity) extends ComponentInventory with MachineHost with internal.Tablet { // Remember our *original* world, so we know which tablets to clear on dimension // changes of players holding tablets - since the player entity instance may be // kept the same and components are not required to properly handle world changes. - val world: World = player.world + val world: World = player.level - lazy val machine: api.machine.Machine = if (world.isRemote) null else Machine.create(this) + lazy val machine: api.machine.Machine = if (world.isClientSide) null else Machine.create(this) val data = new TabletData() - val tablet: component.Tablet = if (world.isRemote) null else new component.Tablet(this) + val tablet: component.Tablet = if (world.isClientSide) null else new component.Tablet(this) //// Client side only - private var isInitialized = !world.isRemote + private var isInitialized = !world.isClientSide var timesChanged: Int = 0 @@ -263,48 +273,45 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp def items: Array[ItemStack] = data.items - override def facing: EnumFacing = RotationHelper.fromYaw(player.rotationYaw) + override def facing: Direction = RotationHelper.fromYaw(player.yRot) - override def toLocal(value: EnumFacing): EnumFacing = - RotationHelper.toLocal(EnumFacing.NORTH, facing, value) + override def toLocal(value: Direction): Direction = + RotationHelper.toLocal(Direction.NORTH, facing, value) - override def toGlobal(value: EnumFacing): EnumFacing = - RotationHelper.toGlobal(EnumFacing.NORTH, facing, value) + override def toGlobal(value: Direction): Direction = + RotationHelper.toGlobal(Direction.NORTH, facing, value) def readFromNBT() { - if (stack.hasTagCompound) { - val data = stack.getTagCompound - load(data) - if (!world.isRemote) { - tablet.load(data.getCompoundTag(Settings.namespace + "component")) - machine.load(data.getCompoundTag(Settings.namespace + "data")) + if (stack.hasTag) { + val data = stack.getTag + loadData(data) + if (!world.isClientSide) { + tablet.loadData(data.getCompound(Settings.namespace + "component")) + machine.loadData(data.getCompound(Settings.namespace + "data")) } } } def writeToNBT(clearState: Boolean = true) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - val data = stack.getTagCompound - if (!world.isRemote) { - if (!data.hasKey(Settings.namespace + "data")) { - data.setTag(Settings.namespace + "data", new NBTTagCompound()) + val data = stack.getOrCreateTag + if (!world.isClientSide) { + if (!data.contains(Settings.namespace + "data")) { + data.put(Settings.namespace + "data", new CompoundNBT()) } - data.setNewCompoundTag(Settings.namespace + "component", tablet.save) - data.setNewCompoundTag(Settings.namespace + "data", machine.save) + data.setNewCompoundTag(Settings.namespace + "component", tablet.saveData(_)) + data.setNewCompoundTag(Settings.namespace + "data", machine.saveData(_)) if (clearState) { // Force tablets into stopped state to avoid errors when trying to // load deleted machine states. - data.getCompoundTag(Settings.namespace + "data").removeTag("state") + data.getCompound(Settings.namespace + "data").remove("state") } } - save(data) + saveData(data) } readFromNBT() - if (!world.isRemote) { + if (!world.isClientSide) { api.Network.joinNewNetwork(machine.node) val charge = Math.max(0, this.data.energy - tablet.node.globalBuffer) tablet.node.changeBuffer(charge) @@ -350,9 +357,9 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp override def host: TabletWrapper = this - override def getSizeInventory: Int = items.length + override def getContainerSize: Int = items.length - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = slot == getSizeInventory - 1 && (Option(Driver.driverFor(stack, getClass)) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = slot == getContainerSize - 1 && (Option(Driver.driverFor(stack, getClass)) match { case Some(driver) => // Same special cases, similar as in robot, but allow keyboards, // because clip-on keyboards kinda seem to make sense, I guess. @@ -362,20 +369,20 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp case _ => false }) - override def isUsableByPlayer(player: EntityPlayer): Boolean = machine != null && machine.canInteract(player.getName) + override def stillValid(player: PlayerEntity): Boolean = machine != null && machine.canInteract(player.getName.getString) - override def markDirty(): Unit = { - data.save(stack) - player.inventory.markDirty() + override def setChanged(): Unit = { + data.saveData(stack) + player.inventory.setChanged() } // ----------------------------------------------------------------------- // - override def xPosition: Double = player.posX + override def xPosition: Double = player.getX - override def yPosition: Double = player.posY + player.getEyeHeight + override def yPosition: Double = player.getY + player.getEyeHeight - override def zPosition: Double = player.posZ + override def zPosition: Double = player.getZ override def markChanged() {} @@ -395,8 +402,8 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp case _ => Tier.None } - override def internalComponents(): Iterable[ItemStack] = (0 until getSizeInventory).collect { - case slot if !getStackInSlot(slot).isEmpty && isComponentSlot(slot, getStackInSlot(slot)) => getStackInSlot(slot) + override def internalComponents(): Iterable[ItemStack] = (0 until getContainerSize).collect { + case slot if !getItem(slot).isEmpty && isComponentSlot(slot, getItem(slot)) => getItem(slot) } override def componentSlot(address: String): Int = components.indexWhere(_.exists(env => env.node != null && env.node.address == address)) @@ -411,7 +418,7 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp // ----------------------------------------------------------------------- // - def update(world: World, player: EntityPlayer, slot: Int, selected: Boolean) { + def update(world: World, player: PlayerEntity, slot: Int, selected: Boolean) { this.player = player if (!isInitialized) { isInitialized = true @@ -428,8 +435,8 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp client.PacketSender.sendMachineItemStateRequest(stack) } - if (!world.isRemote) { - if (isCreative && world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (!world.isClientSide) { + if (isCreative && world.getGameTime % Settings.get.tickFrequency == 0) { machine.node.asInstanceOf[Connector].changeBuffer(Double.PositiveInfinity) } machine.update() @@ -440,10 +447,10 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp if (lastRunning != machine.isRunning) { lastRunning = machine.isRunning - markDirty() + setChanged() player match { - case mp: EntityPlayerMP => server.PacketSender.sendMachineItemState(mp, stack, machine.isRunning) + case mp: ServerPlayerEntity => server.PacketSender.sendMachineItemState(mp, stack, machine.isRunning) case _ => } @@ -459,53 +466,50 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { - data.load(nbt) + override def loadData(nbt: CompoundNBT) { + data.loadData(nbt) } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { saveComponents() - data.save(nbt) + data.saveData(nbt) } } object Tablet { // This is super-hacky, but since it's only used on the client we get away // with storing context information for analyzing a block in the singleton. - var currentlyAnalyzing: Option[(BlockPosition, EnumFacing, Float, Float, Float)] = None + var currentlyAnalyzing: Option[(BlockPosition, Direction, Float, Float, Float)] = None def getId(stack: ItemStack): String = { - - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - if (!stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { - stack.getTagCompound.setString(Settings.namespace + "tablet", UUID.randomUUID().toString) + val data = stack.getOrCreateTag + if (!data.contains(Settings.namespace + "tablet")) { + data.putString(Settings.namespace + "tablet", UUID.randomUUID().toString) } - stack.getTagCompound.getString(Settings.namespace + "tablet") + data.getString(Settings.namespace + "tablet") } - def get(stack: ItemStack, holder: EntityPlayer): TabletWrapper = { - if (holder.world.isRemote) Client.get(stack, holder) + def get(stack: ItemStack, holder: PlayerEntity): TabletWrapper = { + if (holder.level.isClientSide) Client.get(stack, holder) else Server.get(stack, holder) } @SubscribeEvent def onWorldSave(e: WorldEvent.Save) { - Server.saveAll(e.getWorld) + Server.saveAll(e.getWorld.asInstanceOf[World]) } @SubscribeEvent def onWorldUnload(e: WorldEvent.Unload) { - Client.clear(e.getWorld) - Server.clear(e.getWorld) + Client.clear(e.getWorld.asInstanceOf[World]) + Server.clear(e.getWorld.asInstanceOf[World]) } @SubscribeEvent def onClientTick(e: ClientTickEvent) { Client.cleanUp() - FMLCommonHandler.instance.getMinecraftServerInstance match { - case integrated: IntegratedServer if Minecraft.getMinecraft.isGamePaused => + ServerLifecycleHooks.getCurrentServer match { + case integrated: IntegratedServer if Minecraft.getInstance.isPaused => // While the game is paused, manually keep all tablets alive, to avoid // them being cleared from the cache, causing them to stop. Client.keepAlive() @@ -531,16 +535,16 @@ object Tablet { // To allow access in cache entry init. private var currentStack: ItemStack = _ - private var currentHolder: EntityPlayer = _ + private var currentHolder: PlayerEntity = _ - def get(stack: ItemStack, holder: EntityPlayer): TabletWrapper = { + def get(stack: ItemStack, holder: PlayerEntity): TabletWrapper = { val id = getId(stack) cache.synchronized { currentStack = stack currentHolder = holder // if the item is still cached, we can detect if it is dirty (client side only) - if (holder.world.isRemote) { + if (holder.level.isClientSide) { Client.getWeak(stack) match { case Some(weak) => val timesChanged = holder.inventory.getTimesChanged @@ -559,7 +563,7 @@ object Tablet { // Force re-load on world change, in case some components store a // reference to the world object. - if (holder.world != wrapper.world) { + if (holder.level != wrapper.world) { wrapper.writeToNBT(clearState = false) wrapper.autoSave = false cache.invalidate(id) @@ -590,7 +594,7 @@ object Tablet { node.remove() } if (tablet.autoSave) tablet.writeToNBT() - tablet.markDirty() + tablet.setChanged() } } @@ -625,8 +629,8 @@ object Tablet { } def get(stack: ItemStack): Option[TabletWrapper] = { - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { - val id = stack.getTagCompound.getString(Settings.namespace + "tablet") + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "tablet")) { + val id = stack.getTag.getString(Settings.namespace + "tablet") cache.synchronized(Option(cache.getIfPresent(id))) } else None diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 9f3b5410b0..9905c3ff3d 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -6,61 +6,64 @@ import li.cil.oc.Constants import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType -import net.minecraft.client.renderer.block.model.ModelBakery -import net.minecraft.client.renderer.block.model.ModelResourceLocation +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.Hand import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.client.model.ModelLoader class Terminal(val parent: Delegator) extends traits.Delegate with CustomModel { override def maxStackSize = 1 - def hasServer(stack: ItemStack) = stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "server") + def hasServer(stack: ItemStack) = stack.hasTag && stack.getTag.contains(Settings.namespace + "server") - @SideOnly(Side.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { + @OnlyIn(Dist.CLIENT) + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.tooltipLines(stack, world, tooltip, flag) if (hasServer(stack)) { - val server = stack.getTagCompound.getString(Settings.namespace + "server") - tooltip.add("§8" + server.substring(0, 13) + "...§7") + val server = stack.getTag.getString(Settings.namespace + "server") + tooltip.add(new StringTextComponent("§8" + server.substring(0, 13) + "...§7")) } } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) private def modelLocationFromState(running: Boolean) = { new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Terminal + (if (running) "_on" else "_off"), "inventory") } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack): ModelResourceLocation = { modelLocationFromState(hasServer(stack)) } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { for (state <- Seq(true, false)) { val location = modelLocationFromState(state) - ModelBakery.registerItemVariants(parent, new ResourceLocation(location.getResourceDomain + ":" + location.getResourcePath)) + ModelLoader.addSpecialModel(new ResourceLocation(location.getNamespace + ":" + location.getPath)) } } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (!player.isSneaking && stack.hasTagCompound) { - val key = stack.getTagCompound.getString(Settings.namespace + "key") - val server = stack.getTagCompound.getString(Settings.namespace + "server") + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (!player.isCrouching && stack.hasTag) { + val key = stack.getTag.getString(Settings.namespace + "key") + val server = stack.getTag.getString(Settings.namespace + "server") if (key != null && !key.isEmpty && server != null && !server.isEmpty) { - if (world.isRemote) { - player.openGui(OpenComputers, GuiType.Terminal.id, world, 0, 0, 0) + if (world.isClientSide) { + OpenComputers.openGui(player, GuiType.Terminal.id, world, 0, 0, 0) } - player.swingArm(EnumHand.MAIN_HAND) + player.swing(Hand.MAIN_HAND) } } - super.onItemRightClick(stack, world, player) + super.use(stack, world, player) } } diff --git a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala index fefc6e5597..b955020898 100644 --- a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala +++ b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala @@ -5,18 +5,22 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.Util +import net.minecraftforge.client.model.ModelDataManager class TexturePicker(val parent: Delegator) extends traits.Delegate { - override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { - player.getEntityWorld.getBlock(position) match { + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + player.level.getBlock(position) match { case block: Block => - if (player.getEntityWorld.isRemote) { - val model = Minecraft.getMinecraft.getBlockRendererDispatcher.getModelForState(player.getEntityWorld.getBlockState(position.toBlockPos)) - if (model != null && model.getParticleTexture != null && model.getParticleTexture.getIconName != null) { - player.sendMessage(Localization.Chat.TextureName(model.getParticleTexture.getIconName)) + if (player.level.isClientSide) { + val pos = position.toBlockPos + val model = Minecraft.getInstance.getBlockRenderer.getBlockModel(player.level.getBlockState(pos)) + val particle = if (model != null) model.getParticleTexture(ModelDataManager.getModelData(player.level, pos)) else null + if (particle != null && particle.getName != null) { + player.sendMessage(Localization.Chat.TextureName(particle.getName.toString), Util.NIL_UUID) } } true diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala index aac2fb9b38..db3364aa5f 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala @@ -31,7 +31,7 @@ class UpgradeBattery(val parent: Delegator, val tier: Int) extends traits.Delega } traits.Chargeable.applyCharge(amount, buffer, Settings.get.bufferCapacitorUpgrades(tier), used => if (!simulate) { data.buffer = Option(buffer + used) - data.save(stack) + data.saveData(stack) }) } @@ -42,7 +42,7 @@ class UpgradeBattery(val parent: Delegator, val tier: Int) extends traits.Delega override def setCharge(stack: ItemStack, amount: Double): Unit = { val data = new NodeData(stack) data.buffer = Option((0.0 max amount) min maxCharge(stack)) - data.save(stack) + data.saveData(stack) } override def canExtract(stack: ItemStack): Boolean = true diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala index dc0269aa92..ec33304365 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala @@ -4,11 +4,11 @@ import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.util.Rarity -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand import net.minecraft.world.World class UpgradeDatabase(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { @@ -20,15 +20,15 @@ class UpgradeDatabase(val parent: Delegator, val tier: Int) extends traits.Deleg override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (!player.isSneaking) { - player.openGui(OpenComputers, GuiType.Database.id, world, 0, 0, 0) - player.swingArm(EnumHand.MAIN_HAND) + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (!player.isCrouching) { + OpenComputers.openGui(player, GuiType.Database.id, world, 0, 0, 0) + player.swing(Hand.MAIN_HAND) } - else if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "items")) { - stack.setTagCompound(null) - player.swingArm(EnumHand.MAIN_HAND) + else { + stack.removeTagKey(Settings.namespace + "items") + player.swing(Hand.MAIN_HAND) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala index a105ea97b5..6fa1162035 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala @@ -5,20 +5,22 @@ import java.util import li.cil.oc.util.UpgradeExperience import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.{Side, SideOnly} +import net.minecraftforge.api.distmarker.{Dist, OnlyIn} import li.cil.oc.Localization; class UpgradeExperience(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - @SideOnly(Side.CLIENT) override - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag): Unit = { - if (stack.hasTagCompound) { + @OnlyIn(Dist.CLIENT) override + def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + if (stack.hasTag) { val nbt = li.cil.oc.integration.opencomputers.Item.dataTag(stack) val experience = UpgradeExperience.getExperience(nbt) val level = UpgradeExperience.calculateLevelFromExperience(experience) val reportedLevel = UpgradeExperience.calculateExperienceLevel(level, experience) - tooltip.add(Localization.Tooltip.ExperienceLevel(reportedLevel)) + tooltip.add(new StringTextComponent(Localization.Tooltip.ExperienceLevel(reportedLevel))) } super.tooltipLines(stack, world, tooltip, flag) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala index be3d9fdefb..46b75ae135 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala @@ -5,29 +5,29 @@ import java.util import li.cil.oc.util.BlockPosition import li.cil.oc.Localization import li.cil.oc.Settings -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent class UpgradeMF(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override def onItemUseFirst(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): EnumActionResult = { - if (!player.world.isRemote && player.isSneaking) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - val data = stack.getTagCompound - data.setIntArray(Settings.namespace + "coord", Array(position.x, position.y, position.z, player.world.provider.getDimension, side.ordinal())) - return EnumActionResult.SUCCESS + override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = { + if (!player.level.isClientSide && player.isCrouching) { + val data = stack.getOrCreateTag + data.putString(Settings.namespace + "dimension", player.level.dimension.location.toString) + data.putIntArray(Settings.namespace + "coord", Array(position.x, position.y, position.z, side.ordinal())) + return ActionResultType.sidedSuccess(player.level.isClientSide) } super.onItemUseFirst(stack, player, position, side, hitX, hitY, hitZ) } - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) { - tooltip.add(Localization.Tooltip.MFULinked(stack.getTagCompound match { - case data: NBTTagCompound => data.hasKey(Settings.namespace + "coord") + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { + tooltip.add(new StringTextComponent(Localization.Tooltip.MFULinked(stack.getTag match { + case data: CompoundNBT => data.contains(Settings.namespace + "coord") case _ => false - })) + }))) } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala index 00506d0945..6d98ff6019 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala @@ -4,20 +4,22 @@ import java.util import li.cil.oc.Settings import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.fluids.FluidStack -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class UpgradeTank(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - @SideOnly(Side.CLIENT) override - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag): Unit = { - if (stack.hasTagCompound) { - FluidStack.loadFluidStackFromNBT(stack.getTagCompound.getCompoundTag(Settings.namespace + "data")) match { + @OnlyIn(Dist.CLIENT) override + def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + if (stack.hasTag) { + FluidStack.loadFluidStackFromNBT(stack.getTag.getCompound(Settings.namespace + "data")) match { case stack: FluidStack => - tooltip.add(stack.getFluid.getLocalizedName(stack) + ": " + stack.amount + "/16000") + tooltip.add(new StringTextComponent(stack.getFluid.getAttributes.getDisplayName(stack).getString + ": " + stack.getAmount + "/16000")) case _ => } } diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index 3a05dbcc1c..c6179dd510 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -1,39 +1,54 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.api -import li.cil.oc.common.asm.Injectable +import li.cil.oc.common.block.SimpleBlock import li.cil.oc.integration.Mods import net.minecraft.block.Block -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.Rotation import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IWorldReader import net.minecraft.world.World +import net.minecraftforge.common.ToolType -class Wrench extends traits.SimpleItem with api.internal.Wrench { - setHarvestLevel("wrench", 1) - setMaxStackSize(1) +object Wrench { + val WrenchType: ToolType = ToolType.get("wrench") +} - override def doesSneakBypassUse(stack: ItemStack, world: IBlockAccess, pos: BlockPos, player: EntityPlayer): Boolean = true +class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench.WrenchType, 1).tab(CreativeTab)) extends Item(props) with traits.SimpleItem with api.internal.Wrench { + override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true - override def onItemUseFirst(player: EntityPlayer, world: World, pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, hand: EnumHand): EnumActionResult = { - if (world.isBlockLoaded(pos) && world.isBlockModifiable(player, pos)) world.getBlockState(pos).getBlock match { - case block: Block if block.rotateBlock(world, pos, side) => - block.neighborChanged(world.getBlockState(pos), world, pos, Blocks.AIR, pos) - player.swingArm(hand) - if (!world.isRemote) EnumActionResult.SUCCESS else EnumActionResult.PASS - case _ => - super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) + override def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { + if (world.isLoaded(pos) && world.mayInteract(player, pos)) { + val state = world.getBlockState(pos) + state.getBlock match { + case block: SimpleBlock if block.rotateBlock(world, pos, side) => + state.neighborChanged(world, pos, Blocks.AIR, pos, false) + player.swing(hand) + if (!world.isClientSide) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + case _ => + val updated = state.rotate(world, pos, Rotation.CLOCKWISE_90) + if (updated != state) { + world.setBlock(pos, updated, 3) + player.swing(hand) + if (!world.isClientSide) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + } + else super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) + } } else super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) } - def useWrenchOnBlock(player: EntityPlayer, world: World, pos: BlockPos, simulate: Boolean): Boolean = { - if (!simulate) player.swingArm(EnumHand.MAIN_HAND) + def useWrenchOnBlock(player: PlayerEntity, world: World, pos: BlockPos, simulate: Boolean): Boolean = { + if (!simulate) player.swing(Hand.MAIN_HAND) true } } diff --git a/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala index b27ef3925a..848345eb04 100644 --- a/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala @@ -4,32 +4,32 @@ import li.cil.oc.server.component.DebugCard.AccessContext import li.cil.oc.Constants import li.cil.oc.Settings import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT class DebugCardData extends ItemData(Constants.ItemName.DebugCard) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var access: Option[AccessContext] = None private final val DataTag = Settings.namespace + "data" - override def load(nbt: NBTTagCompound): Unit = { - access = AccessContext.load(dataTag(nbt)) + override def loadData(nbt: CompoundNBT): Unit = { + access = AccessContext.loadData(dataTag(nbt)) } - override def save(nbt: NBTTagCompound): Unit = { + override def saveData(nbt: CompoundNBT): Unit = { val tag = dataTag(nbt) AccessContext.remove(tag) - access.foreach(_.save(tag)) + access.foreach(_.saveData(tag)) } - private def dataTag(nbt: NBTTagCompound) = { - if (!nbt.hasKey(DataTag)) { - nbt.setTag(DataTag, new NBTTagCompound()) + private def dataTag(nbt: CompoundNBT) = { + if (!nbt.contains(DataTag)) { + nbt.put(DataTag, new CompoundNBT()) } - nbt.getCompoundTag(DataTag) + nbt.getCompound(DataTag) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/DriveData.scala b/src/main/scala/li/cil/oc/common/item/data/DriveData.scala index 0e4b81fffa..52190f9f20 100644 --- a/src/main/scala/li/cil/oc/common/item/data/DriveData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/DriveData.scala @@ -2,14 +2,14 @@ package li.cil.oc.common.item.data import li.cil.oc.Settings import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import li.cil.oc.server.fs -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity class DriveData extends ItemData(null) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var isUnmanaged = false @@ -22,29 +22,29 @@ class DriveData extends ItemData(null) { private final val UnmanagedTag = Settings.namespace + "unmanaged" private val LockTag = Settings.namespace + "lock" - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { isUnmanaged = nbt.getBoolean(UnmanagedTag) - lockInfo = if (nbt.hasKey(LockTag)) { + lockInfo = if (nbt.contains(LockTag)) { nbt.getString(LockTag) } else "" } - override def save(nbt: NBTTagCompound) { - nbt.setBoolean(UnmanagedTag, isUnmanaged) - nbt.setString(LockTag, lockInfo) + override def saveData(nbt: CompoundNBT) { + nbt.putBoolean(UnmanagedTag, isUnmanaged) + nbt.putString(LockTag, lockInfo) } } object DriveData { - def lock(stack: ItemStack, player: EntityPlayer): Unit = { - val key = player.getName + def lock(stack: ItemStack, player: PlayerEntity): Unit = { + val key = player.getName.getString val data = new DriveData(stack) if (!data.isLocked) { data.lockInfo = key match { case name: String if name != null && name.nonEmpty => name case _ => "notch" // meaning: "unknown" } - data.save(stack) + data.saveData(stack) } } @@ -55,6 +55,6 @@ object DriveData { data.lockInfo = "" } data.isUnmanaged = unmanaged - data.save(stack) + data.saveData(stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/DroneData.scala b/src/main/scala/li/cil/oc/common/item/data/DroneData.scala index ac7e2f6fc0..481a998c28 100644 --- a/src/main/scala/li/cil/oc/common/item/data/DroneData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/DroneData.scala @@ -4,26 +4,26 @@ import com.google.common.base.Strings import li.cil.oc.Constants import li.cil.oc.util.ItemUtils import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT class DroneData extends MicrocontrollerData(Constants.ItemName.Drone) { def this(stack: ItemStack) = { this() - load(stack) + loadData(stack) } var name = "" - override def load(nbt: NBTTagCompound): Unit = { - super.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = { + super.loadData(nbt) name = ItemUtils.getDisplayName(nbt).getOrElse("") if (Strings.isNullOrEmpty(name)) { name = RobotData.randomName } } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) if (!Strings.isNullOrEmpty(name)) { ItemUtils.setDisplayName(nbt, name) } diff --git a/src/main/scala/li/cil/oc/common/item/data/HoverBootsData.scala b/src/main/scala/li/cil/oc/common/item/data/HoverBootsData.scala index b092b8e8aa..9b0b70a0ba 100644 --- a/src/main/scala/li/cil/oc/common/item/data/HoverBootsData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/HoverBootsData.scala @@ -3,23 +3,23 @@ package li.cil.oc.common.item.data import li.cil.oc.Constants import li.cil.oc.Settings import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT class HoverBootsData extends ItemData(Constants.ItemName.HoverBoots) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var charge = 0.0 private final val ChargeTag = Settings.namespace + "charge" - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { charge = nbt.getDouble(ChargeTag) } - override def save(nbt: NBTTagCompound) { - nbt.setDouble(ChargeTag, charge) + override def saveData(nbt: CompoundNBT) { + nbt.putDouble(ChargeTag, charge) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/ItemData.scala b/src/main/scala/li/cil/oc/common/item/data/ItemData.scala index 4fa2a58f56..82e7d5ef73 100644 --- a/src/main/scala/li/cil/oc/common/item/data/ItemData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/ItemData.scala @@ -3,29 +3,26 @@ package li.cil.oc.common.item.data import li.cil.oc.api import li.cil.oc.api.Persistable import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT abstract class ItemData(val itemName: String) extends Persistable { - def load(stack: ItemStack) { - if (stack.hasTagCompound) { + def loadData(stack: ItemStack) { + if (stack.hasTag) { // Because ItemStack's load function doesn't copy the compound tag, // but keeps it as is, leading to oh so fun bugs! - load(stack.getTagCompound.copy().asInstanceOf[NBTTagCompound]) + loadData(stack.getTag.copy().asInstanceOf[CompoundNBT]) } } - def save(stack: ItemStack) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - save(stack.getTagCompound) + def saveData(stack: ItemStack) { + saveData(stack.getOrCreateTag) } def createItemStack() = { if (itemName == null) ItemStack.EMPTY else { val stack = api.Items.get(itemName).createItemStack(1) - save(stack) + saveData(stack) stack } } diff --git a/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala b/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala index 9d1f470365..33ec629e93 100644 --- a/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/MicrocontrollerData.scala @@ -6,13 +6,13 @@ import li.cil.oc.api import li.cil.oc.common.Tier import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.common.util.Constants.NBT class MicrocontrollerData(itemName: String = Constants.BlockName.Microcontroller) extends ItemData(itemName) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var tier = Tier.One @@ -25,11 +25,11 @@ class MicrocontrollerData(itemName: String = Constants.BlockName.Microcontroller private final val ComponentsTag = Settings.namespace + "components" private final val StoredEnergyTag = Settings.namespace + "storedEnergy" - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { tier = nbt.getByte(TierTag) - components = nbt.getTagList(ComponentsTag, NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(new ItemStack(_)).filter(!_.isEmpty) - storedEnergy = nbt.getInteger(StoredEnergyTag) + components = nbt.getList(ComponentsTag, NBT.TAG_COMPOUND). + toTagArray[CompoundNBT].map(ItemStack.of(_)).filter(!_.isEmpty) + storedEnergy = nbt.getInt(StoredEnergyTag) // Reserve slot for EEPROM if necessary, avoids having to resize the // components array in the MCU tile entity, which isn't possible currently. @@ -38,16 +38,16 @@ class MicrocontrollerData(itemName: String = Constants.BlockName.Microcontroller } } - override def save(nbt: NBTTagCompound) { - nbt.setByte(TierTag, tier.toByte) + override def saveData(nbt: CompoundNBT) { + nbt.putByte(TierTag, tier.toByte) nbt.setNewTagList(ComponentsTag, components.filter(!_.isEmpty).toIterable) - nbt.setInteger(StoredEnergyTag, storedEnergy) + nbt.putInt(StoredEnergyTag, storedEnergy) } def copyItemStack(): ItemStack = { val stack = createItemStack() val newInfo = new MicrocontrollerData(stack) - newInfo.save(stack) + newInfo.saveData(stack) stack } } diff --git a/src/main/scala/li/cil/oc/common/item/data/NanomachineData.scala b/src/main/scala/li/cil/oc/common/item/data/NanomachineData.scala index 949786d0db..e17463505d 100644 --- a/src/main/scala/li/cil/oc/common/item/data/NanomachineData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/NanomachineData.scala @@ -4,40 +4,40 @@ import li.cil.oc.common.nanomachines.ControllerImpl import li.cil.oc.Constants import li.cil.oc.Settings import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT class NanomachineData extends ItemData(Constants.ItemName.Nanomachines) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } def this(controller: ControllerImpl) { this() uuid = controller.uuid - val nbt = new NBTTagCompound() - controller.configuration.save(nbt, forItem = true) + val nbt = new CompoundNBT() + controller.configuration.saveData(nbt, forItem = true) configuration = Option(nbt) } var uuid = "" - var configuration: Option[NBTTagCompound] = None + var configuration: Option[CompoundNBT] = None private final val UUIDTag = Settings.namespace + "uuid" private final val ConfigurationTag = Settings.namespace + "configuration" - override def load(nbt: NBTTagCompound): Unit = { + override def loadData(nbt: CompoundNBT): Unit = { uuid = nbt.getString(UUIDTag) - if (nbt.hasKey(ConfigurationTag)) { - configuration = Option(nbt.getCompoundTag(ConfigurationTag)) + if (nbt.contains(ConfigurationTag)) { + configuration = Option(nbt.getCompound(ConfigurationTag)) } else { configuration = None } } - override def save(nbt: NBTTagCompound): Unit = { - nbt.setString(UUIDTag, uuid) - configuration.foreach(nbt.setTag(ConfigurationTag, _)) + override def saveData(nbt: CompoundNBT): Unit = { + nbt.putString(UUIDTag, uuid) + configuration.foreach(nbt.put(ConfigurationTag, _)) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala b/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala index 2d2cd36245..be8c95af20 100644 --- a/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/NavigationUpgradeData.scala @@ -3,20 +3,20 @@ package li.cil.oc.common.item.data import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.item.ItemMap +import net.minecraft.item.FilledMapItem import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.world.World class NavigationUpgradeData extends ItemData(Constants.ItemName.NavigationUpgrade) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } - var map = new ItemStack(net.minecraft.init.Items.FILLED_MAP) + var map = new ItemStack(net.minecraft.item.Items.FILLED_MAP) - def mapData(world: World) = try map.getItem.asInstanceOf[ItemMap].getMapData(map, world) catch { + def mapData(world: World) = try FilledMapItem.getSavedData(map, world) catch { case _: Throwable => throw new Exception("invalid map") } @@ -28,28 +28,25 @@ class NavigationUpgradeData extends ItemData(Constants.ItemName.NavigationUpgrad private final val DataTag = Settings.namespace + "data" private final val MapTag = Settings.namespace + "map" - override def load(stack: ItemStack) { - if (stack.hasTagCompound) { - load(stack.getTagCompound.getCompoundTag(DataTag)) + override def loadData(stack: ItemStack) { + if (stack.hasTag) { + loadData(stack.getTag.getCompound(DataTag)) } } - override def save(stack: ItemStack) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - save(stack.getCompoundTag(DataTag)) + override def saveData(stack: ItemStack) { + saveData(stack.getOrCreateTagElement(DataTag)) } - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey(MapTag)) { - map = new ItemStack(nbt.getCompoundTag(MapTag)) + override def loadData(nbt: CompoundNBT) { + if (nbt.contains(MapTag)) { + map = ItemStack.of(nbt.getCompound(MapTag)) } } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { if (map != null) { - nbt.setNewCompoundTag(MapTag, map.writeToNBT) + nbt.setNewCompoundTag(MapTag, map.save) } } } diff --git a/src/main/scala/li/cil/oc/common/item/data/NodeData.scala b/src/main/scala/li/cil/oc/common/item/data/NodeData.scala index 472eeba24c..ca7a44d3ad 100644 --- a/src/main/scala/li/cil/oc/common/item/data/NodeData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/NodeData.scala @@ -3,13 +3,13 @@ package li.cil.oc.common.item.data import li.cil.oc.Settings import li.cil.oc.api.network.Visibility import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT // Generic one for items that are used as components; gets the items node info. class NodeData extends ItemData(null) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var address: Option[String] = None @@ -18,31 +18,31 @@ class NodeData extends ItemData(null) { private final val DataTag = Settings.namespace + "data" - override def load(nbt: NBTTagCompound): Unit = { - val nodeNbt = nbt.getCompoundTag(DataTag).getCompoundTag(NodeData.NodeTag) - if (nodeNbt.hasKey(NodeData.AddressTag)) { + override def loadData(nbt: CompoundNBT): Unit = { + val nodeNbt = nbt.getCompound(DataTag).getCompound(NodeData.NodeTag) + if (nodeNbt.contains(NodeData.AddressTag)) { address = Option(nodeNbt.getString(NodeData.AddressTag)) } - if (nodeNbt.hasKey(NodeData.BufferTag)) { + if (nodeNbt.contains(NodeData.BufferTag)) { buffer = Option(nodeNbt.getDouble(NodeData.BufferTag)) } - if (nodeNbt.hasKey(NodeData.VisibilityTag)) { - visibility = Option(Visibility.values()(nodeNbt.getInteger(NodeData.VisibilityTag))) + if (nodeNbt.contains(NodeData.VisibilityTag)) { + visibility = Option(Visibility.values()(nodeNbt.getInt(NodeData.VisibilityTag))) } } - override def save(nbt: NBTTagCompound): Unit = { - if (!nbt.hasKey(DataTag)) { - nbt.setTag(DataTag, new NBTTagCompound()) + override def saveData(nbt: CompoundNBT): Unit = { + if (!nbt.contains(DataTag)) { + nbt.put(DataTag, new CompoundNBT()) } - val dataNbt = nbt.getCompoundTag(DataTag) - if (!dataNbt.hasKey(NodeData.NodeTag)) { - dataNbt.setTag(NodeData.NodeTag, new NBTTagCompound()) + val dataNbt = nbt.getCompound(DataTag) + if (!dataNbt.contains(NodeData.NodeTag)) { + dataNbt.put(NodeData.NodeTag, new CompoundNBT()) } - val nodeNbt = dataNbt.getCompoundTag(NodeData.NodeTag) - address.foreach(nodeNbt.setString(NodeData.AddressTag, _)) - buffer.foreach(nodeNbt.setDouble(NodeData.BufferTag, _)) - visibility.map(_.ordinal()).foreach(nodeNbt.setInteger(NodeData.VisibilityTag, _)) + val nodeNbt = dataNbt.getCompound(NodeData.NodeTag) + address.foreach(nodeNbt.putString(NodeData.AddressTag, _)) + buffer.foreach(nodeNbt.putDouble(NodeData.BufferTag, _)) + visibility.map(_.ordinal()).foreach(nodeNbt.putInt(NodeData.VisibilityTag, _)) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/PrintData.scala b/src/main/scala/li/cil/oc/common/item/data/PrintData.scala index 3f1ac731ea..9644d0edd6 100644 --- a/src/main/scala/li/cil/oc/common/item/data/PrintData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/PrintData.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.IMC import li.cil.oc.util.ExtendedAABB._ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.util.math.AxisAlignedBB import net.minecraftforge.common.util.Constants.NBT @@ -18,7 +18,7 @@ import scala.collection.mutable class PrintData extends ItemData(Constants.BlockName.Print) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var label: Option[String] = None @@ -72,17 +72,17 @@ class PrintData extends ItemData(Constants.BlockName.Print) { private final val NoclipOffTag = "noclipOff" private final val NoclipOnTag = "noclipOn" - override def load(nbt: NBTTagCompound): Unit = { - if (nbt.hasKey(LabelTag)) label = Option(nbt.getString(LabelTag)) else label = None - if (nbt.hasKey(TooltipTag)) tooltip = Option(nbt.getString(TooltipTag)) else tooltip = None + override def loadData(nbt: CompoundNBT): Unit = { + if (nbt.contains(LabelTag)) label = Option(nbt.getString(LabelTag)) else label = None + if (nbt.contains(TooltipTag)) tooltip = Option(nbt.getString(TooltipTag)) else tooltip = None isButtonMode = nbt.getBoolean(IsButtonModeTag) - redstoneLevel = nbt.getInteger(RedstoneLevelTag) max 0 min 15 + redstoneLevel = nbt.getInt(RedstoneLevelTag) max 0 min 15 if (nbt.getBoolean(RedstoneLevelTagCompat)) redstoneLevel = 15 pressurePlate = nbt.getBoolean(PressurePlateTag) stateOff.clear() - stateOff ++= nbt.getTagList(StateOffTag, NBT.TAG_COMPOUND).map(PrintData.nbtToShape) + stateOff ++= nbt.getList(StateOffTag, NBT.TAG_COMPOUND).map(PrintData.nbtToShape) stateOn.clear() - stateOn ++= nbt.getTagList(StateOnTag, NBT.TAG_COMPOUND).map(PrintData.nbtToShape) + stateOn ++= nbt.getList(StateOnTag, NBT.TAG_COMPOUND).map(PrintData.nbtToShape) isBeaconBase = nbt.getBoolean(IsBeaconBaseTag) lightLevel = (nbt.getByte(LightLevelTag) & 0xFF) max 0 min 15 noclipOff = nbt.getBoolean(NoclipOffTag) @@ -91,18 +91,18 @@ class PrintData extends ItemData(Constants.BlockName.Print) { opacityDirty = true } - override def save(nbt: NBTTagCompound): Unit = { - label.foreach(nbt.setString(LabelTag, _)) - tooltip.foreach(nbt.setString(TooltipTag, _)) - nbt.setBoolean(IsButtonModeTag, isButtonMode) - nbt.setInteger(RedstoneLevelTag, redstoneLevel) - nbt.setBoolean(PressurePlateTag, pressurePlate) + override def saveData(nbt: CompoundNBT): Unit = { + label.foreach(nbt.putString(LabelTag, _)) + tooltip.foreach(nbt.putString(TooltipTag, _)) + nbt.putBoolean(IsButtonModeTag, isButtonMode) + nbt.putInt(RedstoneLevelTag, redstoneLevel) + nbt.putBoolean(PressurePlateTag, pressurePlate) nbt.setNewTagList(StateOffTag, stateOff.map(PrintData.shapeToNBT)) nbt.setNewTagList(StateOnTag, stateOn.map(PrintData.shapeToNBT)) - nbt.setBoolean(IsBeaconBaseTag, isBeaconBase) - nbt.setByte(LightLevelTag, lightLevel.toByte) - nbt.setBoolean(NoclipOffTag, noclipOff) - nbt.setBoolean(NoclipOnTag, noclipOn) + nbt.putBoolean(IsBeaconBaseTag, isBeaconBase) + nbt.putByte(LightLevelTag, lightLevel.toByte) + nbt.putBoolean(NoclipOffTag, noclipOff) + nbt.putBoolean(NoclipOnTag, noclipOn) } } @@ -179,9 +179,9 @@ object PrintData { 0 } - def nbtToShape(nbt: NBTTagCompound): Shape = { + def nbtToShape(nbt: CompoundNBT): Shape = { val aabb = - if (nbt.hasKey("minX")) { + if (nbt.contains("minX")) { // Compatibility with shapes created with earlier dev-builds. val minX = nbt.getByte("minX") / 16f val minY = nbt.getByte("minY") / 16f @@ -202,13 +202,13 @@ object PrintData { new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ) } val texture = nbt.getString("texture") - val tint = if (nbt.hasKey("tint")) Option(nbt.getInteger("tint")) else None + val tint = if (nbt.contains("tint")) Option(nbt.getInt("tint")) else None new Shape(aabb, texture, tint) } - def shapeToNBT(shape: Shape): NBTTagCompound = { - val nbt = new NBTTagCompound() - nbt.setByteArray("bounds", Array( + def shapeToNBT(shape: Shape): CompoundNBT = { + val nbt = new CompoundNBT() + nbt.putByteArray("bounds", Array( (shape.bounds.minX * 16).round.toByte, (shape.bounds.minY * 16).round.toByte, (shape.bounds.minZ * 16).round.toByte, @@ -216,8 +216,8 @@ object PrintData { (shape.bounds.maxY * 16).round.toByte, (shape.bounds.maxZ * 16).round.toByte )) - nbt.setString("texture", shape.texture) - shape.tint.foreach(nbt.setInteger("tint", _)) + nbt.putString("texture", shape.texture) + shape.tint.foreach(nbt.putInt("tint", _)) nbt } diff --git a/src/main/scala/li/cil/oc/common/item/data/RaidData.scala b/src/main/scala/li/cil/oc/common/item/data/RaidData.scala index 1b6b045d75..19f5191579 100644 --- a/src/main/scala/li/cil/oc/common/item/data/RaidData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/RaidData.scala @@ -4,18 +4,18 @@ import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.common.util.Constants.NBT class RaidData extends ItemData(Constants.BlockName.Raid) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var disks = Array.empty[ItemStack] - var filesystem = new NBTTagCompound() + var filesystem = new CompoundNBT() var label: Option[String] = None @@ -23,18 +23,18 @@ class RaidData extends ItemData(Constants.BlockName.Raid) { private final val FileSystemTag = Settings.namespace + "filesystem" private final val LabelTag = Settings.namespace + "label" - override def load(nbt: NBTTagCompound): Unit = { - disks = nbt.getTagList(DisksTag, NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(new ItemStack(_)) - filesystem = nbt.getCompoundTag(FileSystemTag) - if (nbt.hasKey(LabelTag)) { + override def loadData(nbt: CompoundNBT): Unit = { + disks = nbt.getList(DisksTag, NBT.TAG_COMPOUND). + toTagArray[CompoundNBT].map(ItemStack.of(_)) + filesystem = nbt.getCompound(FileSystemTag) + if (nbt.contains(LabelTag)) { label = Option(nbt.getString(LabelTag)) } } - override def save(nbt: NBTTagCompound): Unit = { + override def saveData(nbt: CompoundNBT): Unit = { nbt.setNewTagList(DisksTag, disks.toIterable) - nbt.setTag(FileSystemTag, filesystem) - label.foreach(nbt.setString(LabelTag, _)) + nbt.put(FileSystemTag, filesystem) + label.foreach(nbt.putString(LabelTag, _)) } } diff --git a/src/main/scala/li/cil/oc/common/item/data/RobotData.scala b/src/main/scala/li/cil/oc/common/item/data/RobotData.scala index 3cafcc9452..4e339295e7 100644 --- a/src/main/scala/li/cil/oc/common/item/data/RobotData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/RobotData.scala @@ -10,7 +10,7 @@ import li.cil.oc.integration.opencomputers.DriverScreen import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ItemUtils import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.common.util.Constants.NBT import scala.io.Source @@ -33,7 +33,7 @@ object RobotData { class RobotData extends ItemData(Constants.BlockName.Robot) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var name = "" @@ -59,33 +59,33 @@ class RobotData extends ItemData(Constants.BlockName.Robot) { private final val ContainersTag = Settings.namespace + "containers" private final val LightColorTag = Settings.namespace + "lightColor" - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { name = ItemUtils.getDisplayName(nbt).getOrElse("") if (Strings.isNullOrEmpty(name)) { name = RobotData.randomName } - totalEnergy = nbt.getInteger(StoredEnergyTag) - robotEnergy = nbt.getInteger(RobotEnergyTag) - tier = nbt.getInteger(TierTag) - components = nbt.getTagList(ComponentsTag, NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(new ItemStack(_)) - containers = nbt.getTagList(ContainersTag, NBT.TAG_COMPOUND). - toArray[NBTTagCompound].map(new ItemStack(_)) - if (nbt.hasKey(LightColorTag)) { - lightColor = nbt.getInteger(LightColorTag) + totalEnergy = nbt.getInt(StoredEnergyTag) + robotEnergy = nbt.getInt(RobotEnergyTag) + tier = nbt.getInt(TierTag) + components = nbt.getList(ComponentsTag, NBT.TAG_COMPOUND). + toTagArray[CompoundNBT].map(ItemStack.of(_)) + containers = nbt.getList(ContainersTag, NBT.TAG_COMPOUND). + toTagArray[CompoundNBT].map(ItemStack.of(_)) + if (nbt.contains(LightColorTag)) { + lightColor = nbt.getInt(LightColorTag) } } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { if (!Strings.isNullOrEmpty(name)) { ItemUtils.setDisplayName(nbt, name) } - nbt.setInteger(StoredEnergyTag, totalEnergy) - nbt.setInteger(RobotEnergyTag, robotEnergy) - nbt.setInteger(TierTag, tier) + nbt.putInt(StoredEnergyTag, totalEnergy) + nbt.putInt(RobotEnergyTag, robotEnergy) + nbt.putInt(TierTag, tier) nbt.setNewTagList(ComponentsTag, components.toIterable) nbt.setNewTagList(ContainersTag, containers.toIterable) - nbt.setInteger(LightColorTag, lightColor) + nbt.putInt(LightColorTag, lightColor) } def copyItemStack() = { @@ -96,8 +96,8 @@ class RobotData extends ItemData(Constants.BlockName.Robot) { newInfo.components.foreach(cs => Option(api.Driver.driverFor(cs)) match { case Some(driver) if driver == DriverScreen => val nbt = driver.dataTag(cs) - for (tagName <- nbt.getKeySet.toArray) { - nbt.removeTag(tagName.asInstanceOf[String]) + for (tagName <- nbt.getAllKeys.toArray) { + nbt.remove(tagName.asInstanceOf[String]) } case _ => }) @@ -105,7 +105,7 @@ class RobotData extends ItemData(Constants.BlockName.Robot) { // internal buffer. This is for creative use only, anyway. newInfo.totalEnergy = 0 newInfo.robotEnergy = 50000 - newInfo.save(stack) + newInfo.saveData(stack) stack } } diff --git a/src/main/scala/li/cil/oc/common/item/data/TabletData.scala b/src/main/scala/li/cil/oc/common/item/data/TabletData.scala index 7830d0a438..953c2237cd 100644 --- a/src/main/scala/li/cil/oc/common/item/data/TabletData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/TabletData.scala @@ -5,13 +5,13 @@ import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.util.ExtendedNBT._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.common.util.Constants.NBT class TabletData extends ItemData(Constants.ItemName.Tablet) { def this(stack: ItemStack) { this() - load(stack) + loadData(stack) } var items = Array.fill[ItemStack](32)(ItemStack.EMPTY) @@ -30,36 +30,36 @@ class TabletData extends ItemData(Constants.ItemName.Tablet) { private final val TierTag = Settings.namespace + "tier" private final val ContainerTag = Settings.namespace + "container" - override def load(nbt: NBTTagCompound) { - nbt.getTagList(ItemsTag, NBT.TAG_COMPOUND).foreach((slotNbt: NBTTagCompound) => { + override def loadData(nbt: CompoundNBT) { + nbt.getList(ItemsTag, NBT.TAG_COMPOUND).foreach((slotNbt: CompoundNBT) => { val slot = slotNbt.getByte(SlotTag) if (slot >= 0 && slot < items.length) { - items(slot) = new ItemStack(slotNbt.getCompoundTag(ItemTag)) + items(slot) = ItemStack.of(slotNbt.getCompound(ItemTag)) } }) isRunning = nbt.getBoolean(IsRunningTag) energy = nbt.getDouble(EnergyTag) maxEnergy = nbt.getDouble(MaxEnergyTag) - tier = nbt.getInteger(TierTag) - if (nbt.hasKey(ContainerTag)) { - container = new ItemStack(nbt.getCompoundTag(ContainerTag)) + tier = nbt.getInt(TierTag) + if (nbt.contains(ContainerTag)) { + container = ItemStack.of(nbt.getCompound(ContainerTag)) } } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { nbt.setNewTagList(ItemsTag, items.zipWithIndex collect { case (stack, slot) if !stack.isEmpty => (stack, slot) } map { case (stack, slot) => - val slotNbt = new NBTTagCompound() - slotNbt.setByte(SlotTag, slot.toByte) - slotNbt.setNewCompoundTag(ItemTag, stack.writeToNBT) + val slotNbt = new CompoundNBT() + slotNbt.putByte(SlotTag, slot.toByte) + slotNbt.setNewCompoundTag(ItemTag, stack.save) }) - nbt.setBoolean(IsRunningTag, isRunning) - nbt.setDouble(EnergyTag, energy) - nbt.setDouble(MaxEnergyTag, maxEnergy) - nbt.setInteger(TierTag, tier) - if (!container.isEmpty) nbt.setNewCompoundTag(ContainerTag, container.writeToNBT) + nbt.putBoolean(IsRunningTag, isRunning) + nbt.putDouble(EnergyTag, energy) + nbt.putDouble(MaxEnergyTag, maxEnergy) + nbt.putInt(TierTag, tier) + if (!container.isEmpty) nbt.setNewCompoundTag(ContainerTag, container.save) } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala index 61b7245437..0c53086577 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala @@ -7,12 +7,15 @@ import li.cil.oc.api import li.cil.oc.api.driver.item.MutableProcessor import li.cil.oc.integration.opencomputers.DriverCPU import li.cil.oc.util.Tooltip -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand -import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand +import net.minecraft.util.Util +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.util.text.TranslationTextComponent import net.minecraft.world.World import scala.collection.convert.WrapAsScala._ @@ -23,13 +26,15 @@ trait CPULike extends Delegate { override protected def tooltipData: Seq[Any] = Seq(Settings.get.cpuComponentSupport(cpuTier)) - override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) { - tooltip.addAll(Tooltip.get("cpu.Architecture", api.Machine.getArchitectureName(DriverCPU.architecture(stack)))) + override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { + for (curr <- Tooltip.get("cpu.Architecture", api.Machine.getArchitectureName(DriverCPU.architecture(stack)))) { + tooltip.add(new StringTextComponent(curr)) + } } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (player.isSneaking) { - if (!world.isRemote) { + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (player.isCrouching) { + if (!world.isClientSide) { api.Driver.driverFor(stack) match { case driver: MutableProcessor => val architectures = driver.allArchitectures.toList @@ -39,13 +44,13 @@ trait CPULike extends Delegate { val archClass = architectures(newIndex) val archName = api.Machine.getArchitectureName(archClass) driver.setArchitecture(stack, archClass) - player.sendMessage(new TextComponentTranslation(Settings.namespace + "tooltip.cpu.Architecture", archName)) + player.sendMessage(new TranslationTextComponent(Settings.namespace + "tooltip.cpu.Architecture", archName), Util.NIL_UUID) } - player.swingArm(EnumHand.MAIN_HAND) + player.swing(Hand.MAIN_HAND) case _ => // No known driver for this processor. } } } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala b/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala index 0b92efcfe4..f9dad6ab46 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/Chargeable.scala @@ -1,14 +1,14 @@ package li.cil.oc.common.item.traits import li.cil.oc.{Settings, api} -import li.cil.oc.common.asm.Injectable import li.cil.oc.integration.Mods import li.cil.oc.integration.opencomputers.ModOpenComputers -import net.minecraft.util.{EnumFacing, ResourceLocation} -import net.minecraftforge.fml.common.Optional +import net.minecraft.util.{Direction, ResourceLocation} import net.minecraft.item.ItemStack import net.minecraftforge.common.capabilities.{Capability, ICapabilityProvider} import net.minecraftforge.energy.{CapabilityEnergy, IEnergyStorage} +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier // TODO Forge power capabilities. trait Chargeable extends api.driver.item.Chargeable { @@ -40,14 +40,16 @@ object Chargeable { unused } - class Provider(stack: ItemStack, item: li.cil.oc.common.item.traits.Chargeable) extends ICapabilityProvider with IEnergyStorage { - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { - capability == CapabilityEnergy.ENERGY - } + class Provider(stack: ItemStack, item: li.cil.oc.common.item.traits.Chargeable) extends ICapabilityProvider with NonNullSupplier[Provider] with IEnergyStorage { + private val wrapper = LazyOptional.of(this) + + def get = this + + def invalidate() = wrapper.invalidate - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - if (hasCapability(capability, facing)) this.asInstanceOf[T] - else null.asInstanceOf[T] + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + if (capability == CapabilityEnergy.ENERGY) wrapper.cast[T] + else LazyOptional.empty[T] } def receiveEnergy(maxReceive: Int, simulate: Boolean): Int = diff --git a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala b/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala index ce6bb9b06a..f4b13bfc11 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala @@ -11,19 +11,24 @@ import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.EnumAction -import net.minecraft.item.EnumRarity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item // Rarity +import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.item.UseAction import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IBlockReader import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn + +import scala.collection.convert.WrapAsScala._ trait Delegate { def parent: Delegator @@ -40,31 +45,36 @@ trait Delegate { def maxStackSize = 64 - def createItemStack(amount: Int = 1) = new ItemStack(parent, amount, itemId) + def createItemStack(amount: Int = 1) = { + val stack = new ItemStack(parent, amount) + stack.setDamageValue(itemId) + stack + } // ----------------------------------------------------------------------- // - def doesSneakBypassUse(world: IBlockAccess, pos: BlockPos, player: EntityPlayer) = false + def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity) = false - def onItemUseFirst(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): EnumActionResult = EnumActionResult.PASS + def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = ActionResultType.PASS - def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = false + @Deprecated + def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = false - def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = ActionResult.newResult(EnumActionResult.PASS, stack) + def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = new ActionResult(ActionResultType.PASS, stack) - def getItemUseAction(stack: ItemStack): EnumAction = EnumAction.NONE + def getUseAnimation(stack: ItemStack): UseAction = UseAction.NONE def getMaxItemUseDuration(stack: ItemStack) = 0 - def onItemUseFinish(stack: ItemStack, world: World, player: EntityLivingBase): ItemStack = stack + def finishUsingItem(stack: ItemStack, world: World, player: LivingEntity): ItemStack = stack - def onPlayerStoppedUsing(stack: ItemStack, player: EntityLivingBase, duration: Int) {} + def onPlayerStoppedUsing(stack: ItemStack, player: LivingEntity, duration: Int) {} def update(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean) {} // ----------------------------------------------------------------------- // - def rarity(stack: ItemStack): EnumRarity = Rarity.byTier(tierFromDriver(stack)) + def rarity(stack: ItemStack): item.Rarity = Rarity.byTier(tierFromDriver(stack)) protected def tierFromDriver(stack: ItemStack): Int = api.Driver.driverFor(stack) match { @@ -74,29 +84,33 @@ trait Delegate { def color(stack: ItemStack, pass: Int) = 0xFFFFFF - def getContainerItem(stack: ItemStack): ItemStack = ItemStack.EMPTY + @Deprecated + def getCraftingRemainingItem(): Item = null - def hasContainerItem(stack: ItemStack): Boolean = false + @Deprecated + def hasCraftingRemainingItem(): Boolean = false def displayName(stack: ItemStack): Option[String] = None - @SideOnly(Side.CLIENT) - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { + @OnlyIn(Dist.CLIENT) + def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { if (tooltipName.isDefined) { - tooltip.addAll(Tooltip.get(tooltipName.get, tooltipData: _*)) + for (curr <- Tooltip.get(tooltipName.get, tooltipData: _*)) { + tooltip.add(new StringTextComponent(curr)) + } tooltipExtended(stack, tooltip) } tooltipCosts(stack, tooltip) } // For stuff that goes to the normal 'extended' tooltip, before the costs. - protected def tooltipExtended(stack: ItemStack, tooltip: java.util.List[String]) {} + protected def tooltipExtended(stack: ItemStack, tooltip: java.util.List[ITextComponent]) {} - protected def tooltipCosts(stack: ItemStack, tooltip: java.util.List[String]) { - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "data")) { - val data = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") - if (data.hasKey("node") && data.getCompoundTag("node").hasKey("address")) { - tooltip.add("§8" + data.getCompoundTag("node").getString("address").substring(0, 13) + "...§7") + protected def tooltipCosts(stack: ItemStack, tooltip: java.util.List[ITextComponent]) { + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { + val data = stack.getTag.getCompound(Settings.namespace + "data") + if (data.contains("node") && data.getCompound("node").contains("address")) { + tooltip.add(new StringTextComponent("§8" + data.getCompound("node").getString("address").substring(0, 13) + "...§7")) } } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala index c4fd815d66..3bc2721afd 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -8,11 +8,13 @@ import li.cil.oc.Settings import li.cil.oc.common.GuiType import net.minecraft.client.util.ITooltipFlag import li.cil.oc.common.item.data.DriveData -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumHand +import net.minecraft.util.ActionResultType +import net.minecraft.util.Hand +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World trait FileSystemLike extends Delegate { @@ -20,34 +22,34 @@ trait FileSystemLike extends Delegate { def kiloBytes: Int - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag): Unit = { - if (stack.hasTagCompound) { - val nbt = stack.getTagCompound - if (nbt.hasKey(Settings.namespace + "data")) { - val data = nbt.getCompoundTag(Settings.namespace + "data") - if (data.hasKey(Settings.namespace + "fs.label")) { - tooltip.add(data.getString(Settings.namespace + "fs.label")) + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + if (stack.hasTag) { + val nbt = stack.getTag + if (nbt.contains(Settings.namespace + "data")) { + val data = nbt.getCompound(Settings.namespace + "data") + if (data.contains(Settings.namespace + "fs.label")) { + tooltip.add(new StringTextComponent(data.getString(Settings.namespace + "fs.label"))) } - if (flag.isAdvanced && data.hasKey("fs")) { - val fsNbt = data.getCompoundTag("fs") - if (fsNbt.hasKey("capacity.used")) { + if (flag.isAdvanced && data.contains("fs")) { + val fsNbt = data.getCompound("fs") + if (fsNbt.contains("capacity.used")) { val used = fsNbt.getLong("capacity.used") - tooltip.add(Localization.Tooltip.DiskUsage(used, kiloBytes * 1024)) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskUsage(used, kiloBytes * 1024))) } } } val data = new DriveData(stack) - tooltip.add(Localization.Tooltip.DiskMode(data.isUnmanaged)) - tooltip.add(Localization.Tooltip.DiskLock(data.lockInfo)) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskMode(data.isUnmanaged))) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskLock(data.lockInfo))) } super.tooltipLines(stack, world, tooltip, flag) } - override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = { - if (!player.isSneaking && (!stack.hasTagCompound || !stack.getTagCompound.hasKey(Settings.namespace + "lootFactory"))) { - player.openGui(OpenComputers, GuiType.Drive.id, world, 0, 0, 0) - player.swingArm(EnumHand.MAIN_HAND) + override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { + if (!player.isCrouching && (!stack.hasTag || !stack.getTag.contains(Settings.namespace + "lootFactory"))) { + OpenComputers.openGui(player, GuiType.Drive.id, world, 0, 0, 0) + player.swing(Hand.MAIN_HAND) } - ActionResult.newResult(EnumActionResult.SUCCESS, stack) + new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala index f499f96775..3dc68f749a 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala @@ -4,19 +4,21 @@ import java.util import li.cil.oc.Localization import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait ItemTier extends Delegate { self: Delegate => - @SideOnly(Side.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { + @OnlyIn(Dist.CLIENT) + override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.tooltipLines(stack, world, tooltip, flag) if (flag.isAdvanced) { - tooltip.add(Localization.Tooltip.Tier(tierFromDriver(stack) + 1)) + tooltip.add(new StringTextComponent(Localization.Tooltip.Tier(tierFromDriver(stack) + 1))) } } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 2c85eafa62..86849f9ebe 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -4,40 +4,68 @@ import java.util import li.cil.oc.CreativeTab import li.cil.oc.Settings +import li.cil.oc.common.item.Delegator import li.cil.oc.common.tileentity import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.BlockItemUseContext import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.item.ItemUseContext +import net.minecraft.util.ActionResultType +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IWorldReader import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -trait SimpleItem extends Item { - setCreativeTab(CreativeTab) +import scala.collection.convert.WrapAsScala._ +trait SimpleItem extends Item { def createItemStack(amount: Int = 1) = new ItemStack(this, amount) - override def isBookEnchantable(stack: ItemStack, book: ItemStack) = false + @Deprecated + private var unlocalizedName = super.getDescriptionId() + + @Deprecated + private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name + + override def getDescriptionId = unlocalizedName - override def doesSneakBypassUse(stack: ItemStack, world: IBlockAccess, pos: BlockPos, player: EntityPlayer): Boolean = { - world.getTileEntity(pos) match { + override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = { + world.getBlockEntity(pos) match { case drive: tileentity.DiskDrive => true case _ => super.doesSneakBypassUse(stack, world, pos, player) } } - @SideOnly(Side.CLIENT) - override def addInformation(stack: ItemStack, world: World, tooltip: util.List[String], flag: ITooltipFlag) { - tooltip.addAll(Tooltip.get(getClass.getSimpleName.toLowerCase)) + @Deprecated + override def onItemUseFirst(stack: ItemStack, ctx: ItemUseContext): ActionResultType = { + val pos = ctx.getClickedPos + val hitPos = ctx.getClickLocation + onItemUseFirst(ctx.getPlayer, ctx.getPlayer.level, pos, ctx.getClickedFace, + (hitPos.x - pos.getX).toFloat, (hitPos.y - pos.getY).toFloat, (hitPos.z - pos.getZ).toFloat, ctx.getHand) + } + + @Deprecated + def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand) = ActionResultType.PASS + + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { + tooltip.add(new StringTextComponent(curr)) + } - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "data")) { - val data = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") - if (data.hasKey("node") && data.getCompoundTag("node").hasKey("address")) { - tooltip.add("§8" + data.getCompoundTag("node").getString("address").substring(0, 13) + "...§7") + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { + val data = stack.getTag.getCompound(Settings.namespace + "data") + if (data.contains("node") && data.getCompound("node").contains("address")) { + tooltip.add(new StringTextComponent("§8" + data.getCompound("node").getString("address").substring(0, 13) + "...§7")) } } } diff --git a/src/main/scala/li/cil/oc/common/launch/CoreModContainer.scala b/src/main/scala/li/cil/oc/common/launch/CoreModContainer.scala deleted file mode 100644 index afab110fd9..0000000000 --- a/src/main/scala/li/cil/oc/common/launch/CoreModContainer.scala +++ /dev/null @@ -1,19 +0,0 @@ -package li.cil.oc.common.launch - -import com.google.common.eventbus.EventBus -import net.minecraftforge.fml.common.DummyModContainer -import net.minecraftforge.fml.common.LoadController -import net.minecraftforge.fml.common.ModMetadata - -class CoreModContainer extends DummyModContainer({ - val md = new ModMetadata() - md.authorList.add("Sangar") - md.modId = "opencomputers|core" - md.version = "@VERSION@" - md.name = "OpenComputers (Core)" - md.url = "https://oc.cil.li/" - md.description = "OC core mod used for class transformer and as API owner to avoid cyclic dependencies." - md -}) { - override def registerBus(bus: EventBus, controller: LoadController) = true -} diff --git a/src/main/scala/li/cil/oc/common/launch/TransformerLoader.scala b/src/main/scala/li/cil/oc/common/launch/TransformerLoader.scala deleted file mode 100644 index 1b27f4a649..0000000000 --- a/src/main/scala/li/cil/oc/common/launch/TransformerLoader.scala +++ /dev/null @@ -1,23 +0,0 @@ -package li.cil.oc.common.launch - -import java.util - -import li.cil.oc.common.asm.ClassTransformer -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.{SortingIndex, TransformerExclusions} - -@SortingIndex(1001) -@TransformerExclusions(Array("li.cil.oc.common.asm")) -class TransformerLoader extends IFMLLoadingPlugin { - val instance = this - - override def getModContainerClass = "li.cil.oc.common.launch.CoreModContainer" - - override def getASMTransformerClass = Array(classOf[ClassTransformer].getName) - - override def getAccessTransformerClass = null - - override def getSetupClass = null - - override def injectData(data: util.Map[String, AnyRef]) {} -} diff --git a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala index c419650c9d..32550f539c 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala @@ -21,12 +21,13 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.PlayerUtils -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.potion.Potion -import net.minecraft.potion.PotionEffect -import net.minecraft.util.EnumParticleTypes +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.ParticleTypes +import net.minecraft.potion.Effect +import net.minecraft.potion.Effects +import net.minecraft.potion.EffectInstance import net.minecraft.util.ResourceLocation import net.minecraft.world.World @@ -34,16 +35,16 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessEndpoint { +class ControllerImpl(val player: PlayerEntity) extends Controller with WirelessEndpoint { if (isServer) api.Network.joinWirelessNetwork(this) - var previousDimension = player.world.provider.getDimension + var previousDimension = player.level.dimension lazy val CommandRange = Settings.get.nanomachinesCommandRange * Settings.get.nanomachinesCommandRange final val FullSyncInterval = 20 * 60 final val OverloadDamage = new DamageSourceWithRandomCause("oc.nanomachinesOverload", 3). - setDamageBypassesArmor(). - setDamageIsAbsolute() + bypassArmor(). + bypassMagic() var uuid = UUID.randomUUID.toString var responsePort = 0 @@ -56,7 +57,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE var activeBehaviorsDirty = true var hasSentConfiguration = false - override def world: World = player.getEntityWorld + override def world: World = player.level override def x: Int = BlockPosition(player).x @@ -65,8 +66,8 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE override def z: Int = BlockPosition(player).z override def receivePacket(packet: Packet, sender: WirelessEndpoint): Unit = { - if (getLocalBuffer > 0 && commandDelay < 1 && !player.isDead) { - val (dx, dy, dz) = ((sender.x + 0.5) - player.posX, (sender.y + 0.5) - player.posY, (sender.z + 0.5) - player.posZ) + if (getLocalBuffer > 0 && commandDelay < 1 && player.isAlive) { + val (dx, dy, dz) = ((sender.x + 0.5) - player.getX, (sender.y + 0.5) - player.getY, (sender.z + 0.5) - player.getZ) val dSquared = Math.sqrt(dx * dx + dy * dy + dz * dz) if (dSquared <= CommandRange) packet.data.headOption match { case Some(header: Array[Byte]) if new String(header, Charsets.UTF_8) == "nanomachines" => @@ -83,11 +84,11 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE case Array("saveConfiguration") => val nanomachines = api.Items.get(Constants.ItemName.Nanomachines) try { - val index = player.inventory.mainInventory.indexWhere(stack => api.Items.get(stack) == nanomachines && new NanomachineData(stack).configuration.isEmpty) + val index = player.inventory.items.indexWhere(stack => api.Items.get(stack) == nanomachines && new NanomachineData(stack).configuration.isEmpty) if (index >= 0) { - val stack = player.inventory.decrStackSize(index, 1) - new NanomachineData(this).save(stack) - player.inventory.addItemStackToInventory(stack) + val stack = player.inventory.removeItem(index, 1) + new NanomachineData(this).saveData(stack) + player.inventory.add(stack) InventoryUtils.spawnStackInWorld(BlockPosition(player), stack) respond(sender, "saved", true) } @@ -100,11 +101,11 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE case Array("getHealth") => respond(sender, "health", player.getHealth, player.getMaxHealth) case Array("getHunger") => - respond(sender, "hunger", player.getFoodStats.getFoodLevel, player.getFoodStats.getSaturationLevel) + respond(sender, "hunger", player.getFoodData.getFoodLevel, player.getFoodData.getSaturationLevel) case Array("getAge") => - respond(sender, "age", (player.getIdleTime / 20f).toInt) + respond(sender, "age", (player.getNoActionTime / 20f).toInt) case Array("getName") => - respond(sender, "name", player.getDisplayName.getUnformattedComponentText) + respond(sender, "name", player.getDisplayName.getString) case Array("getExperience") => respond(sender, "experience", player.experienceLevel) @@ -171,10 +172,10 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE activeBehaviorsDirty = true player match { - case playerMP: EntityPlayerMP if playerMP.connection != null => - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("blindness"), 100)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("poison"), 150)) - player.addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("slowness"), 200)) + case playerMP: ServerPlayerEntity if playerMP.connection != null => + player.addEffect(new EffectInstance(Effects.BLINDNESS, 100)) + player.addEffect(new EffectInstance(Effects.POISON, 150)) + player.addEffect(new EffectInstance(Effects.MOVEMENT_SLOWDOWN, 200)) changeBuffer(-Settings.get.nanomachineReconfigureCost) hasSentConfiguration = false @@ -217,7 +218,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE override def changeBuffer(delta: Double): Double = { if (isClient) delta - else if (delta < 0 && (Settings.get.ignorePower || player.capabilities.isCreativeMode)) 0.0 + else if (delta < 0 && (Settings.get.ignorePower || player.isCreative)) 0.0 else { val newValue = storedEnergy + delta storedEnergy = math.min(math.max(newValue, 0), getLocalBufferSize) @@ -228,7 +229,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE // ----------------------------------------------------------------------- // def update(): Unit = { - if (player.isDead) { + if (!player.isAlive) { return } @@ -245,10 +246,10 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE // load is called while the world is still set to the overworld, but // no dimension change event is fired if the player actually logged // out in another dimension... yay) - if (player.world.provider.getDimension != previousDimension) { + if (player.level.dimension != previousDimension) { api.Network.leaveWirelessNetwork(this, previousDimension) api.Network.joinWirelessNetwork(this) - previousDimension = player.world.provider.getDimension + previousDimension = player.level.dimension } else { api.Network.updateWirelessNetwork(this) @@ -271,14 +272,14 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE active.foreach(_.update()) if (isServer) { - if (player.getEntityWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (player.level.getGameTime % Settings.get.tickFrequency == 0) { changeBuffer(-Settings.get.nanomachineCost * Settings.get.tickFrequency * (activeInputs + 0.5)) PacketSender.sendNanomachinePower(player) } val overload = activeInputs - getSafeActiveInputs - if (!player.capabilities.isCreativeMode && overload > 0 && player.getEntityWorld.getTotalWorldTime % 20 == 0) { - player.attackEntityFrom(OverloadDamage, overload) + if (!player.isCreative && overload > 0 && player.level.getGameTime % 20 == 0) { + player.hurt(OverloadDamage, overload) } } @@ -286,7 +287,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE val energyRatio = getLocalBuffer / (getLocalBufferSize + 1) val triggerRatio = activeInputs / (configuration.triggers.length + 1) val intensity = (energyRatio + triggerRatio) * 0.25 - PlayerUtils.spawnParticleAround(player, EnumParticleTypes.PORTAL, intensity) + PlayerUtils.spawnParticleAround(player, ParticleTypes.PORTAL, intensity) } } @@ -299,7 +300,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE // Send a full sync every now and then, e.g. for other players coming // closer that weren't there to get the initial info for an enabled // input. - if (!hasSentConfiguration || player.getEntityWorld.getTotalWorldTime % FullSyncInterval == 0) { + if (!hasSentConfiguration || player.level.getGameTime % FullSyncInterval == 0) { hasSentConfiguration = true PacketSender.sendNanomachineConfiguration(player) } @@ -340,24 +341,24 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE // ----------------------------------------------------------------------- // - def save(nbt: NBTTagCompound): Unit = configuration.synchronized { - nbt.setString("uuid", uuid) - nbt.setInteger("port", responsePort) - nbt.setDouble("energy", storedEnergy) - nbt.setNewCompoundTag("configuration", configuration.save) + def saveData(nbt: CompoundNBT): Unit = configuration.synchronized { + nbt.putString("uuid", uuid) + nbt.putInt("port", responsePort) + nbt.putDouble("energy", storedEnergy) + nbt.setNewCompoundTag("configuration", configuration.saveData) } - def load(nbt: NBTTagCompound): Unit = configuration.synchronized { + def loadData(nbt: CompoundNBT): Unit = configuration.synchronized { uuid = nbt.getString("uuid") - responsePort = nbt.getInteger("port") + responsePort = nbt.getInt("port") storedEnergy = nbt.getDouble("energy") - configuration.load(nbt.getCompoundTag("configuration")) + configuration.loadData(nbt.getCompound("configuration")) activeBehaviorsDirty = true } // ----------------------------------------------------------------------- // - private def isClient = world.isRemote + private def isClient = world.isClientSide private def isServer = !isClient diff --git a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala index c166b696a0..40324fa914 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala @@ -6,7 +6,7 @@ import li.cil.oc.api.nanomachines.BehaviorProvider import li.cil.oc.api.nanomachines.Controller import li.cil.oc.server.PacketSender import li.cil.oc.util.PlayerUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import scala.collection.convert.WrapAsJava._ import scala.collection.mutable @@ -14,38 +14,38 @@ import scala.collection.mutable object Nanomachines extends api.detail.NanomachinesAPI { val providers = mutable.Set.empty[BehaviorProvider] - val serverControllers: mutable.WeakHashMap[EntityPlayer, ControllerImpl] = mutable.WeakHashMap.empty[EntityPlayer, ControllerImpl] - val clientControllers: mutable.WeakHashMap[EntityPlayer, ControllerImpl] = mutable.WeakHashMap.empty[EntityPlayer, ControllerImpl] + val serverControllers: mutable.WeakHashMap[PlayerEntity, ControllerImpl] = mutable.WeakHashMap.empty[PlayerEntity, ControllerImpl] + val clientControllers: mutable.WeakHashMap[PlayerEntity, ControllerImpl] = mutable.WeakHashMap.empty[PlayerEntity, ControllerImpl] - def controllers(player: EntityPlayer): mutable.WeakHashMap[EntityPlayer, ControllerImpl] = if (player.getEntityWorld.isRemote) clientControllers else serverControllers + def controllers(player: PlayerEntity): mutable.WeakHashMap[PlayerEntity, ControllerImpl] = if (player.level.isClientSide) clientControllers else serverControllers override def addProvider(provider: BehaviorProvider): Unit = providers += provider override def getProviders: java.lang.Iterable[BehaviorProvider] = providers - def getController(player: EntityPlayer): Controller = { + def getController(player: PlayerEntity): Controller = { if (hasController(player)) controllers(player).getOrElseUpdate(player, new ControllerImpl(player)) else null } - def hasController(player: EntityPlayer): Boolean = { + def hasController(player: PlayerEntity): Boolean = { PlayerUtils.persistedData(player).getBoolean(Settings.namespace + "hasNanomachines") } - def installController(player: EntityPlayer): Controller = { + def installController(player: PlayerEntity): Controller = { if (!hasController(player)) { - PlayerUtils.persistedData(player).setBoolean(Settings.namespace + "hasNanomachines", true) + PlayerUtils.persistedData(player).putBoolean(Settings.namespace + "hasNanomachines", true) } getController(player) // Initialize controller instance. } - override def uninstallController(player: EntityPlayer): Unit = { + override def uninstallController(player: PlayerEntity): Unit = { getController(player) match { case controller: ControllerImpl => controller.dispose() controllers(player) -= player - PlayerUtils.persistedData(player).removeTag(Settings.namespace + "hasNanomachines") - if (!player.getEntityWorld.isRemote) { + PlayerUtils.persistedData(player).remove(Settings.namespace + "hasNanomachines") + if (!player.level.isClientSide) { PacketSender.sendNanomachineConfiguration(player) } case _ => // Doesn't have one anyway. diff --git a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala index a172acd030..7190915731 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala @@ -8,10 +8,11 @@ import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.nanomachines.BehaviorProvider import li.cil.oc.server.PacketSender import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.text.TextComponentString +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Util +import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.TextFormatting import net.minecraftforge.common.util.Constants.NBT @@ -58,7 +59,7 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { } // Build connections. - val rng = new Random(controller.player.getEntityWorld.rand.nextInt()) + val rng = new Random(controller.player.level.random.nextInt()) def connect[Sink <: ConnectorNeuron, Source <: Neuron](sinks: Iterable[Sink], sources: mutable.ArrayBuffer[Source]): Unit = { // Shuffle sink list to give each entry the same chance. @@ -99,7 +100,7 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { // Enter debug configuration, one input -> one behavior, and list mapping in console. def debug(): Unit = { val log = controller.player match { - case playerMP: EntityPlayerMP => (s: String) => PacketSender.sendClientLog(s, playerMP) + case playerMP: ServerPlayerEntity => (s: String) => PacketSender.sendClientLog(s, playerMP) case _ => (s: String) => OpenComputers.log.info(s) } log(s"Creating debug configuration for nanomachines in player ${controller.player.getDisplayName}.") @@ -123,7 +124,7 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { } } - def print(player: EntityPlayer): Unit = { + def print(player: PlayerEntity): Unit = { val sb = StringBuilder.newBuilder def colored(value: Any, enabled: Boolean) = { if (enabled) sb.append(TextFormatting.GREEN) @@ -153,13 +154,13 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { } } sb.append(")") - player.sendMessage(new TextComponentString(sb.toString())) + player.sendMessage(new StringTextComponent(sb.toString()), Util.NIL_UUID) sb.clear() } } - override def save(nbt: NBTTagCompound): Unit = { - save(nbt, forItem = false) + override def saveData(nbt: CompoundNBT): Unit = { + saveData(nbt, forItem = false) } private final val TriggersTag = "triggers" @@ -170,46 +171,46 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { private final val TriggerInputsTag = "triggerInputs" private final val ConnectorInputsTag = "connectorInputs" - def save(nbt: NBTTagCompound, forItem: Boolean): Unit = { + def saveData(nbt: CompoundNBT, forItem: Boolean): Unit = { nbt.setNewTagList(TriggersTag, triggers.map(t => { - val nbt = new NBTTagCompound() - nbt.setBoolean(IsActiveTag, t.isActive && !forItem) + val nbt = new CompoundNBT() + nbt.putBoolean(IsActiveTag, t.isActive && !forItem) nbt })) nbt.setNewTagList(ConnectorsTag, connectors.map(c => { - val nbt = new NBTTagCompound() - nbt.setIntArray(TriggerInputsTag, c.inputs.map(triggers.indexOf(_)).filter(_ >= 0).toArray) + val nbt = new CompoundNBT() + nbt.putIntArray(TriggerInputsTag, c.inputs.map(triggers.indexOf(_)).filter(_ >= 0).toArray) nbt })) nbt.setNewTagList(BehaviorsTag, behaviors.map(b => { - val nbt = new NBTTagCompound() - nbt.setIntArray(TriggerInputsTag, b.inputs.map(triggers.indexOf(_)).filter(_ >= 0).toArray) - nbt.setIntArray(ConnectorInputsTag, b.inputs.map(connectors.indexOf(_)).filter(_ >= 0).toArray) - nbt.setTag(BehaviorTag, b.provider.writeToNBT(b.behavior)) + val nbt = new CompoundNBT() + nbt.putIntArray(TriggerInputsTag, b.inputs.map(triggers.indexOf(_)).filter(_ >= 0).toArray) + nbt.putIntArray(ConnectorInputsTag, b.inputs.map(connectors.indexOf(_)).filter(_ >= 0).toArray) + nbt.put(BehaviorTag, b.provider.save(b.behavior)) nbt })) } - override def load(nbt: NBTTagCompound): Unit = { + override def loadData(nbt: CompoundNBT): Unit = { triggers.clear() - nbt.getTagList(TriggersTag, NBT.TAG_COMPOUND).foreach((t: NBTTagCompound) => { + nbt.getList(TriggersTag, NBT.TAG_COMPOUND).foreach((t: CompoundNBT) => { val neuron = new TriggerNeuron() neuron.isActive = t.getBoolean(IsActiveTag) triggers += neuron }) connectors.clear() - nbt.getTagList(ConnectorsTag, NBT.TAG_COMPOUND).foreach((t: NBTTagCompound) => { + nbt.getList(ConnectorsTag, NBT.TAG_COMPOUND).foreach((t: CompoundNBT) => { val neuron = new ConnectorNeuron() neuron.inputs ++= t.getIntArray(TriggerInputsTag).map(triggers.apply) connectors += neuron }) behaviors.clear() - nbt.getTagList(BehaviorsTag, NBT.TAG_COMPOUND).foreach((t: NBTTagCompound) => { - api.Nanomachines.getProviders.find(p => p.readFromNBT(controller.player, t.getCompoundTag(BehaviorTag)) match { + nbt.getList(BehaviorsTag, NBT.TAG_COMPOUND).foreach((t: CompoundNBT) => { + api.Nanomachines.getProviders.find(p => p.load(controller.player, t.getCompound(BehaviorTag)) match { case b: Behavior => val neuron = new BehaviorNeuron(p, b) neuron.inputs ++= t.getIntArray(TriggerInputsTag).map(triggers.apply) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala index 691d879d7c..7f93526519 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala @@ -9,33 +9,35 @@ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.world.World +import net.minecraft.world.storage.IServerWorldInfo import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.entity.player.PlayerInteractEvent -import net.minecraftforge.fml.common.eventhandler.Event +import net.minecraftforge.eventbus.api.Event import scala.collection.mutable object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b1bddcdfe7") { - override def createScalaBehaviors(player: EntityPlayer) = Iterable(new DisintegrationBehavior(player)) + override def createScalaBehaviors(player: PlayerEntity) = Iterable(new DisintegrationBehavior(player)) - override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new DisintegrationBehavior(player) + override def readBehaviorFromNBT(player: PlayerEntity, nbt: CompoundNBT) = new DisintegrationBehavior(player) - class DisintegrationBehavior(p: EntityPlayer) extends AbstractBehavior(p) { + class DisintegrationBehavior(p: PlayerEntity) extends AbstractBehavior(p) { var breakingMap = mutable.Map.empty[BlockPosition, SlowBreakInfo] var breakingMapNew = mutable.Map.empty[BlockPosition, SlowBreakInfo] // Note: intentionally not overriding getNameHint. Gotta find this one manually! override def onDisable(reason: DisableReason): Unit = { - val world = player.getEntityWorld + val world = player.level for (pos <- breakingMap.keys) { world.destroyBlockInWorldPartially(pos.hashCode(), pos, -1) } @@ -43,11 +45,11 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b } override def update(): Unit = { - val world = player.getEntityWorld - if (!world.isRemote) player match { + val world = player.level + if (!world.isClientSide) player match { case _: FakePlayer => // Nope - case playerMP: EntityPlayerMP => - val now = world.getTotalWorldTime + case playerMP: ServerPlayerEntity => + val now = world.getGameTime // Check blocks in range. val blockPos = BlockPosition(player) @@ -59,17 +61,21 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b breakingMapNew += pos -> info info.update(world, player, now) case None => - val event = new PlayerInteractEvent.LeftClickBlock(player, pos.toBlockPos, player.getHorizontalFacing, null) + val event = new PlayerInteractEvent.LeftClickBlock(player, pos.toBlockPos, player.getDirection) MinecraftForge.EVENT_BUS.post(event) val allowed = !event.isCanceled && event.getUseBlock != Event.Result.DENY && event.getUseItem != Event.Result.DENY - val adventureOk = !world.getWorldInfo.getGameType.hasLimitedInteractions || player.canPlayerEdit(pos.toBlockPos, null, player.getHeldItemMainhand) + val placingRestricted = world.getLevelData match { + case srvInfo: IServerWorldInfo => srvInfo.getGameType.isBlockPlacingRestricted + case _ => true // Means it's not a server world (somehow). + } + val adventureOk = !placingRestricted || player.mayUseItemAt(pos.toBlockPos, null, player.getItemInHand(Hand.MAIN_HAND)) if (allowed && adventureOk && !world.isAirBlock(pos)) { val blockState = world.getBlockState(pos.toBlockPos) - val hardness = blockState.getBlock.getPlayerRelativeBlockHardness(world.getBlockState(pos.toBlockPos), player, world, pos.toBlockPos) + val hardness = blockState.getDestroyProgress(player, world, pos.toBlockPos) if (hardness > 0) { val timeToBreak = (1 / hardness).toInt if (timeToBreak < 20 * 30) { - val info = new SlowBreakInfo(now, now + timeToBreak, pos, StackOption(player.getHeldItemMainhand).map(_.copy()), blockState) + val info = new SlowBreakInfo(now, now + timeToBreak, pos, StackOption(player.getItemInHand(Hand.MAIN_HAND)).map(_.copy()), blockState) world.destroyBlockInWorldPartially(pos.hashCode(), pos, 0) breakingMapNew += pos -> info } @@ -101,19 +107,19 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b } } - class SlowBreakInfo(val timeStarted: Long, val timeBroken: Long, val pos: BlockPosition, val originalTool: StackOption, val blockState: IBlockState) { + class SlowBreakInfo(val timeStarted: Long, val timeBroken: Long, val pos: BlockPosition, val originalTool: StackOption, val blockState: BlockState) { var lastDamageSent = 0 - def checkTool(player: EntityPlayer): Boolean = { - val currentTool = StackOption(player.getHeldItemMainhand).map(_.copy()) + def checkTool(player: PlayerEntity): Boolean = { + val currentTool = StackOption(player.getItemInHand(Hand.MAIN_HAND)).map(_.copy()) (currentTool, originalTool) match { - case (SomeStack(stackA), SomeStack(stackB)) => stackA.getItem == stackB.getItem && (stackA.isItemStackDamageable || stackA.getItemDamage == stackB.getItemDamage) + case (SomeStack(stackA), SomeStack(stackB)) => stackA.getItem == stackB.getItem && (stackA.isDamageableItem || stackA.getDamageValue == stackB.getDamageValue) case (EmptyStack, EmptyStack) => true case _ => false } } - def update(world: World, player: EntityPlayer, now: Long): Unit = { + def update(world: World, player: PlayerEntity, now: Long): Unit = { val timeTotal = timeBroken - timeStarted if (timeTotal > 0) { val timeTaken = now - timeStarted @@ -125,12 +131,12 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b } } - def finish(world: World, player: EntityPlayerMP): Unit = { + def finish(world: World, player: ServerPlayerEntity): Unit = { val sameBlock = world.getBlockState(pos.toBlockPos) == blockState if (sameBlock) { world.destroyBlockInWorldPartially(pos.hashCode(), pos, -1) - if (player.interactionManager.tryHarvestBlock(pos.toBlockPos)) { - world.playAuxSFX(2001, pos, Block.getIdFromBlock(blockState.getBlock) + (blockState.getBlock.getMetaFromState(blockState) << 12)) + if (player.gameMode.destroyBlock(pos.toBlockPos)) { + world.playAuxSFX(2001, pos, Block.getId(blockState)) } } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala index 29a4bd2e18..4ff389a19f 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala @@ -6,24 +6,24 @@ import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.nanomachines.DisableReason import li.cil.oc.api.prefab.AbstractBehavior import li.cil.oc.integration.util.DamageSourceWithRandomCause -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT object HungryProvider extends ScalaProvider("d697c24a-014c-4773-a288-23084a59e9e8") { final val FillCount = 10 // Create a bunch of these to have a higher chance of one being picked / available. final val HungryDamage = new DamageSourceWithRandomCause("oc.nanomachinesHungry", 3). - setDamageBypassesArmor(). - setDamageIsAbsolute() + bypassArmor(). + bypassMagic() - override def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] = Iterable.fill(FillCount)(new HungryBehavior(player)) + override def createScalaBehaviors(player: PlayerEntity): Iterable[Behavior] = Iterable.fill(FillCount)(new HungryBehavior(player)) - override protected def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = new HungryBehavior(player) + override protected def readBehaviorFromNBT(player: PlayerEntity, nbt: CompoundNBT): Behavior = new HungryBehavior(player) - class HungryBehavior(player: EntityPlayer) extends AbstractBehavior(player) { + class HungryBehavior(player: PlayerEntity) extends AbstractBehavior(player) { override def onDisable(reason: DisableReason): Unit = { if (reason == DisableReason.OutOfEnergy) { - player.attackEntityFrom(HungryDamage, Settings.get.nanomachinesHungryDamage) + player.hurt(HungryDamage, Settings.get.nanomachinesHungryDamage) api.Nanomachines.getController(player).changeBuffer(Settings.get.nanomachinesHungryEnergyRestored) } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala index 49a0f966dd..c9d4b91e59 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala @@ -3,33 +3,33 @@ package li.cil.oc.common.nanomachines.provider import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.prefab.AbstractBehavior -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.math.Vec3d +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.math.vector.Vector3d import scala.collection.convert.WrapAsScala._ object MagnetProvider extends ScalaProvider("9324d5ec-71f1-41c2-b51c-406e527668fc") { - override def createScalaBehaviors(player: EntityPlayer) = Iterable(new MagnetBehavior(player)) + override def createScalaBehaviors(player: PlayerEntity) = Iterable(new MagnetBehavior(player)) - override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new MagnetBehavior(player) + override def readBehaviorFromNBT(player: PlayerEntity, nbt: CompoundNBT) = new MagnetBehavior(player) - class MagnetBehavior(player: EntityPlayer) extends AbstractBehavior(player) { + class MagnetBehavior(player: PlayerEntity) extends AbstractBehavior(player) { override def getNameHint = "magnet" override def update(): Unit = { - val world = player.getEntityWorld - if (!world.isRemote) { + val world = player.level + if (!world.isClientSide) { val actualRange = Settings.get.nanomachineMagnetRange * api.Nanomachines.getController(player).getInputCount(this) - val items = world.getEntitiesWithinAABB(classOf[EntityItem], player.getEntityBoundingBox.grow(actualRange, actualRange, actualRange)) + val items = world.getEntitiesOfClass(classOf[ItemEntity], player.getBoundingBox.inflate(actualRange, actualRange, actualRange)) items.collect { - case item: EntityItem if !item.cannotPickup && !item.getItem.isEmpty && player.inventory.mainInventory.exists(stack => stack.isEmpty || stack.getCount < stack.getMaxStackSize && stack.isItemEqual(item.getItem)) => - val dx = player.posX - item.posX - val dy = player.posY - item.posY - val dz = player.posZ - item.posZ - val delta = new Vec3d(dx, dy, dz).normalize() - item.addVelocity(delta.x * 0.1, delta.y * 0.1, delta.z * 0.1) + case item: ItemEntity if !item.hasPickUpDelay && !item.getItem.isEmpty && player.inventory.items.exists(stack => stack.isEmpty || stack.getCount < stack.getMaxStackSize && stack.sameItem(item.getItem)) => + val dx = player.getX - item.getX + val dy = player.getY - item.getY + val dz = player.getZ - item.getZ + val delta = new Vector3d(dx, dy, dz).normalize() + item.push(delta.x * 0.1, delta.y * 0.1, delta.z * 0.1) } } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala index 2aaa72b51c..2cad56597b 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala @@ -5,48 +5,50 @@ import li.cil.oc.api import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.prefab.AbstractBehavior import li.cil.oc.util.PlayerUtils -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumParticleTypes +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.BasicParticleType +import net.minecraft.particles.ParticleType +import net.minecraft.particles.ParticleTypes +import net.minecraftforge.registries.ForgeRegistries +import net.minecraftforge.registries.ForgeRegistry object ParticleProvider extends ScalaProvider("b48c4bbd-51bb-4915-9367-16cff3220e4b") { - final val ParticleTypes = Array( - EnumParticleTypes.FIREWORKS_SPARK, - EnumParticleTypes.TOWN_AURA, - EnumParticleTypes.SMOKE_NORMAL, - EnumParticleTypes.SPELL_WITCH, - EnumParticleTypes.NOTE, - EnumParticleTypes.ENCHANTMENT_TABLE, - EnumParticleTypes.FLAME, - EnumParticleTypes.LAVA, - EnumParticleTypes.WATER_SPLASH, - EnumParticleTypes.REDSTONE, - EnumParticleTypes.SLIME, - EnumParticleTypes.HEART, - EnumParticleTypes.VILLAGER_HAPPY + final val ParticleTypeList: Array[BasicParticleType] = Array( + ParticleTypes.FIREWORK, + ParticleTypes.SMOKE, + ParticleTypes.WITCH, + ParticleTypes.NOTE, + ParticleTypes.ENCHANT, + ParticleTypes.FLAME, + ParticleTypes.LAVA, + ParticleTypes.SPLASH, + ParticleTypes.ITEM_SLIME, + ParticleTypes.HEART, + ParticleTypes.HAPPY_VILLAGER ) - override def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] = ParticleTypes.map(new ParticleBehavior(_, player)) + override def createScalaBehaviors(player: PlayerEntity): Iterable[Behavior] = ParticleTypeList.map(new ParticleBehavior(_, player)) - override def writeBehaviorToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { + override def writeBehaviorToNBT(behavior: Behavior, nbt: CompoundNBT): Unit = { behavior match { case particles: ParticleBehavior => - nbt.setInteger("effectName", particles.effectType.getParticleID) + nbt.putInt("effectName", ForgeRegistries.PARTICLE_TYPES.asInstanceOf[ForgeRegistry[ParticleType[_]]].getID(particles.effectType)) case _ => // Wat. } } - override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = { - val effectType = EnumParticleTypes.getParticleFromId(nbt.getInteger("effectName")) - new ParticleBehavior(effectType, player) + override def readBehaviorFromNBT(player: PlayerEntity, nbt: CompoundNBT): Behavior = { + val effectType = ForgeRegistries.PARTICLE_TYPES.asInstanceOf[ForgeRegistry[ParticleType[_]]].getValue(nbt.getInt("effectName")) + new ParticleBehavior(effectType.asInstanceOf[BasicParticleType], player) } - class ParticleBehavior(var effectType: EnumParticleTypes, player: EntityPlayer) extends AbstractBehavior(player) { - override def getNameHint = "particles." + effectType.getParticleName + class ParticleBehavior(var effectType: BasicParticleType, player: PlayerEntity) extends AbstractBehavior(player) { + override def getNameHint = "particles." + effectType.getRegistryName.getPath override def update(): Unit = { - val world = player.getEntityWorld - if (world.isRemote && Settings.get.enableNanomachinePfx) { + val world = player.level + if (world.isClientSide && Settings.get.enableNanomachinePfx) { PlayerUtils.spawnParticleAround(player, effectType, api.Nanomachines.getController(player).getInputCount(this) * 0.25) } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala index 548d1751b2..8e05924bc3 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala @@ -5,10 +5,12 @@ import li.cil.oc.api import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.nanomachines.DisableReason import li.cil.oc.api.prefab.AbstractBehavior -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.potion.Potion -import net.minecraft.potion.PotionEffect +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.potion.Effect +import net.minecraft.potion.EffectInstance +import net.minecraft.util.ResourceLocation +import net.minecraftforge.registries.ForgeRegistries import scala.collection.convert.WrapAsScala._ @@ -18,46 +20,47 @@ object PotionProvider extends ScalaProvider("c29e4eec-5a46-479a-9b3d-ad0f06da784 def filterPotions[T](list: Iterable[T]) = { list.map { - case name: String => Option(Potion.getPotionFromResourceLocation(name)) - case id: java.lang.Number => Option(Potion.getPotionById(id.intValue())) + case name: String => Option(ForgeRegistries.POTIONS.getValue(new ResourceLocation(name))) + case loc: ResourceLocation => Option(ForgeRegistries.POTIONS.getValue(loc)) + case id: java.lang.Number => Option(Effect.byId(id.intValue())) case _ => None }.collect { case Some(potion) => potion }.toSet } - def isPotionEligible(potion: Potion) = potion != null && PotionWhitelist.contains(potion) + def isPotionEligible(potion: Effect) = potion != null && PotionWhitelist.contains(potion) - override def createScalaBehaviors(player: EntityPlayer) = { - Potion.REGISTRY.filter(isPotionEligible).map(new PotionBehavior(_, player)) + override def createScalaBehaviors(player: PlayerEntity) = { + ForgeRegistries.POTIONS.getValues.filter(isPotionEligible).map(new PotionBehavior(_, player)) } - override def writeBehaviorToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { + override def writeBehaviorToNBT(behavior: Behavior, nbt: CompoundNBT): Unit = { behavior match { case potionBehavior: PotionBehavior => - nbt.setString("potionId", Potion.REGISTRY.getNameForObject(potionBehavior.potion).toString) + nbt.putString("potionId", potionBehavior.potion.getRegistryName.toString) case _ => // Shouldn't happen, ever. } } - override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = { + override def readBehaviorFromNBT(player: PlayerEntity, nbt: CompoundNBT) = { val potionId = nbt.getString("potionId") - new PotionBehavior(Potion.getPotionFromResourceLocation(potionId), player) + new PotionBehavior(ForgeRegistries.POTIONS.getValue(new ResourceLocation(potionId)), player) } - class PotionBehavior(val potion: Potion, player: EntityPlayer) extends AbstractBehavior(player) { + class PotionBehavior(val potion: Effect, player: PlayerEntity) extends AbstractBehavior(player) { final val Duration = 600 - def amplifier(player: EntityPlayer) = api.Nanomachines.getController(player).getInputCount(this) - 1 + def amplifier(player: PlayerEntity) = api.Nanomachines.getController(player).getInputCount(this) - 1 - override def getNameHint: String = potion.getName.stripPrefix("potion.") + override def getNameHint: String = potion.getDescriptionId.stripPrefix("effect.") override def onDisable(reason: DisableReason): Unit = { - player.removePotionEffect(potion) + player.removeEffect(potion) } override def update(): Unit = { - player.addPotionEffect(new PotionEffect(potion, Duration, amplifier(player), true, Settings.get.enableNanomachinePfx)) + player.addEffect(new EffectInstance(potion, Duration, amplifier(player), true, Settings.get.enableNanomachinePfx)) } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala index 93285282a4..a24f7445d7 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala @@ -2,12 +2,12 @@ package li.cil.oc.common.nanomachines.provider import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.prefab.AbstractProvider -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import scala.collection.convert.WrapAsJava._ abstract class ScalaProvider(id: String) extends AbstractProvider(id) { - def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] + def createScalaBehaviors(player: PlayerEntity): Iterable[Behavior] - override def createBehaviors(player: EntityPlayer): java.lang.Iterable[Behavior] = asJavaIterable(createScalaBehaviors(player)) + override def createBehaviors(player: PlayerEntity): java.lang.Iterable[Behavior] = asJavaIterable(createScalaBehaviors(player)) } diff --git a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala deleted file mode 100644 index a746a58d71..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala +++ /dev/null @@ -1,90 +0,0 @@ -package li.cil.oc.common.recipe - -import li.cil.oc.util.Color -import li.cil.oc.util.ItemColorizer -import li.cil.oc.util.StackOption -import net.minecraft.block.Block -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.world.World - -/** - * @author asie, Vexatos - */ -class ColorizeRecipe(target: Item, source: Array[Item] = null) extends ContainerItemAwareRecipe { - def this(target: Block, source: Array[Item]) = this(Item.getItemFromBlock(target), source) - def this(target: Block) = this(target, null) - - val targetItem: Item = target - val sourceItems: Array[Item] = if (source != null) source else Array(targetItem) - - override def matches(crafting: InventoryCrafting, world: World): Boolean = { - val stacks = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))) - val targets = stacks.filter(stack => sourceItems.contains(stack.getItem) || stack.getItem == targetItem) - val other = stacks.filterNot(targets.contains(_)) - targets.size == 1 && other.nonEmpty && other.forall(Color.isDye) - } - - override def getCraftingResult(crafting: InventoryCrafting): ItemStack = { - var targetStack: ItemStack = ItemStack.EMPTY - val color = Array[Int](0, 0, 0) - var colorCount = 0 - var maximum = 0 - - (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))).foreach { stack => - if (sourceItems.contains(stack.getItem) - || stack.getItem == targetItem) { - targetStack = stack.copy() - targetStack.setCount(1) - } else { - val dye = Color.findDye(stack) - if (dye.isEmpty) - return ItemStack.EMPTY - - val itemColor = Color.byOreName(dye.get).getColorComponentValues - val red = (itemColor(0) * 255.0F).toInt - val green = (itemColor(1) * 255.0F).toInt - val blue = (itemColor(2) * 255.0F).toInt - maximum += Math.max(red, Math.max(green, blue)) - color(0) += red - color(1) += green - color(2) += blue - colorCount = colorCount + 1 - } - } - - if (targetStack.isEmpty) return ItemStack.EMPTY - - if (targetItem == targetStack.getItem) { - if (ItemColorizer.hasColor(targetStack)) { - val itemColor = ItemColorizer.getColor(targetStack) - val red = (itemColor >> 16 & 255).toFloat / 255.0F - val green = (itemColor >> 8 & 255).toFloat / 255.0F - val blue = (itemColor & 255).toFloat / 255.0F - maximum = (maximum.toFloat + Math.max(red, Math.max(green, blue)) * 255.0F).toInt - color(0) = (color(0).toFloat + red * 255.0F).toInt - color(1) = (color(1).toFloat + green * 255.0F).toInt - color(2) = (color(2).toFloat + blue * 255.0F).toInt - colorCount = colorCount + 1 - } - } else if (sourceItems.contains(targetStack.getItem)) { - targetStack = new ItemStack(targetItem, targetStack.getCount, targetStack.getItemDamage) - } - - var red = color(0) / colorCount - var green = color(1) / colorCount - var blue = color(2) / colorCount - val max = maximum.toFloat / colorCount.toFloat - val div = Math.max(red, Math.max(green, blue)).toFloat - red = (red.toFloat * max / div).toInt - green = (green.toFloat * max / div).toInt - blue = (blue.toFloat * max / div).toInt - ItemColorizer.setColor(targetStack, (red << 16) | (green << 8) | blue) - targetStack - } - - override def getMinimumRecipeSize = 2 - - override def getRecipeOutput = ItemStack.EMPTY -} diff --git a/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala deleted file mode 100644 index 69f38488ba..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ContainerItemAwareRecipe.scala +++ /dev/null @@ -1,14 +0,0 @@ -package li.cil.oc.common.recipe - -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.crafting.IRecipe -import net.minecraftforge.common.ForgeHooks -import net.minecraftforge.registries.IForgeRegistryEntry - -trait ContainerItemAwareRecipe extends IForgeRegistryEntry.Impl[IRecipe] with IRecipe { - override def getRemainingItems(inv: InventoryCrafting) = ForgeHooks.defaultRecipeGetRemainingItems(inv) - - def getMinimumRecipeSize: Int - - override def canFit(width: Int, height: Int): Boolean = width * height >= getMinimumRecipeSize -} diff --git a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala deleted file mode 100644 index 7ec676235e..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala +++ /dev/null @@ -1,48 +0,0 @@ -package li.cil.oc.common.recipe - -import li.cil.oc.util.ItemColorizer -import li.cil.oc.util.StackOption -import net.minecraft.block.Block -import net.minecraft.init.Items -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.world.World - -/** - * @author Vexatos - */ -class DecolorizeRecipe(target: Item) extends ContainerItemAwareRecipe { - def this(target: Block) = this(Item.getItemFromBlock(target)) - - val targetItem: Item = target - - override def matches(crafting: InventoryCrafting, world: World): Boolean = { - val stacks = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))) - val targets = stacks.filter(stack => stack.getItem == targetItem) - val other = stacks.filterNot(targets.contains) - targets.size == 1 && other.size == 1 && other.forall(_.getItem == Items.WATER_BUCKET) - } - - override def getCraftingResult(crafting: InventoryCrafting): ItemStack = { - var targetStack: ItemStack = ItemStack.EMPTY - - (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))).foreach { stack => - if (stack.getItem == targetItem) { - targetStack = stack.copy() - targetStack.setCount(1) - } else if (stack.getItem != Items.WATER_BUCKET) { - return ItemStack.EMPTY - } - } - - if (targetStack.isEmpty) return ItemStack.EMPTY - - ItemColorizer.removeColor(targetStack) - targetStack - } - - override def getMinimumRecipeSize = 2 - - override def getRecipeOutput = ItemStack.EMPTY -} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedFuzzyShapelessRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedFuzzyShapelessRecipe.scala deleted file mode 100644 index 6232b9e577..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedFuzzyShapelessRecipe.scala +++ /dev/null @@ -1,27 +0,0 @@ -package li.cil.oc.common.recipe - -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.ItemStack -import net.minecraftforge.oredict.ShapelessOreRecipe - -import scala.collection.mutable.ListBuffer - -class ExtendedFuzzyShapelessRecipe(result: ItemStack, ingredients: AnyRef*) extends ExtendedShapelessOreRecipe(result, ingredients: _*) { - override def matches(inv: net.minecraft.inventory.InventoryCrafting, world: net.minecraft.world.World): Boolean = { - val requiredItems = ingredients.map(any => any.asInstanceOf[ItemStack]).toList.to[ListBuffer] - //.groupBy{ case s: ItemStack => s.getItem }.mapValues(_.size).toSeq: _*) - for (i <- 0 until inv.getSizeInventory) { - val itemStack = inv.getStackInSlot(i) - if (!itemStack.isEmpty) { - val index = requiredItems.indexWhere(req => { - if (req.getItem != itemStack.getItem) return false - req.getItemDamage == itemStack.getItemDamage - }) - if (index >= 0) { - requiredItems.remove(index) - } - } - } - requiredItems.isEmpty - } -} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala deleted file mode 100644 index 46bec34d3f..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala +++ /dev/null @@ -1,268 +0,0 @@ -package li.cil.oc.common.recipe - -import java.util.UUID - -import li.cil.oc.Constants -import li.cil.oc.Settings -import li.cil.oc.api -import li.cil.oc.api.detail.ItemInfo -import li.cil.oc.common.item.data.DroneData -import li.cil.oc.common.item.data.MicrocontrollerData -import li.cil.oc.common.item.data.PrintData -import li.cil.oc.common.item.data.RobotData -import li.cil.oc.common.item.data.TabletData -import li.cil.oc.server.machine.luac.LuaStateFactory -import li.cil.oc.util.Color -import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.SideTracker -import net.minecraft.init.Blocks -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.IRecipe -import net.minecraft.nbt.NBTTagCompound - -import scala.collection.convert.WrapAsScala._ -import scala.util.control.Breaks._ - -object ExtendedRecipe { - private lazy val drone = api.Items.get(Constants.ItemName.Drone) - private lazy val eeprom = api.Items.get(Constants.ItemName.EEPROM) - private lazy val luaBios = api.Items.get(Constants.ItemName.LuaBios) - private lazy val mcu = api.Items.get(Constants.BlockName.Microcontroller) - private lazy val navigationUpgrade = api.Items.get(Constants.ItemName.NavigationUpgrade) - private lazy val linkedCard = api.Items.get(Constants.ItemName.LinkedCard) - private lazy val floppy = api.Items.get(Constants.ItemName.Floppy) - private lazy val hdds = Array( - api.Items.get(Constants.ItemName.HDDTier1), - api.Items.get(Constants.ItemName.HDDTier2), - api.Items.get(Constants.ItemName.HDDTier3) - ) - private lazy val cpus = Array( - api.Items.get(Constants.ItemName.CPUTier1), - api.Items.get(Constants.ItemName.CPUTier2), - api.Items.get(Constants.ItemName.CPUTier3), - api.Items.get(Constants.ItemName.APUTier1), - api.Items.get(Constants.ItemName.APUTier2) - ) - private lazy val robot = api.Items.get(Constants.BlockName.Robot) - private lazy val tablet = api.Items.get(Constants.ItemName.Tablet) - private lazy val print = api.Items.get(Constants.BlockName.Print) - private lazy val disabled = { - val stack = new ItemStack(Blocks.DIRT) - val tag = new NBTTagCompound() - tag.setNewCompoundTag("display", _.setNewTagList("Lore", "Autocrafting of this item is disabled to avoid exploits.")) - stack.setTagCompound(tag) - stack - } - - def addNBTToResult(recipe: IRecipe, craftedStack: ItemStack, inventory: InventoryCrafting): ItemStack = { - val craftedItemName = api.Items.get(craftedStack) - - if (craftedItemName == navigationUpgrade) { - Option(api.Driver.driverFor(craftedStack)).foreach(driver => - for (stack <- getItems(inventory)) { - if (stack.getItem == net.minecraft.init.Items.FILLED_MAP) { - // Store information of the map used for crafting in the result. - val nbt = driver.dataTag(craftedStack) - nbt.setNewCompoundTag(Settings.namespace + "map", stack.writeToNBT) - } - }) - } - - if (craftedItemName == linkedCard) { - if (SideTracker.isServer) { - Option(api.Driver.driverFor(craftedStack)).foreach(driver => { - val nbt = driver.dataTag(craftedStack) - nbt.setString(Settings.namespace + "tunnel", UUID.randomUUID().toString) - }) - } - } - - if (cpus.contains(craftedItemName)) { - LuaStateFactory.setDefaultArch(craftedStack) - } - - if (craftedItemName == floppy || hdds.contains(craftedItemName)) { - if (!craftedStack.hasTagCompound) { - craftedStack.setTagCompound(new NBTTagCompound()) - } - val nbt = craftedStack.getTagCompound - if (recipe.canFit(1, 1)) { - // Formatting / loot to normal disk conversion, only keep coloring. - val colorKey = Settings.namespace + "color" - for (stack <- getItems(inventory)) { - if (api.Items.get(stack) != null && (api.Items.get(stack) == floppy || api.Items.get(stack).name == "lootDisk") && stack.hasTagCompound) { - val oldData = stack.getTagCompound - if (oldData.hasKey(colorKey) && oldData.getInteger(colorKey) != Color.dyes.indexOf("lightGray")) { - nbt.setTag(colorKey, oldData.getTag(colorKey).copy()) - } - } - } - if (nbt.hasNoTags) { - craftedStack.setTagCompound(null) - } - } - else if (getItems(inventory).forall(api.Items.get(_) == floppy)) { - // Copy operation. - for (stack <- getItems(inventory)) { - if (api.Items.get(stack) == floppy && stack.hasTagCompound) { - val oldData = stack.getTagCompound - for (oldTagName <- oldData.getKeySet.map(_.asInstanceOf[String]) if !nbt.hasKey(oldTagName)) { - nbt.setTag(oldTagName, oldData.getTag(oldTagName).copy()) - } - } - } - } - } - - if (craftedItemName == print && - recipe.isInstanceOf[ExtendedShapelessOreRecipe] && - recipe.asInstanceOf[ExtendedShapelessOreRecipe].getIngredients.size == 2) { - // First, copy old data. - val data = new PrintData(craftedStack) - val inputs = getItems(inventory) - for (stack <- inputs) { - if (api.Items.get(stack) == print) { - data.load(stack) - } - } - - // Then apply new data. - val beaconBlocks = Array( - new ItemStack(net.minecraft.init.Blocks.IRON_BLOCK), - new ItemStack(net.minecraft.init.Blocks.GOLD_BLOCK), - new ItemStack(net.minecraft.init.Blocks.EMERALD_BLOCK), - new ItemStack(net.minecraft.init.Blocks.DIAMOND_BLOCK) - ) - - val glowstoneDust = new ItemStack(net.minecraft.init.Items.GLOWSTONE_DUST) - val glowstone = new ItemStack(net.minecraft.init.Blocks.GLOWSTONE) - for (stack <- inputs) { - if (beaconBlocks.exists(_.isItemEqual(stack))) { - if (data.isBeaconBase) { - // Crafting wouldn't change anything, prevent accidental resource loss. - return ItemStack.EMPTY - } - data.isBeaconBase = true - } - if (glowstoneDust.isItemEqual(stack)) { - if (data.lightLevel == 15) { - // Crafting wouldn't change anything, prevent accidental resource loss. - return ItemStack.EMPTY - } - data.lightLevel = math.min(15, data.lightLevel + 1) - } - if (glowstone.isItemEqual(stack)) { - if (data.lightLevel == 15) { - // Crafting wouldn't change anything, prevent accidental resource loss. - return ItemStack.EMPTY - } - data.lightLevel = math.min(15, data.lightLevel + 4) - } - } - - // Finally apply modified data. - data.save(craftedStack) - } - - // EEPROM copying. - if (craftedItemName == eeprom && - craftedStack.getCount == 2 && - recipe.isInstanceOf[ExtendedShapelessOreRecipe] && - recipe.asInstanceOf[ExtendedShapelessOreRecipe].getIngredients.size == 2) breakable { - for (stack <- getItems(inventory)) { - if (api.Items.get(stack) == eeprom && stack.hasTagCompound) { - val copy = stack.getTagCompound.copy.asInstanceOf[NBTTagCompound] - // Erase node address, just in case. - copy.getCompoundTag(Settings.namespace + "data").getCompoundTag("node").removeTag("address") - craftedStack.setTagCompound(copy) - break() - } - } - } - - // Swapping EEPROM in devices. - recraft(craftedStack, inventory, mcu, stack => new MCUDataWrapper(stack)) - recraft(craftedStack, inventory, drone, stack => new DroneDataWrapper(stack)) - recraft(craftedStack, inventory, robot, stack => new RobotDataWrapper(stack)) - recraft(craftedStack, inventory, tablet, stack => new TabletDataWrapper(stack)) - - craftedStack - } - - private def getItems(inventory: InventoryCrafting) = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot).filter(!_.isEmpty) - - private def recraft(craftedStack: ItemStack, inventory: InventoryCrafting, descriptor: ItemInfo, dataFactory: (ItemStack) => ItemDataWrapper) { - if (api.Items.get(craftedStack) == descriptor) { - // Find old Microcontroller. - getItems(inventory).find(api.Items.get(_) == descriptor) match { - case Some(oldMcu) => - val data = dataFactory(oldMcu) - - // Remove old EEPROM. - val oldRom = data.components.filter(api.Items.get(_) == eeprom) - data.components = data.components.diff(oldRom) - - // Insert new EEPROM. - for (stack <- getItems(inventory)) { - if (api.Items.get(stack) == eeprom) { - data.components :+= stack.copy.splitStack(1) - } - } - - data.save(craftedStack) - case _ => - } - } - } - - private trait ItemDataWrapper { - def components: Array[ItemStack] - - def components_=(value: Array[ItemStack]): Unit - - def save(stack: ItemStack): Unit - } - - private class MCUDataWrapper(val stack: ItemStack) extends ItemDataWrapper { - val data = new MicrocontrollerData(stack) - - override def components: Array[ItemStack] = data.components - - override def components_=(value: Array[ItemStack]): Unit = data.components = value - - override def save(stack: ItemStack): Unit = data.save(stack) - } - - private class DroneDataWrapper(val stack: ItemStack) extends ItemDataWrapper { - val data = new DroneData(stack) - - override def components: Array[ItemStack] = data.components - - override def components_=(value: Array[ItemStack]): Unit = data.components = value - - override def save(stack: ItemStack): Unit = data.save(stack) - } - - private class RobotDataWrapper(val stack: ItemStack) extends ItemDataWrapper { - val data = new RobotData(stack) - - override def components: Array[ItemStack] = data.components - - override def components_=(value: Array[ItemStack]): Unit = data.components = value - - override def save(stack: ItemStack): Unit = data.save(stack) - } - - private class TabletDataWrapper(val stack: ItemStack) extends ItemDataWrapper { - val data = new TabletData(stack) - - var components: Array[ItemStack] = data.items.filter(!_.isEmpty) - - override def save(stack: ItemStack): Unit = { - data.items = components.clone() - data.save(stack) - } - } - -} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedOreRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedOreRecipe.scala deleted file mode 100644 index 0dc472b3e0..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedOreRecipe.scala +++ /dev/null @@ -1,11 +0,0 @@ -package li.cil.oc.common.recipe - -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.ItemStack -import net.minecraft.util.ResourceLocation -import net.minecraftforge.oredict.ShapedOreRecipe - -class ExtendedShapedOreRecipe(result: ItemStack, ingredients: AnyRef*) extends ShapedOreRecipe(null, result, ingredients: _*) { - override def getCraftingResult(inventory: InventoryCrafting) = - ExtendedRecipe.addNBTToResult(this, super.getCraftingResult(inventory), inventory) -} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessOreRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessOreRecipe.scala deleted file mode 100644 index 1c9c9ce6b2..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessOreRecipe.scala +++ /dev/null @@ -1,10 +0,0 @@ -package li.cil.oc.common.recipe - -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.ItemStack -import net.minecraftforge.oredict.ShapelessOreRecipe - -class ExtendedShapelessOreRecipe(result: ItemStack, ingredients: AnyRef*) extends ShapelessOreRecipe(null, result, ingredients: _*) { - override def getCraftingResult(inventory: InventoryCrafting): ItemStack = - ExtendedRecipe.addNBTToResult(this, super.getCraftingResult(inventory), inventory) -} diff --git a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala deleted file mode 100644 index 2743649551..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala +++ /dev/null @@ -1,53 +0,0 @@ -package li.cil.oc.common.recipe - -import li.cil.oc.Settings -import li.cil.oc.common.Loot -import li.cil.oc.integration.util.Wrench -import li.cil.oc.util.StackOption -import net.minecraft.inventory.InventoryCrafting -import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.IRecipe -import net.minecraft.util.NonNullList -import net.minecraft.world.World -import net.minecraftforge.registries.IForgeRegistryEntry - -import scala.collection.immutable - -class LootDiskCyclingRecipe extends IForgeRegistryEntry.Impl[IRecipe] with IRecipe { - override def matches(crafting: InventoryCrafting, world: World): Boolean = { - val stacks = collectStacks(crafting).toArray - stacks.length == 2 && stacks.exists(Loot.isLootDisk) && stacks.exists(Wrench.isWrench) - } - - override def getCraftingResult(crafting: InventoryCrafting): ItemStack = { - val lootDiskStacks = Loot.disksForCycling - collectStacks(crafting).find(Loot.isLootDisk) match { - case Some(lootDisk) if lootDiskStacks.nonEmpty => - val lootFactoryName = getLootFactoryName(lootDisk) - val oldIndex = lootDiskStacks.indexWhere(s => getLootFactoryName(s) == lootFactoryName) - val newIndex = (oldIndex + 1) % lootDiskStacks.length - lootDiskStacks(newIndex).copy() - case _ => ItemStack.EMPTY - } - } - - def getLootFactoryName(stack: ItemStack): String = stack.getTagCompound.getString(Settings.namespace + "lootFactory") - - def collectStacks(crafting: InventoryCrafting): immutable.IndexedSeq[ItemStack] = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))) - - override def canFit(width: Int, height: Int): Boolean = width * height >= 2 - - override def getRecipeOutput: ItemStack = ItemStack.EMPTY - - override def getRemainingItems(crafting: InventoryCrafting): NonNullList[ItemStack] = { - val result = NonNullList.withSize[ItemStack](crafting.getSizeInventory, ItemStack.EMPTY) - for (slot <- 0 until crafting.getSizeInventory) { - val stack = crafting.getStackInSlot(slot) - if (Wrench.isWrench(stack)) { - result.set(slot, stack.copy()) - stack.setCount(0) - } - } - result - } -} diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala deleted file mode 100644 index 93589d601b..0000000000 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ /dev/null @@ -1,516 +0,0 @@ -package li.cil.oc.common.recipe - -import java.io.File -import java.io.FileReader - -import com.typesafe.config._ -import li.cil.oc._ -import li.cil.oc.common.Loot -import li.cil.oc.common.block.SimpleBlock -import li.cil.oc.common.init.Items -import li.cil.oc.common.item.Delegator -import li.cil.oc.common.item.data.PrintData -import li.cil.oc.common.item.traits.Delegate -import li.cil.oc.common.item.traits.SimpleItem -import li.cil.oc.integration.util.ItemBlacklist -import li.cil.oc.util.Color -import net.minecraft.block.Block -import net.minecraft.item.Item -import net.minecraft.item.ItemBlock -import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.IRecipe -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.ResourceLocation -import net.minecraft.util.registry.RegistryNamespaced -import net.minecraftforge.fluids.FluidRegistry -import net.minecraftforge.fluids.FluidStack -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.registry.GameRegistry -import net.minecraftforge.oredict.OreDictionary -import net.minecraftforge.oredict.RecipeSorter -import net.minecraftforge.oredict.RecipeSorter.Category -import net.minecraftforge.registries.{GameData, IForgeRegistryEntry} -import org.apache.commons.io.FileUtils - -import scala.collection.convert.WrapAsScala._ -import scala.collection.mutable - -object Recipes { - val list: mutable.LinkedHashMap[ItemStack, String] = mutable.LinkedHashMap.empty[ItemStack, String] - val oreDictEntries: mutable.LinkedHashMap[String, ItemStack] = mutable.LinkedHashMap.empty[String, ItemStack] - var hadErrors = false - val recipeHandlers: mutable.LinkedHashMap[String, (ItemStack, Config) => Unit] = mutable.LinkedHashMap.empty[String, (ItemStack, Config) => Unit] - - def registerRecipeHandler(name: String, recipe: (ItemStack, Config) => Unit): Unit = { - recipeHandlers += name -> recipe - } - - def addBlock(instance: Block, name: String, oreDict: String*): Block = { - Items.registerBlock(instance, name) - addRecipe(new ItemStack(instance), name) - register(instance match { - case simple: SimpleBlock => simple.createItemStack() - case _ => new ItemStack(instance) - }, oreDict: _*) - instance - } - - def addSubItem[T <: Delegate](delegate: T, name: String, oreDict: String*): T = { - Items.registerItem(delegate, name) - addRecipe(delegate.createItemStack(), name) - register(delegate.createItemStack(), oreDict: _*) - delegate - } - - def addItem(instance: Item, name: String, oreDict: String*): Item = { - Items.registerItem(instance, name) - addRecipe(new ItemStack(instance), name) - register(instance match { - case simple: SimpleItem => simple.createItemStack() - case _ => new ItemStack(instance) - }, oreDict: _*) - instance - } - - def addSubItem[T <: common.item.traits.Delegate](delegate: T, name: String, registerRecipe: Boolean, oreDict: String*): T = { - Items.registerItem(delegate, name) - if (registerRecipe) { - addRecipe(delegate.createItemStack(), name) - register(delegate.createItemStack(), oreDict: _*) - } - else { - ItemBlacklist.hide(delegate) - } - delegate - } - - def addStack(stack: ItemStack, name: String, oreDict: String*): ItemStack = { - Items.registerStack(stack, name) - addRecipe(stack, name) - register(stack, oreDict: _*) - stack - } - - def addRecipe(stack: ItemStack, name: String) { - list += stack -> name - } - - private def register(item: ItemStack, names: String*) { - for (name <- names if name != null) { - oreDictEntries += name -> item - } - } - - def init() { - RecipeSorter.register(Settings.namespace + "extshaped", classOf[ExtendedShapedOreRecipe], Category.SHAPED, "after:forge:shapedore") - RecipeSorter.register(Settings.namespace + "extshapeless", classOf[ExtendedShapelessOreRecipe], Category.SHAPELESS, "after:forge:shapelessore") - RecipeSorter.register(Settings.namespace + "colorizer", classOf[ColorizeRecipe], Category.SHAPELESS, "after:forge:shapelessore") - RecipeSorter.register(Settings.namespace + "decolorizer", classOf[DecolorizeRecipe], Category.SHAPELESS, "after:oc:colorizer") - RecipeSorter.register(Settings.namespace + "lootcycler", classOf[LootDiskCyclingRecipe], Category.SHAPELESS, "after:forge:shapelessore") - - for ((name, stack) <- oreDictEntries) { - if (!OreDictionary.getOres(name).contains(stack)) { - OreDictionary.registerOre(name, stack) - } - } - oreDictEntries.clear() - - try { - val recipeSets = Array("default", "hardmode", "gregtech", "peaceful") - val recipeDirectory = new File(Loader.instance.getConfigDir + File.separator + "opencomputers") - val userRecipes = new File(recipeDirectory, "user.recipes") - userRecipes.getParentFile.mkdirs() - if (!userRecipes.exists()) { - FileUtils.copyURLToFile(getClass.getResource("/assets/opencomputers/recipes/user.recipes"), userRecipes) - } - for (recipeSet <- recipeSets) { - FileUtils.copyURLToFile(getClass.getResource(s"/assets/opencomputers/recipes/$recipeSet.recipes"), new File(recipeDirectory, s"$recipeSet.recipes")) - } - lazy val config: ConfigParseOptions = ConfigParseOptions.defaults. - setSyntax(ConfigSyntax.CONF). - setIncluder(new ConfigIncluder with ConfigIncluderFile { - var fallback: ConfigIncluder = _ - - override def withFallback(fallback: ConfigIncluder): ConfigIncluder = { - this.fallback = fallback - this - } - - override def include(context: ConfigIncludeContext, what: String): ConfigObject = fallback.include(context, what) - - override def includeFile(context: ConfigIncludeContext, what: File): ConfigObject = { - val in = if (what.isAbsolute) new FileReader(what) else new FileReader(new File(userRecipes.getParentFile, what.getPath)) - val result = ConfigFactory.parseReader(in, config) - in.close() - result.root() - } - }) - val recipes = ConfigFactory.parseFile(userRecipes, config) - - // Register all known recipes. - for ((stack, name) <- list) { - if (recipes.hasPath(name)) { - val value = recipes.getValue(name) - value.valueType match { - case ConfigValueType.OBJECT => - addRecipe(stack, recipes.getConfig(name), s"'$name'") - case ConfigValueType.BOOLEAN => - // Explicitly disabled, keep in NEI if true. - if (!value.unwrapped.asInstanceOf[Boolean]) { - hide(stack) - } - case _ => - OpenComputers.log.error(s"Failed adding recipe for '$name', you will not be able to craft this item. The error was: Invalid value for recipe.") - hadErrors = true - } - } - else { - OpenComputers.log.warn(s"No recipe for '$name', you will not be able to craft this item. To suppress this warning, disable the recipe (assign `false` to it).") - hadErrors = true - } - } - - // Register all unknown recipes. Well. Loot disk recipes. - if (recipes.hasPath("lootdisks")) try { - val lootRecipes = recipes.getConfigList("lootdisks") - val lootStacks = Loot.globalDisks.map(_._1) - for (recipe <- lootRecipes) { - val name = recipe.getString("name") - lootStacks.find(s => s.getTagCompound.getString(Settings.namespace + "lootFactory") == name) match { - case Some(stack) => addRecipe(stack, recipe, s"loot disk '$name'") - case _ => - OpenComputers.log.warn(s"Failed adding recipe for loot disk '$name': No such global loot disk.") - hadErrors = true - } - } - } - catch { - case t: Throwable => - OpenComputers.log.warn("Failed parsing loot disk recipes.", t) - hadErrors = true - } - - if (recipes.hasPath("generic")) try { - val genericRecipes = recipes.getConfigList("generic") - for (recipe <- genericRecipes) { - val result = recipe.getValue("result").unwrapped() - parseIngredient(result) match { - case stack: ItemStack => addRecipe(stack, recipe, s"'$result'") - case _ => - OpenComputers.log.warn(s"Failed adding generic recipe for '$result': Invalid output (make sure it's not an OreDictionary name).") - hadErrors = true - } - } - } - catch { - case t: Throwable => - OpenComputers.log.warn("Failed parsing generic recipes.", t) - hadErrors = true - } - - // Recrafting operations. - val cable = api.Items.get(Constants.BlockName.Cable) - val chamelium = api.Items.get(Constants.ItemName.Chamelium) - val chameliumBlock = api.Items.get(Constants.BlockName.ChameliumBlock) - val drone = api.Items.get(Constants.ItemName.Drone) - val eeprom = api.Items.get(Constants.ItemName.EEPROM) - val floppy = api.Items.get(Constants.ItemName.Floppy) - val hoverBoots = api.Items.get(Constants.ItemName.HoverBoots) - val mcu = api.Items.get(Constants.BlockName.Microcontroller) - val navigationUpgrade = api.Items.get(Constants.ItemName.NavigationUpgrade) - val print = api.Items.get(Constants.BlockName.Print) - val relay = api.Items.get(Constants.BlockName.Relay) - val robot = api.Items.get(Constants.BlockName.Robot) - val tablet = api.Items.get(Constants.ItemName.Tablet) - val linkedCard = api.Items.get(Constants.ItemName.LinkedCard) - - // Navigation upgrade recrafting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - navigationUpgrade.createItemStack(1), - navigationUpgrade.createItemStack(1), new ItemStack(net.minecraft.init.Items.FILLED_MAP, 1, OreDictionary.WILDCARD_VALUE))) - - // Floppy disk coloring. - for (dye <- Color.dyes) { - val result = floppy.createItemStack(1) - val tag = new NBTTagCompound() - tag.setInteger(Settings.namespace + "color", Color.dyes.indexOf(dye)) - result.setTagCompound(tag) - Recipes.addRecipe(new ExtendedShapelessOreRecipe(result, floppy.createItemStack(1), dye)) - } - - // Microcontroller recrafting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - mcu.createItemStack(1), - mcu.createItemStack(1), eeprom.createItemStack(1))) - - // Drone recrafting. - Recipes.addRecipe(new ExtendedFuzzyShapelessRecipe( - drone.createItemStack(1), - drone.createItemStack(1), eeprom.createItemStack(1))) - - // EEPROM copying via crafting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - eeprom.createItemStack(2), - eeprom.createItemStack(1), eeprom.createItemStack(1))) - - // Robot recrafting. - Recipes.addRecipe(new ExtendedFuzzyShapelessRecipe( - robot.createItemStack(1), - robot.createItemStack(1), eeprom.createItemStack(1))) - - // Tablet recrafting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - tablet.createItemStack(1), - tablet.createItemStack(1), eeprom.createItemStack(1))) - - // Chamelium block splitting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - chamelium.createItemStack(9), - chameliumBlock.createItemStack(1))) - - // Chamelium dying. - for ((dye, meta) <- Color.dyes.zipWithIndex) { - val result = chameliumBlock.createItemStack(1) - result.setItemDamage(meta) - val input = chameliumBlock.createItemStack(1) - input.setItemDamage(OreDictionary.WILDCARD_VALUE) - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - result, - input, dye)) - } - - // Print beaconification. - val beaconPrint = print.createItemStack(1) - - { - val printData = new PrintData(beaconPrint) - printData.isBeaconBase = true - printData.save(beaconPrint) - } - - for (block <- Array( - net.minecraft.init.Blocks.IRON_BLOCK, - net.minecraft.init.Blocks.GOLD_BLOCK, - net.minecraft.init.Blocks.EMERALD_BLOCK, - net.minecraft.init.Blocks.DIAMOND_BLOCK - )) { - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - beaconPrint, - print.createItemStack(1), new ItemStack(block))) - } - - // Floppy disk formatting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe(floppy.createItemStack(1), floppy.createItemStack(1))) - - // Hard disk formatting. - val hdds = Array( - api.Items.get(Constants.ItemName.HDDTier1), - api.Items.get(Constants.ItemName.HDDTier2), - api.Items.get(Constants.ItemName.HDDTier3) - ) - for (hdd <- hdds) { - Recipes.addRecipe(new ExtendedShapelessOreRecipe(hdd.createItemStack(1), hdd.createItemStack(1))) - } - - // EEPROM formatting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe(eeprom.createItemStack(1), eeprom.createItemStack(1))) - - // Print light value increments. - val lightPrint = print.createItemStack(1) - - { - val printData = new PrintData(lightPrint) - printData.lightLevel = 1 - printData.save(lightPrint) - } - - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - lightPrint, - print.createItemStack(1), new ItemStack(net.minecraft.init.Items.GLOWSTONE_DUST))) - - { - val printData = new PrintData(lightPrint) - printData.lightLevel = 4 - printData.save(lightPrint) - } - - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - lightPrint, - print.createItemStack(1), new ItemStack(net.minecraft.init.Blocks.GLOWSTONE))) - - // Hover Boot dyeing - Recipes.addRecipe(new ColorizeRecipe(hoverBoots.item()), "colorizeBoots") - Recipes.addRecipe(new DecolorizeRecipe(hoverBoots.item()), "decolorizeBoots") - - // Cable dyeing - Recipes.addRecipe(new ColorizeRecipe(cable.block()), "colorizeCable") - Recipes.addRecipe(new DecolorizeRecipe(cable.block()), "decolorizeCable") - - // Loot disk cycling. - if (Settings.get.lootRecrafting) { - Recipes.addRecipe(new LootDiskCyclingRecipe(), "lootCycling") - } - - // link card copying via crafting. - Recipes.addRecipe(new ExtendedShapelessOreRecipe( - linkedCard.createItemStack(2), - linkedCard.createItemStack(1), linkedCard.createItemStack(1))) - - } - catch { - case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) - } - list.clear() - } - - private def addRecipe(output: ItemStack, recipe: Config, name: String) = try { - val recipeType = tryGetType(recipe) - recipeHandlers.get(recipeType) match { - case Some(recipeHandler) => recipeHandler(output, recipe) - case _ => - OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item. The error was: Invalid recipe type '$recipeType'.") - hadErrors = true - } - } - catch { - case e: RecipeException => - OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item.", e) - hadErrors = true - } - - def tryGetCount(recipe: Config): Int = if (recipe.hasPath("output")) recipe.getInt("output") else 1 - - def parseIngredient(entry: AnyRef): AnyRef = entry match { - case map: java.util.Map[AnyRef, AnyRef]@unchecked => - if (map.contains("oreDict")) { - map.get("oreDict") match { - case value: String => value - case other => throw new RecipeException(s"Invalid name in recipe (not a string: $other).") - } - } - else if (map.contains("item")) { - map.get("item") match { - case name: String => - findItem(name) match { - case Some(item: Item) => new ItemStack(item, 1, tryGetId(map)) - case _ => throw new RecipeException(s"No item found with name '$name'.") - } - case id: Number => new ItemStack(validateItemId(id), 1, tryGetId(map)) - case other => throw new RecipeException(s"Invalid item name in recipe (not a string: $other).") - } - } - else if (map.contains("block")) { - map.get("block") match { - case name: String => - findBlock(name) match { - case Some(block: Block) => new ItemStack(block, 1, tryGetId(map)) - case _ => throw new RecipeException(s"No block found with name '$name'.") - } - case id: Number => new ItemStack(validateBlockId(id), 1, tryGetId(map)) - case other => throw new RecipeException(s"Invalid block name (not a string: $other).") - } - } - else throw new RecipeException("Invalid ingredient type (no oreDict, item or block entry).") - case name: String => - if (name == null || name.trim.isEmpty) ItemStack.EMPTY - else if (OreDictionary.getOres(name) != null && !OreDictionary.getOres(name).isEmpty) name - else { - findItem(name) match { - case Some(item: Item) => new ItemStack(item, 1, 0) - case _ => - findBlock(name) match { - case Some(block: Block) => new ItemStack(block, 1, 0) - case _ => throw new RecipeException(s"No ore dictionary entry, item or block found for ingredient with name '$name'.") - } - } - } - case other => throw new RecipeException(s"Invalid ingredient type (not a map or string): $other") - } - - def parseFluidIngredient(entry: Config): Option[FluidStack] = { - val fluid = FluidRegistry.getFluid(entry.getString("name")) - val amount = - if (entry.hasPath("amount")) entry.getInt("amount") - else 1000 - Option(new FluidStack(fluid, amount)) - } - - private def findItem(name: String) = getObjectWithoutFallback(Item.REGISTRY, name).orElse(Item.REGISTRY.find { - case item: Item => item.getUnlocalizedName == name || item.getUnlocalizedName == "item." + name || Item.REGISTRY.getNameForObject(item).toString == name - case _ => false - }) - - private def findBlock(name: String) = getObjectWithoutFallback(Block.REGISTRY.asInstanceOf[RegistryNamespaced[ResourceLocation, Block]], name).orElse(Block.REGISTRY.find { - case block: Block => block.getUnlocalizedName == name || block.getUnlocalizedName == "tile." + name || Block.REGISTRY.getNameForObject(block).toString == name - case _ => false - }) - - private def getObjectWithoutFallback[V](registry: RegistryNamespaced[ResourceLocation, V], key: String) = { - val loc = new ResourceLocation(key) - if (registry.containsKey(loc)) Option(registry.getObject(loc)) - else None - } - - private def tryGetType(recipe: Config) = if (recipe.hasPath("type")) recipe.getString("type") else "shaped" - - private def tryGetId(ingredient: java.util.Map[AnyRef, AnyRef]): Int = - if (ingredient.contains("subID")) ingredient.get("subID") match { - case id: Number => id.intValue - case "any" => OreDictionary.WILDCARD_VALUE - case id: String => Integer.valueOf(id) - case _ => 0 - } else 0 - - private def validateBlockId(id: Number) = { - val index = id.intValue - val block = Block.getBlockById(index) - if (block == null) throw new RecipeException(s"Invalid block ID: $index") - block - } - - private def validateItemId(id: Number) = { - val index = id.intValue - val item = Item.getItemById(index) - if (item == null) throw new RecipeException(s"Invalid item ID: $index") - item - } - - private def hide(value: ItemStack) { - Delegator.subItem(value) match { - case Some(stack) => - stack.showInItemList = false - case _ => - } - value.getItem match { - case simple: SimpleItem => - simple.setCreativeTab(null) - case _ => - } - value.getItem match { - case itemBlock: ItemBlock => itemBlock.getBlock match { - case simple: SimpleBlock => - simple.setCreativeTab(null) - ItemBlacklist.hide(simple) - case _ => - } - case _ => - } - } - - class RecipeException(message: String) extends RuntimeException(message) - - def addRecipe(recipe: IForgeRegistryEntry.Impl[IRecipe], group: String): IRecipe = GameData.register_impl(recipe.setRegistryName(Settings.resourceDomain, group)) - - private var recipeCounter: Int = 0 - - def addRecipe(recipe: IForgeRegistryEntry.Impl[IRecipe]): Unit = recipe match { - case r: IRecipe => r.getRecipeOutput match { - case stack: ItemStack if !stack.isEmpty => - // Who cares about recipe names? - addRecipe(recipe, stack.getItem.getRegistryName.getResourcePath + recipeCounter.toString) - recipeCounter += 1 - case _ => throw new IllegalArgumentException("invalid recipe name: " + r.getRecipeOutput) - } - case _ => throw new IllegalArgumentException("invalid recipe name: " + recipe) - } -} diff --git a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala index 6928527b22..588493ae02 100644 --- a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala +++ b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala @@ -12,7 +12,7 @@ import li.cil.oc.common.Tier import li.cil.oc.util.ExtendedNBT._ import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.util.text.ITextComponent import net.minecraftforge.common.util.Constants.NBT @@ -26,14 +26,14 @@ object AssemblerTemplates { private val templateFilters = mutable.ArrayBuffer.empty[Method] - def add(template: NBTTagCompound): Unit = { + def add(template: CompoundNBT): Unit = { val selector = IMC.getStaticMethod(template.getString("select"), classOf[ItemStack]) val validator = IMC.getStaticMethod(template.getString("validate"), classOf[IInventory]) val assembler = IMC.getStaticMethod(template.getString("assemble"), classOf[IInventory]) val hostClass = tryGetHostClass(template.getString("hostClass")) - val containerSlots = template.getTagList("containerSlots", NBT.TAG_COMPOUND).map((tag: NBTTagCompound) => parseSlot(tag, Some(Slot.Container), hostClass)).take(3).padTo(3, NoSlot).toArray - val upgradeSlots = template.getTagList("upgradeSlots", NBT.TAG_COMPOUND).map((tag: NBTTagCompound) => parseSlot(tag, Some(Slot.Upgrade), hostClass)).take(9).padTo(9, NoSlot).toArray - val componentSlots = template.getTagList("componentSlots", NBT.TAG_COMPOUND).map((tag: NBTTagCompound) => parseSlot(tag, None, hostClass)).take(9).padTo(9, NoSlot).toArray + val containerSlots = template.getList("containerSlots", NBT.TAG_COMPOUND).map((tag: CompoundNBT) => parseSlot(tag, Some(Slot.Container), hostClass)).take(3).padTo(3, NoSlot).toArray + val upgradeSlots = template.getList("upgradeSlots", NBT.TAG_COMPOUND).map((tag: CompoundNBT) => parseSlot(tag, Some(Slot.Upgrade), hostClass)).take(9).padTo(9, NoSlot).toArray + val componentSlots = template.getList("componentSlots", NBT.TAG_COMPOUND).map((tag: CompoundNBT) => parseSlot(tag, None, hostClass)).take(9).padTo(9, NoSlot).toArray templates += new Template(selector, validator, assembler, containerSlots, upgradeSlots, componentSlots) } @@ -85,10 +85,10 @@ object AssemblerTemplates { } } - private def parseSlot(nbt: NBTTagCompound, kindOverride: Option[String], hostClass: Option[Class[_ <: EnvironmentHost]]) = { - val kind = kindOverride.getOrElse(if (nbt.hasKey("type")) nbt.getString("type") else Slot.None) - val tier = if (nbt.hasKey("tier")) nbt.getInteger("tier") else Tier.Any - val validator = if (nbt.hasKey("validate")) Option(IMC.getStaticMethod(nbt.getString("validate"), classOf[IInventory], classOf[Int], classOf[Int], classOf[ItemStack])) else None + private def parseSlot(nbt: CompoundNBT, kindOverride: Option[String], hostClass: Option[Class[_ <: EnvironmentHost]]) = { + val kind = kindOverride.getOrElse(if (nbt.contains("type")) nbt.getString("type") else Slot.None) + val tier = if (nbt.contains("tier")) nbt.getInt("tier") else Tier.Any + val validator = if (nbt.contains("validate")) Option(IMC.getStaticMethod(nbt.getString("validate"), classOf[IInventory], classOf[Int], classOf[Int], classOf[ItemStack])) else None new Slot(kind, tier, validator, hostClass) } diff --git a/src/main/scala/li/cil/oc/common/template/DisassemblerTemplates.scala b/src/main/scala/li/cil/oc/common/template/DisassemblerTemplates.scala index 42a4733616..fe86a58cd6 100644 --- a/src/main/scala/li/cil/oc/common/template/DisassemblerTemplates.scala +++ b/src/main/scala/li/cil/oc/common/template/DisassemblerTemplates.scala @@ -5,14 +5,14 @@ import java.lang.reflect.Method import li.cil.oc.OpenComputers import li.cil.oc.common.IMC import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable object DisassemblerTemplates { private val templates = mutable.ArrayBuffer.empty[Template] - def add(template: NBTTagCompound): Unit = try { + def add(template: CompoundNBT): Unit = try { val selector = IMC.getStaticMethod(template.getString("select"), classOf[ItemStack]) val disassembler = IMC.getStaticMethod(template.getString("disassemble"), classOf[ItemStack], classOf[Array[ItemStack]]) diff --git a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala index 1ba28adb26..5de4adc5ee 100644 --- a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala @@ -30,14 +30,14 @@ object DroneTemplate extends Template { def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) def assemble(inventory: IInventory) = { - val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) + val items = (0 until inventory.getContainerSize).map(inventory.getItem) val data = new DroneData() data.tier = caseTier(inventory) data.name = RobotData.randomName data.components = items.drop(1).filter(!_.isEmpty).toArray data.storedEnergy = Settings.get.bufferDrone.toInt val stack = api.Items.get(Constants.ItemName.Drone).createItemStack(1) - data.save(stack) + data.saveData(stack) val energy = Settings.get.droneBaseCost + complexity(inventory) * Settings.get.droneComplexityCost Array(stack, Double.box(energy)) @@ -139,5 +139,5 @@ object DroneTemplate extends Template { else if (caseTier(inventory) == Tier.Four) 9001 // Creative else 5 - override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getStackInSlot(0)) + override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getItem(0)) } diff --git a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala index 333eab5175..119abf23bd 100644 --- a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala @@ -29,7 +29,7 @@ object MicrocontrollerTemplate extends Template { def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) def assemble(inventory: IInventory): Array[Object] = { - val items = (0 until inventory.getSizeInventory).map(inventory.getStackInSlot) + val items = (0 until inventory.getContainerSize).map(inventory.getItem) val data = new MicrocontrollerData() data.tier = caseTier(inventory) data.components = items.drop(1).filter(!_.isEmpty).toArray @@ -133,5 +133,5 @@ object MicrocontrollerTemplate extends Template { else if (caseTier(inventory) == Tier.Four) 9001 // Creative else 4 - override protected def caseTier(inventory: IInventory): Int = ItemUtils.caseTier(inventory.getStackInSlot(0)) + override protected def caseTier(inventory: IInventory): Int = ItemUtils.caseTier(inventory.getItem(0)) } diff --git a/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala b/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala index 71b48b3538..99d1a9f50f 100644 --- a/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/NavigationUpgradeTemplate.scala @@ -13,7 +13,7 @@ object NavigationUpgradeTemplate { def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { val info = new NavigationUpgradeData(stack) ingredients.map { - case part if part.getItem == net.minecraft.init.Items.FILLED_MAP => info.map + case part if part.getItem == net.minecraft.item.Items.FILLED_MAP => info.map case part => part } } diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala index 59ae9f1b7d..4e8971574b 100644 --- a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -27,7 +27,7 @@ object RobotTemplate extends Template { def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) def assemble(inventory: IInventory) = { - val items = (1 until inventory.getSizeInventory).map(inventory.getStackInSlot) + val items = (1 until inventory.getContainerSize).map(inventory.getItem) val data = new RobotData() data.tier = caseTier(inventory) data.name = RobotData.randomName @@ -187,5 +187,5 @@ object RobotTemplate extends Template { "li.cil.oc.common.template.RobotTemplate.disassemble") } - override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getStackInSlot(0)) + override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getItem(0)) } diff --git a/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala b/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala index 6b4edab25c..0dd6549125 100644 --- a/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala @@ -18,7 +18,7 @@ object ServerTemplate { val info = new ServerInventory { override def container = stack } - Array(ingredients, (0 until info.getSizeInventory).map(info.getStackInSlot).filter(null !=).toArray) + Array(ingredients, (0 until info.getContainerSize).map(info.getItem).filter(null !=).toArray) } def register() { diff --git a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala index 13bd1c04da..d3180af979 100644 --- a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala @@ -38,15 +38,15 @@ object TabletTemplate extends Template { def validate(inventory: IInventory): Array[AnyRef] = validateComputer(inventory) def assemble(inventory: IInventory): Array[AnyRef] = { - val items = (1 until inventory.getSizeInventory).map(slot => inventory.getStackInSlot(slot)) + val items = (1 until inventory.getContainerSize).map(slot => inventory.getItem(slot)) val data = new TabletData() - data.tier = ItemUtils.caseTier(inventory.getStackInSlot(0)) + data.tier = ItemUtils.caseTier(inventory.getItem(0)) data.container = items.headOption.getOrElse(ItemStack.EMPTY) data.items = Array(api.Items.get(Constants.BlockName.ScreenTier1).createItemStack(1)) ++ items.drop(if (data.tier == Tier.One) 0 else 1).filter(!_.isEmpty) data.energy = Settings.get.bufferTablet data.maxEnergy = data.energy val stack = api.Items.get(Constants.ItemName.Tablet).createItemStack(1) - data.save(stack) + data.saveData(stack) val energy = Settings.get.tabletBaseCost + complexity(inventory) * Settings.get.tabletComplexityCost Array(stack, Double.box(energy)) @@ -152,5 +152,5 @@ object TabletTemplate extends Template { override protected def maxComplexity(inventory: IInventory) = super.maxComplexity(inventory) / 2 + 5 - override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getStackInSlot(0)) + override protected def caseTier(inventory: IInventory) = ItemUtils.caseTier(inventory.getItem(0)) } diff --git a/src/main/scala/li/cil/oc/common/template/Template.scala b/src/main/scala/li/cil/oc/common/template/Template.scala index e237af2a88..e5a5e7f623 100644 --- a/src/main/scala/li/cil/oc/common/template/Template.scala +++ b/src/main/scala/li/cil/oc/common/template/Template.scala @@ -62,7 +62,7 @@ abstract class Template { } protected def exists(inventory: IInventory, p: ItemStack => Boolean) = { - (0 until inventory.getSizeInventory).exists(slot => StackOption(inventory.getStackInSlot(slot)) match { + (0 until inventory.getContainerSize).exists(slot => StackOption(inventory.getItem(slot)) match { case SomeStack(stack) => p(stack) case _ => false }) @@ -78,8 +78,8 @@ abstract class Template { case _ => false }) - protected def requiresRAM(inventory: IInventory) = !(0 until inventory.getSizeInventory). - map(inventory.getStackInSlot). + protected def requiresRAM(inventory: IInventory) = !(0 until inventory.getContainerSize). + map(inventory.getItem). exists(stack => api.Driver.driverFor(stack, hostClass) match { case driver: api.driver.item.Processor => val architecture = driver.architecture(stack) @@ -104,8 +104,8 @@ abstract class Template { protected def complexity(inventory: IInventory) = { var acc = 0 - for (slot <- 1 until inventory.getSizeInventory) { - val stack = inventory.getStackInSlot(slot) + for (slot <- 1 until inventory.getContainerSize) { + val stack = inventory.getItem(slot) acc += (Option(api.Driver.driverFor(stack, hostClass)) match { case Some(driver: api.driver.item.Processor) => 0 // CPUs are exempt, since they control the limit. case Some(driver: api.driver.item.Container) => (1 + driver.tier(stack)) * 2 @@ -118,8 +118,8 @@ abstract class Template { protected def maxComplexity(inventory: IInventory) = { val caseTier = this.caseTier(inventory) - val cpuTier = (0 until inventory.getSizeInventory).foldRight(0)((slot, acc) => { - val stack = inventory.getStackInSlot(slot) + val cpuTier = (0 until inventory.getContainerSize).foldRight(0)((slot, acc) => { + val stack = inventory.getItem(slot) acc + (api.Driver.driverFor(stack, hostClass) match { case processor: api.driver.item.Processor => processor.tier(stack) case _ => 0 diff --git a/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala b/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala index edcad23014..7bc4f0e225 100644 --- a/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala +++ b/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala @@ -3,9 +3,9 @@ package li.cil.oc.common.template import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api -import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation +import net.minecraftforge.registries.ForgeRegistries import scala.collection.convert.WrapAsScala._ @@ -13,13 +13,15 @@ object TemplateBlacklist { private lazy val TheBlacklist = { // scnr val pattern = """^([^@]+)(?:@(\d+))?$""".r def parseDescriptor(id: String, meta: Int) = { - val item = Item.REGISTRY.getObject(new ResourceLocation(id)) + val item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id)) if (item == null) { OpenComputers.log.warn(s"Bad assembler blacklist entry '$id', unknown item id.") None } else { - Option(new ItemStack(item, 1, meta)) + val stack = new ItemStack(item, 1) + stack.setDamageValue(meta) + Option(stack) } } Settings.get.assemblerBlacklist.map { @@ -42,6 +44,6 @@ object TemplateBlacklist { } def filter(stack: ItemStack): Boolean = { - !TheBlacklist.exists(_.isItemEqual(stack)) + !TheBlacklist.exists(_.sameItem(stack)) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala index 0b4ac2e1e5..11ac4e7475 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala @@ -15,19 +15,20 @@ import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network._ import li.cil.oc.common.Slot import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.SoundEvents +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.SoundEvents import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.WrapAsJava._ import scala.collection.mutable -class Adapter extends traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { +class Adapter extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { val node = api.Network.newNode(this, Visibility.Network).create() private val blocks = Array.fill[Option[(ManagedEnvironment, DriverBlock)]](6)(None) @@ -49,21 +50,21 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra override protected def defaultState = true - override def setSideOpen(side: EnumFacing, value: Boolean) { + override def setSideOpen(side: Direction, value: Boolean) { super.setSideOpen(side, value) if (isServer) { ServerPacketSender.sendAdapterState(this) - getWorld.playSound(null, x + 0.5, y + 0.5, z + 0.5, SoundEvents.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5f, getWorld.rand.nextFloat() * 0.25f + 0.7f) - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, false) + getLevel.playSound(null, getBlockPos, SoundEvents.PISTON_EXTEND, SoundCategory.BLOCKS, 0.5f, getLevel.random.nextFloat() * 0.25f + 0.7f) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) neighborChanged(side) } else { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { (blocks collect { case Some((environment, _)) => environment.node }) ++ @@ -83,10 +84,10 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra } } - def neighborChanged(d: EnumFacing) { + def neighborChanged(d: Direction) { if (node != null && node.network != null) { - val blockPos = getPos.offset(d) - getWorld.getTileEntity(blockPos) match { + val blockPos = getBlockPos.relative(d) + getLevel.getBlockEntity(blockPos) match { case _: traits.Environment => // Don't provide adaption for our stuffs. This is mostly to avoid // cables and other non-functional stuff popping up in the adapter @@ -94,7 +95,7 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra // but the only 'downside' is that it can't be used to manipulate // inventories, which I actually consider a plus :P case _ => - Option(api.Driver.driverFor(getWorld, blockPos, d)) match { + Option(api.Driver.driverFor(getLevel, blockPos, d)) match { case Some(newDriver) if isSideOpen(d) => blocks(d.ordinal()) match { case Some((oldEnvironment, driver)) => if (newDriver != driver) { @@ -105,13 +106,13 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra node.disconnect(oldEnvironment.node) // Then rebuild - if we have something. - val environment = newDriver.createEnvironment(getWorld, blockPos, d) + val environment = newDriver.createEnvironment(getLevel, blockPos, d) if (environment != null) { blocks(d.ordinal()) = Some((environment, newDriver)) if (environment.canUpdate) { updatingBlocks += environment } - blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) + blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new CompoundNBT())) node.connect(environment.node) } } // else: the more things change, the more they stay the same. @@ -120,7 +121,7 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra return } // A challenger appears. Maybe. - val environment = newDriver.createEnvironment(getWorld, blockPos, d) + val environment = newDriver.createEnvironment(getLevel, blockPos, d) if (environment != null) { blocks(d.ordinal()) = Some((environment, newDriver)) if (environment.canUpdate) { @@ -128,10 +129,10 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra } blocksData(d.ordinal()) match { case Some(data) if data.name == environment.getClass.getName => - environment.load(data.data) + environment.loadData(data.data) case _ => } - blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) + blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new CompoundNBT())) node.connect(environment.node) } } @@ -139,7 +140,7 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra case Some((environment, driver)) => // We had something there, but it's gone now... node.disconnect(environment.node) - environment.save(blocksData(d.ordinal()).get.data) + environment.saveData(blocksData(d.ordinal()).get.data) Option(environment.node).foreach(_.remove()) blocks(d.ordinal()) = None updatingBlocks -= environment @@ -152,7 +153,7 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra def neighborChanged() { if (node != null && node.network != null) { - for (d <- EnumFacing.values) { + for (d <- Direction.values) { neighborChanged(d) } } @@ -176,9 +177,9 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra // ----------------------------------------------------------------------- // - override def getSizeInventory = 1 + override def getContainerSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack) = (slot, Option(Driver.driverFor(stack, getClass))) match { + override def canPlaceItem(slot: Int, stack: ItemStack) = (slot, Option(Driver.driverFor(stack, getClass))) match { case (0, Some(driver)) => driver.slot(stack) == Slot.Upgrade case _ => false } @@ -189,44 +190,44 @@ class Adapter extends traits.Environment with traits.ComponentInventory with tra private final val BlockNameTag = "name" private final val BlockDataTag = "data" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) - val blocksNbt = nbt.getTagList(BlocksTag, NBT.TAG_COMPOUND) - (0 until (blocksNbt.tagCount min blocksData.length)). - map(blocksNbt.getCompoundTagAt). + val blocksNbt = nbt.getList(BlocksTag, NBT.TAG_COMPOUND) + (0 until (blocksNbt.size min blocksData.length)). + map(blocksNbt.getCompound). zipWithIndex. foreach { case (blockNbt, i) => - if (blockNbt.hasKey(BlockNameTag) && blockNbt.hasKey(BlockDataTag)) { - blocksData(i) = Some(new BlockData(blockNbt.getString(BlockNameTag), blockNbt.getCompoundTag(BlockDataTag))) + if (blockNbt.contains(BlockNameTag) && blockNbt.contains(BlockDataTag)) { + blocksData(i) = Some(new BlockData(blockNbt.getString(BlockNameTag), blockNbt.getCompound(BlockDataTag))) } } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) - val blocksNbt = new NBTTagList() + val blocksNbt = new ListNBT() for (i <- blocks.indices) { - val blockNbt = new NBTTagCompound() + val blockNbt = new CompoundNBT() blocksData(i) match { case Some(data) => blocks(i) match { - case Some((environment, _)) => environment.save(data.data) + case Some((environment, _)) => environment.saveData(data.data) case _ => } - blockNbt.setString(BlockNameTag, data.name) - blockNbt.setTag(BlockDataTag, data.data) + blockNbt.putString(BlockNameTag, data.name) + blockNbt.put(BlockDataTag, data.data) case _ => } - blocksNbt.appendTag(blockNbt) + blocksNbt.add(blockNbt) } - nbt.setTag(BlocksTag, blocksNbt) + nbt.put(BlocksTag, blocksNbt) } // ----------------------------------------------------------------------- // - private class BlockData(val name: String, val data: NBTTagCompound) + private class BlockData(val name: String, val data: CompoundNBT) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala index c65c8a1649..c3db18f83c 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala @@ -18,14 +18,15 @@ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class Assembler extends traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { +class Assembler extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { val node = api.Network.newNode(this, Visibility.Network). withComponent("assembler"). withConnector(Settings.get.bufferConverter). @@ -48,15 +49,15 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = side != EnumFacing.UP + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = side != Direction.UP - override def sidedNode(side: EnumFacing) = if (side != EnumFacing.UP) node else null + override def sidedNode(side: Direction) = if (side != Direction.UP) node else null - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing) = canConnect(side) + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction) = canConnect(side) - override protected def connector(side: EnumFacing) = Option(if (side != EnumFacing.UP) node else null) + override protected def connector(side: Direction) = Option(if (side != Direction.UP) node else null) override def energyThroughput = Settings.get.assemblerRate @@ -68,7 +69,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits // ----------------------------------------------------------------------- // - def canAssemble = AssemblerTemplates.select(getStackInSlot(0)) match { + def canAssemble = AssemblerTemplates.select(getItem(0)) match { case Some(template) => !isAssembling && output.isEmpty && template.validate(this)._1 case _ => false } @@ -82,11 +83,11 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits // ----------------------------------------------------------------------- // def start(finishImmediately: Boolean = false): Boolean = this.synchronized { - AssemblerTemplates.select(getStackInSlot(0)) match { + AssemblerTemplates.select(getItem(0)) match { case Some(template) if !isAssembling && output.isEmpty && template.validate(this)._1 => - for (slot <- 0 until getSizeInventory) { - val stack = getStackInSlot(slot) - if (!stack.isEmpty && !isItemValidForSlot(slot, stack)) return false + for (slot <- 0 until getContainerSize) { + val stack = getItem(slot) + if (!stack.isEmpty && !canPlaceItem(slot, stack)) return false } val (stack, energy) = template.assemble(this) output = StackOption(stack) @@ -99,8 +100,8 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits requiredEnergy = totalRequiredEnergy ServerPacketSender.sendRobotAssembling(this, assembling = true) - for (slot <- 0 until getSizeInventory) updateItems(slot, ItemStack.EMPTY) - markDirty() + for (slot <- 0 until getContainerSize) updateItems(slot, ItemStack.EMPTY) + setChanged() true case _ => false @@ -112,7 +113,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits @Callback(doc = """function(): string, number or boolean -- The current state of the assembler, `busy' or `idle', followed by the progress or template validity, respectively.""") def status(context: Context, args: Arguments): Array[Object] = { if (isAssembling) result("busy", progress) - else AssemblerTemplates.select(getStackInSlot(0)) match { + else AssemblerTemplates.select(getItem(0)) match { case Some(template) if template.validate(this)._1 => result("idle", true) case _ => result("idle", false) } @@ -125,12 +126,12 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits override def updateEntity() { super.updateEntity() - if (!output.isEmpty && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (!output.isEmpty && getLevel.getGameTime % Settings.get.tickFrequency == 0) { val want = math.max(1, math.min(requiredEnergy, Settings.get.assemblerTickAmount * Settings.get.tickFrequency)) val have = want + (if (Settings.get.ignorePower) 0 else node.changeBuffer(-want)) requiredEnergy -= have if (requiredEnergy <= 0) { - setInventorySlotContents(0, output.get) + setItem(0, output.get) output = EmptyStack requiredEnergy = 0 } @@ -145,47 +146,47 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits private final val TotalTag = Settings.namespace + "total" private final val RemainingTag = Settings.namespace + "remaining" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(OutputTag)) { - output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTag))) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + if (nbt.contains(OutputTag)) { + output = StackOption(ItemStack.of(nbt.getCompound(OutputTag))) } - else if (nbt.hasKey(OutputTagCompat)) { - output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTagCompat))) + else if (nbt.contains(OutputTagCompat)) { + output = StackOption(ItemStack.of(nbt.getCompound(OutputTagCompat))) } totalRequiredEnergy = nbt.getDouble(TotalTag) requiredEnergy = nbt.getDouble(RemainingTag) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setNewCompoundTag(OutputTag, output.get.writeToNBT) - nbt.setDouble(TotalTag, totalRequiredEnergy) - nbt.setDouble(RemainingTag, requiredEnergy) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.setNewCompoundTag(OutputTag, output.get.save) + nbt.putDouble(TotalTag, totalRequiredEnergy) + nbt.putDouble(RemainingTag, requiredEnergy) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) requiredEnergy = nbt.getDouble(RemainingTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setDouble(RemainingTag, requiredEnergy) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putDouble(RemainingTag, requiredEnergy) } // ----------------------------------------------------------------------- // - override def getSizeInventory = 22 + override def getContainerSize = 22 - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack) = + override def canPlaceItem(slot: Int, stack: ItemStack) = if (slot == 0) { !isAssembling && AssemblerTemplates.select(stack).isDefined } - else AssemblerTemplates.select(getStackInSlot(0)) match { + else AssemblerTemplates.select(getItem(0)) match { case Some(template) => val tplSlot = if ((1 until 4) contains slot) template.containerSlots(slot - 1) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala index 3bbde82150..e2937cad71 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala @@ -5,19 +5,20 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.common import li.cil.oc.Constants import li.cil.oc.util.Color -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.DyeColor import li.cil.oc.util.ItemColorizer import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.tileentity.TileEntity -class Cable extends traits.Environment with traits.NotAnalyzable with traits.ImmibisMicroblock with traits.Colored { +class Cable extends TileEntity(null) with traits.Environment with traits.NotAnalyzable with traits.ImmibisMicroblock with traits.Colored { val node = api.Network.newNode(this, Visibility.None).create() - setColor(Color.rgbValues(EnumDyeColor.SILVER)) + setColor(Color.rgbValues(DyeColor.LIGHT_GRAY)) def createItemStack() = { val stack = api.Items.get(Constants.BlockName.Cable).createItemStack(1) - if (getColor != Color.rgbValues(EnumDyeColor.SILVER)) { + if (getColor != Color.rgbValues(DyeColor.LIGHT_GRAY)) { ItemColorizer.setColor(stack, getColor) } stack @@ -35,10 +36,10 @@ class Cable extends traits.Environment with traits.NotAnalyzable with traits.Imm override protected def onColorChanged() { super.onColorChanged() - if (getWorld != null && isServer) { + if (getLevel != null && isServer) { api.Network.joinOrCreateNetwork(this) } } - override def getRenderBoundingBox = common.block.Cable.bounds(getWorld, getPos).offset(x, y, z) + override def getRenderBoundingBox = common.block.Cable.bounds(getLevel, getBlockPos).move(x, y, z) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala index 31da0e1c21..13182e4705 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala @@ -10,11 +10,13 @@ import li.cil.oc.api import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraft.util.math.BlockPos import scala.collection.convert.WrapAsJava._ -class Capacitor extends traits.Environment with DeviceInfo { +class Capacitor extends TileEntity(null) with traits.Environment with DeviceInfo { // Start with maximum theoretical capacity, gets reduced after validation. // This is done so that we don't lose energy while loading. val node = api.Network.newNode(this, Visibility.Network). @@ -37,7 +39,7 @@ class Capacitor extends traits.Environment with DeviceInfo { super.dispose() if (isServer) { indirectNeighbors.map(coordinate => { - if (getWorld.isBlockLoaded(coordinate)) Option(getWorld.getTileEntity(coordinate)) + if (getLevel.isLoaded(coordinate)) Option(getLevel.getBlockEntity(coordinate)) else None }).collect { case Some(capacitor: Capacitor) => capacitor.recomputeCapacity() @@ -57,14 +59,14 @@ class Capacitor extends traits.Environment with DeviceInfo { def recomputeCapacity(updateSecondGradeNeighbors: Boolean = false) { node.setLocalBufferSize( Settings.get.bufferCapacitor + - Settings.get.bufferCapacitorAdjacencyBonus * EnumFacing.values.count(side => { - val blockPos = getPos.offset(side) - getWorld.isBlockLoaded(blockPos) && (getWorld.getTileEntity(blockPos) match { + Settings.get.bufferCapacitorAdjacencyBonus * Direction.values.count(side => { + val blockPos = getBlockPos.relative(side) + getLevel.isLoaded(blockPos) && (getLevel.getBlockEntity(blockPos) match { case capacitor: Capacitor => true case _ => false }) }) + - Settings.get.bufferCapacitorAdjacencyBonus / 2 * indirectNeighbors.count(blockPos => getWorld.isBlockLoaded(blockPos) && (getWorld.getTileEntity(blockPos) match { + Settings.get.bufferCapacitorAdjacencyBonus / 2 * indirectNeighbors.count(blockPos => getLevel.isLoaded(blockPos) && (getLevel.getBlockEntity(blockPos) match { case capacitor: Capacitor => if (updateSecondGradeNeighbors) { capacitor.recomputeCapacity() @@ -74,7 +76,7 @@ class Capacitor extends traits.Environment with DeviceInfo { }))) } - private def indirectNeighbors = EnumFacing.values.map(getPos.offset(_, 2)) + private def indirectNeighbors = Direction.values.map(getBlockPos.relative(_, 2): BlockPos) protected def maxCapacity = Settings.get.bufferCapacitor + Settings.get.bufferCapacitorAdjacencyBonus * 9 } diff --git a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala index b355dc9d92..fade14c91e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala @@ -6,16 +6,15 @@ import li.cil.oc.Constants import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass import li.cil.oc.Settings -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.passive.{EntityOcelot, EntitySheep} +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.passive.{OcelotEntity, SheepEntity} import net.minecraft.util.DamageSource -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ -import li.cil.oc.common.tileentity.traits.Tickable -class CarpetedCapacitor extends Capacitor with Tickable { +class CarpetedCapacitor extends Capacitor with traits.Tickable { private final lazy val deviceInfo = Map( DeviceAttribute.Class -> DeviceClass.Power, DeviceAttribute.Description -> "Battery", @@ -26,38 +25,38 @@ class CarpetedCapacitor extends Capacitor with Tickable { override def getDeviceInfo: util.Map[String, String] = deviceInfo - private def _world: net.minecraft.world.World = getWorld + private def _world: net.minecraft.world.World = getLevel private val rng = scala.util.Random private val chance: Double = Settings.get.carpetDamageChance private var nextChanceTime: Long = 0 - private def energyFromGroup(entities: Set[EntityLivingBase], power: Double): Double = { + private def energyFromGroup(entities: Set[LivingEntity], power: Double): Double = { if (entities.size < 2) return 0 def tryDamageOne(): Unit = { for (ent <- entities) { if (rng.nextDouble() < chance) { - ent.attackEntityFrom(DamageSource.GENERIC, 1) - ent.setRevengeTarget(ent) // panic - ent.knockBack(ent, 0, .25, 0) + ent.hurt(DamageSource.GENERIC, 1) + ent.setLastHurtByMob(ent) // panic + ent.knockback(0, .25, 0) // wait a minute before the next possible shock - nextChanceTime = _world.getTotalWorldTime + (20 * 60) + nextChanceTime = _world.getGameTime + (20 * 60) return } } } - if (chance > 0 && nextChanceTime < _world.getTotalWorldTime) { + if (chance > 0 && nextChanceTime < _world.getGameTime) { tryDamageOne() } power } override def updateEntity() { - if (node != null && (_world.getTotalWorldTime + hashCode) % 20 == 0) { - val entities = _world.getEntitiesWithinAABB(classOf[EntityLivingBase], capacitorPowerBounds) - .filter(entity => entity.isEntityAlive) + if (node != null && (_world.getGameTime + hashCode) % 20 == 0) { + val entities = _world.getEntitiesOfClass(classOf[LivingEntity], capacitorPowerBounds) + .filter(entity => entity.isAlive) .toSet - val sheepPower = energyFromGroup(entities.filter(_.isInstanceOf[EntitySheep]), Settings.get.sheepPower) - val ocelotPower = energyFromGroup(entities.filter(_.isInstanceOf[EntityOcelot]), Settings.get.ocelotPower) + val sheepPower = energyFromGroup(entities.filter(_.isInstanceOf[SheepEntity]), Settings.get.sheepPower) + val ocelotPower = energyFromGroup(entities.filter(_.isInstanceOf[OcelotEntity]), Settings.get.ocelotPower) val totalPower = sheepPower + ocelotPower if (totalPower > 0) { node.changeBuffer(totalPower) @@ -65,5 +64,5 @@ class CarpetedCapacitor extends Capacitor with Tickable { } } - private def capacitorPowerBounds = position.offset(EnumFacing.UP).bounds + private def capacitorPowerBounds = position.offset(Direction.UP).bounds } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala index 20d423a0c5..aefba594aa 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala @@ -16,16 +16,17 @@ import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.block.property.PropertyRunning import li.cil.oc.util.Color -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { +class Case(var tier: Int) extends TileEntity(null) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { def this() = { this(0) // If no tier was defined when constructing this case, then we don't yet know the inventory size @@ -44,17 +45,17 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with DeviceAttribute.Description -> "Computer", DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor, DeviceAttribute.Product -> "Blocker", - DeviceAttribute.Capacity -> getSizeInventory.toString + DeviceAttribute.Capacity -> getContainerSize.toString ) override def getDeviceInfo: util.Map[String, String] = deviceInfo // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing) = side != facing + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction) = side != facing - override protected def connector(side: EnumFacing) = Option(if (side != facing && machine != null) machine.node.asInstanceOf[Connector] else null) + override protected def connector(side: Direction) = Option(if (side != facing && machine != null) machine.node.asInstanceOf[Connector] else null) override def energyThroughput = Settings.get.caseRate(tier) @@ -67,7 +68,7 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with // ----------------------------------------------------------------------- // override def updateEntity() { - if (isServer && isCreative && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (isServer && isCreative && getLevel.getGameTime % Settings.get.tickFrequency == 0) { // Creative case, make it generate power. node.asInstanceOf[Connector].changeBuffer(Double.PositiveInfinity) } @@ -78,12 +79,12 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with override protected def onRunningChanged(): Unit = { super.onRunningChanged() - getBlockType match { + getBlockState.getBlock match { case block: common.block.Case => { - val state = getWorld.getBlockState(getPos) + val state = getLevel.getBlockState(getBlockPos) // race condition that the world no longer has this block at the position (e.g. it was broken) if (block == state.getBlock) { - getWorld.setBlockState(getPos, state.withProperty(PropertyRunning.Running, Boolean.box(isRunning))) + getLevel.setBlockAndUpdate(getBlockPos, state.setValue(PropertyRunning.Running, Boolean.box(isRunning))) } } case _ => @@ -94,16 +95,16 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with private final val TierTag = Settings.namespace + "tier" - override def readFromNBTForServer(nbt: NBTTagCompound) { + override def loadForServer(nbt: CompoundNBT) { tier = nbt.getByte(TierTag) max 0 min 3 setColor(Color.rgbValues(Color.byTier(tier))) - super.readFromNBTForServer(nbt) + super.loadForServer(nbt) isSizeInventoryReady = true } - override def writeToNBTForServer(nbt: NBTTagCompound) { - nbt.setByte(TierTag, tier.toByte) - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + nbt.putByte(TierTag, tier.toByte) + super.saveForServer(nbt) } // ----------------------------------------------------------------------- // @@ -130,12 +131,12 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with } } - override def getSizeInventory = if (tier < 0 || tier >= InventorySlots.computer.length) 0 else InventorySlots.computer(tier).length + override def getContainerSize = if (tier < 0 || tier >= InventorySlots.computer.length) 0 else InventorySlots.computer(tier).length - override def isUsableByPlayer(player: EntityPlayer) = - super.isUsableByPlayer(player) && (!isCreative || player.capabilities.isCreativeMode) + override def stillValid(player: PlayerEntity) = + super.stillValid(player) && (!isCreative || player.isCreative) - override def isItemValidForSlot(slot: Int, stack: ItemStack) = + override def canPlaceItem(slot: Int, stack: ItemStack) = Option(Driver.driverFor(stack, getClass)).fold(false)(driver => { val provided = InventorySlots.computer(tier)(slot) driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index 946e5d0b9d..634cbc1c4d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -19,20 +19,22 @@ import li.cil.oc.integration.util.ItemCharge import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumParticleTypes -import net.minecraft.util.math.Vec3d -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.ParticleTypes +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraft.util.Util +import net.minecraft.util.math.vector.Vector3d +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class Charger extends traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { +class Charger extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -57,10 +59,10 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing): Boolean = side != facing + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction): Boolean = side != facing - override protected def connector(side: EnumFacing) = Option(if (side != facing) node else null) + override protected def connector(side: Direction) = Option(if (side != facing) node else null) override def energyThroughput: Double = Settings.get.chargerRate @@ -73,8 +75,8 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R else util.EnumSet.noneOf(classOf[api.util.StateAware.State]) } - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Null = { - player.sendMessage(Localization.Analyzer.ChargerSpeed(chargeSpeed)) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Null = { + player.sendMessage(Localization.Analyzer.ChargerSpeed(chargeSpeed), Util.NIL_UUID) null } @@ -92,11 +94,11 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R super.updateEntity() // Offset by hashcode to avoid all chargers ticking at the same time. - if ((getWorld.getWorldInfo.getWorldTotalTime + math.abs(hashCode())) % 20 == 0) { + if ((getLevel.getLevelData.getGameTime + math.abs(hashCode())) % 20 == 0) { updateConnectors() } - if (isServer && getWorld.getWorldInfo.getWorldTotalTime % Settings.get.tickFrequency == 0) { + if (isServer && getLevel.getLevelData.getGameTime % Settings.get.tickFrequency == 0) { var canCharge = Settings.get.ignorePower // Charging of external devices. @@ -117,7 +119,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R val charge = Settings.get.chargeRateTablet * chargeSpeed * Settings.get.tickFrequency canCharge ||= charge > 0 && node.globalBuffer >= charge * 0.5 if (canCharge) { - (0 until getSizeInventory).map(getStackInSlot).foreach(chargeStack(_, charge)) + (0 until getContainerSize).map(getItem).foreach(chargeStack(_, charge)) } } @@ -140,15 +142,15 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R } } - if (isClient && chargeSpeed > 0 && hasPower && getWorld.getWorldInfo.getWorldTotalTime % 10 == 0) { + if (isClient && chargeSpeed > 0 && hasPower && getLevel.getLevelData.getGameTime % 10 == 0) { connectors.foreach(connector => { val position = connector.pos - val theta = getWorld.rand.nextDouble * Math.PI - val phi = getWorld.rand.nextDouble * Math.PI * 2 + val theta = getLevel.random.nextDouble * Math.PI + val phi = getLevel.random.nextDouble * Math.PI * 2 val dx = 0.45 * Math.sin(theta) * Math.cos(phi) val dy = 0.45 * Math.sin(theta) * Math.sin(phi) val dz = 0.45 * Math.cos(theta) - getWorld.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, position.x + dx, position.y + dz, position.z + dy, 0, 0, 0) + getLevel.addParticle(ParticleTypes.HAPPY_VILLAGER, position.x + dx, position.y + dz, position.z + dy, 0, 0, 0) }) } } @@ -169,40 +171,40 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R private final val InvertSignalTag = Settings.namespace + "invertSignal" private final val InvertSignalTagCompat = "invertSignal" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(ChargeSpeedTagCompat)) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + if (nbt.contains(ChargeSpeedTagCompat)) chargeSpeed = nbt.getDouble(ChargeSpeedTagCompat) max 0 min 1 else chargeSpeed = nbt.getDouble(ChargeSpeedTag) max 0 min 1 - if (nbt.hasKey(HasPowerTagCompat)) + if (nbt.contains(HasPowerTagCompat)) hasPower = nbt.getBoolean(HasPowerTagCompat) else hasPower = nbt.getBoolean(HasPowerTag) - if (nbt.hasKey(InvertSignalTagCompat)) + if (nbt.contains(InvertSignalTagCompat)) invertSignal = nbt.getBoolean(InvertSignalTagCompat) else invertSignal = nbt.getBoolean(InvertSignalTag) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setDouble(ChargeSpeedTag, chargeSpeed) - nbt.setBoolean(HasPowerTag, hasPower) - nbt.setBoolean(InvertSignalTag, invertSignal) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.putDouble(ChargeSpeedTag, chargeSpeed) + nbt.putBoolean(HasPowerTag, hasPower) + nbt.putBoolean(InvertSignalTag, invertSignal) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) chargeSpeed = nbt.getDouble(ChargeSpeedTag) hasPower = nbt.getBoolean(HasPowerTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setDouble(ChargeSpeedTag, chargeSpeed) - nbt.setBoolean(HasPowerTag, hasPower) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putDouble(ChargeSpeedTag, chargeSpeed) + nbt.putBoolean(HasPowerTag, hasPower) } // ----------------------------------------------------------------------- // @@ -213,16 +215,16 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R case _ => false }) - override def getSizeInventory = 1 + override def getContainerSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { case (0, Some(driver)) if driver.slot(stack) == Slot.Tablet => true case _ => ItemCharge.canCharge(stack) } // ----------------------------------------------------------------------- // - override def updateRedstoneInput(side: EnumFacing) { + override def updateRedstoneInput(side: Direction) { super.updateRedstoneInput(side) val signal = getInput.max min 15 @@ -239,20 +241,20 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R } def updateConnectors() { - val robots = EnumFacing.values.map(side => { + val robots = Direction.values.map(side => { val blockPos = BlockPosition(this).offset(side) - if (getWorld.blockExists(blockPos)) Option(getWorld.getTileEntity(blockPos)) + if (getLevel.blockExists(blockPos)) Option(getLevel.getBlockEntity(blockPos)) else None }).collect { case Some(t: RobotProxy) => new RobotChargeable(t.robot) } - val bounds = BlockPosition(this).bounds.grow(1, 1, 1) - val drones = getWorld.getEntitiesWithinAABB(classOf[Drone], bounds).collect { + val bounds = BlockPosition(this).bounds.inflate(1, 1, 1) + val drones = getLevel.getEntitiesOfClass(classOf[Drone], bounds).collect { case drone: Drone => new DroneChargeable(drone) } - val players = getWorld.getEntitiesWithinAABB(classOf[EntityPlayer], bounds).collect { - case player: EntityPlayer => player + val players = getLevel.getEntitiesOfClass(classOf[PlayerEntity], bounds).collect { + case player: PlayerEntity => player } val chargeablePlayers = players.collect { @@ -265,13 +267,13 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R if (connectors.size != newConnectors.length || (connectors.nonEmpty && (connectors -- newConnectors).nonEmpty)) { connectors.clear() connectors ++= newConnectors - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, false) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) } // scan players for chargeable equipment equipment.clear() players.foreach { - player => player.inventory.mainInventory.foreach { + player => player.inventory.items.foreach { stack: ItemStack => if (Option(Driver.driverFor(stack, getClass)) match { case Some(driver) if driver.slot(stack) == Slot.Tablet => true @@ -284,7 +286,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R } trait Chargeable { - def pos: Vec3d + def pos: Vector3d def changeBuffer(delta: Double): Double } @@ -299,7 +301,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R } class RobotChargeable(val robot: Robot) extends ConnectorChargeable(robot.node.asInstanceOf[Connector]) { - override def pos: Vec3d = BlockPosition(robot).toVec3 + override def pos: Vector3d = BlockPosition(robot).toVec3 override def equals(obj: scala.Any): Boolean = obj match { case chargeable: RobotChargeable => chargeable.robot == robot @@ -310,7 +312,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R } class DroneChargeable(val drone: Drone) extends ConnectorChargeable(drone.components.node.asInstanceOf[Connector]) { - override def pos: Vec3d = new Vec3d(drone.posX, drone.posY, drone.posZ) + override def pos: Vector3d = new Vector3d(drone.getX, drone.getY, drone.getZ) override def equals(obj: scala.Any): Boolean = obj match { case chargeable: DroneChargeable => chargeable.drone == drone @@ -320,8 +322,8 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R override def hashCode(): Int = drone.hashCode() } - class PlayerChargeable(val player: EntityPlayer) extends Chargeable { - override def pos: Vec3d = new Vec3d(player.posX, player.posY, player.posZ) + class PlayerChargeable(val player: PlayerEntity) extends Chargeable { + override def pos: Vector3d = new Vector3d(player.getX, player.getY, player.getZ) override def changeBuffer(delta: Double): Double = { api.Nanomachines.getController(player) match { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index 9d02fb9c9b..28be470007 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -17,19 +17,20 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ItemUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -class Disassembler extends traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo { +class Disassembler extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo { val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -40,7 +41,7 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra var totalRequiredEnergy = 0.0 - override def getInventoryStackLimit: Int = 1 + override def getMaxStackSize: Int = 1 var buffer = 0.0 @@ -51,7 +52,7 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra private def setActive(value: Boolean) = if (value != isActive) { isActive = value ServerPacketSender.sendDisassemblerActive(this, isActive) - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, true) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) } private final lazy val deviceInfo = Map( @@ -65,10 +66,10 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing): Boolean = side != EnumFacing.UP + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction): Boolean = side != Direction.UP - override protected def connector(side: EnumFacing) = Option(if (side != EnumFacing.UP) node else null) + override protected def connector(side: Direction) = Option(if (side != Direction.UP) node else null) override def energyThroughput: Double = Settings.get.disassemblerRate @@ -82,10 +83,10 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra override def updateEntity() { super.updateEntity() - if (isServer && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (isServer && getLevel.getGameTime % Settings.get.tickFrequency == 0) { if (queue.isEmpty) { - val instant = disassembleNextInstantly // Is reset via decrStackSize - disassemble(decrStackSize(0, 1), instant) + val instant = disassembleNextInstantly // Is reset via removeItem + disassemble(removeItem(0, 1), instant) setActive(queue.nonEmpty) } else { @@ -100,7 +101,7 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra while (buffer >= Settings.get.disassemblerItemCost && queue.nonEmpty) { buffer -= Settings.get.disassemblerItemCost val stack = queue.remove(0) - if (disassembleNextInstantly || getWorld.rand.nextDouble >= Settings.get.disassemblerBreakChance) { + if (disassembleNextInstantly || getLevel.random.nextDouble >= Settings.get.disassemblerBreakChance) { drop(stack) } } @@ -111,8 +112,8 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra def disassemble(stack: ItemStack, instant: Boolean = false) { // Validate the item, never trust Minecraft / other Mods on anything! - if (isItemValidForSlot(0, stack)) { - val ingredients = ItemUtils.getIngredients(stack) + if (canPlaceItem(0, stack)) { + val ingredients = ItemUtils.getIngredients(getLevel.getRecipeManager, stack) DisassemblerTemplates.select(stack) match { case Some(template) => val (stacks, drops) = template.disassemble(stack, ingredients) @@ -132,11 +133,11 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra private def drop(stack: ItemStack) { if (!stack.isEmpty) { - for (side <- EnumFacing.values if stack.getCount > 0) { + for (side <- Direction.values if stack.getCount > 0) { InventoryUtils.insertIntoInventoryAt(stack, BlockPosition(this).offset(side), Some(side.getOpposite)) } if (stack.getCount > 0) { - spawnStackInWorld(stack, Option(EnumFacing.UP)) + spawnStackInWorld(stack, Option(Direction.UP)) } } } @@ -148,55 +149,55 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra private final val TotalTag = Settings.namespace + "total" private final val IsActiveTag = Settings.namespace + "isActive" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) queue.clear() - queue ++= nbt.getTagList(QueueTag, NBT.TAG_COMPOUND). - map((tag: NBTTagCompound) => new ItemStack(tag)) + queue ++= nbt.getList(QueueTag, NBT.TAG_COMPOUND). + map((tag: CompoundNBT) => ItemStack.of(tag)) buffer = nbt.getDouble(BufferTag) totalRequiredEnergy = nbt.getDouble(TotalTag) isActive = queue.nonEmpty } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) nbt.setNewTagList(QueueTag, queue) - nbt.setDouble(BufferTag, buffer) - nbt.setDouble(TotalTag, totalRequiredEnergy) + nbt.putDouble(BufferTag, buffer) + nbt.putDouble(TotalTag, totalRequiredEnergy) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) isActive = nbt.getBoolean(IsActiveTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setBoolean(IsActiveTag, isActive) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putBoolean(IsActiveTag, isActive) } // ----------------------------------------------------------------------- // - override def getSizeInventory = 1 + override def getContainerSize = 1 - override def isItemValidForSlot(i: Int, stack: ItemStack): Boolean = + override def canPlaceItem(i: Int, stack: ItemStack): Boolean = allowDisassembling(stack) && - (((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && ItemUtils.getIngredients(stack).nonEmpty) || + (((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && ItemUtils.getIngredients(getLevel.getRecipeManager, stack).nonEmpty) || DisassemblerTemplates.select(stack).isDefined) - private def allowDisassembling(stack: ItemStack) = !stack.isEmpty && (!stack.hasTagCompound || !stack.getTagCompound.getBoolean(Settings.namespace + "undisassemblable")) + private def allowDisassembling(stack: ItemStack) = !stack.isEmpty && (!stack.hasTag || !stack.getTag.getBoolean(Settings.namespace + "undisassemblable")) - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { - super.setInventorySlotContents(slot, stack) - if (!getWorld.isRemote) { + override def setItem(slot: Int, stack: ItemStack): Unit = { + super.setItem(slot, stack) + if (!getLevel.isClientSide) { disassembleNextInstantly = false } } - override def onSetInventorySlotContents(player: EntityPlayer, slot: Int, stack: ItemStack): Unit = { - if (!getWorld.isRemote) { - disassembleNextInstantly = !stack.isEmpty && slot == 0 && player.capabilities.isCreativeMode + override def onSetInventorySlotContents(player: PlayerEntity, slot: Int, stack: ItemStack): Unit = { + if (!getLevel.isClientSide) { + disassembleNextInstantly = !stack.isEmpty && slot == 0 && player.isCreative } } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala index d206db2260..fdc388e7e9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala @@ -21,16 +21,17 @@ import li.cil.oc.common.Sound import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class DiskDrive extends traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { +class DiskDrive extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { // Used on client side to check whether to render disk activity indicators. var lastAccess = 0L @@ -63,14 +64,14 @@ class DiskDrive extends traits.Environment with traits.ComponentInventory with t @Callback(doc = "function([velocity:number]):boolean -- Eject the currently present medium from the drive.") def eject(context: Context, args: Arguments): Array[AnyRef] = { val velocity = args.optDouble(0, 0) max 0 min 1 - val ejected = decrStackSize(0, 1) + val ejected = removeItem(0, 1) if (!ejected.isEmpty) { val entity = InventoryUtils.spawnStackInWorld(position, ejected, Option(facing)) if (entity != null) { - val vx = facing.getFrontOffsetX * velocity - val vy = facing.getFrontOffsetY * velocity - val vz = facing.getFrontOffsetZ * velocity - entity.addVelocity(vx, vy, vz) + val vx = facing.getStepX * velocity + val vy = facing.getStepY * velocity + val vz = facing.getStepZ * velocity + entity.push(vx, vy, vz) } result(true) } @@ -88,14 +89,14 @@ class DiskDrive extends traits.Environment with traits.ComponentInventory with t // ----------------------------------------------------------------------- // // Analyzable - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = filesystemNode.fold(null: Array[Node])(Array(_)) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = filesystemNode.fold(null: Array[Node])(Array(_)) // ----------------------------------------------------------------------- // // IInventory - override def getSizeInventory = 1 + override def getContainerSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { case (0, Some(driver)) => driver.slot(stack) == Slot.Floppy case _ => false } @@ -130,16 +131,16 @@ class DiskDrive extends traits.Environment with traits.ComponentInventory with t private final val DiskTag = Settings.namespace + "disk" - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - if (nbt.hasKey(DiskTag)) { - setInventorySlotContents(0, new ItemStack(nbt.getCompoundTag(DiskTag))) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + if (nbt.contains(DiskTag)) { + setItem(0, ItemStack.of(nbt.getCompound(DiskTag))) } } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - if (!items(0).isEmpty) nbt.setNewCompoundTag(DiskTag, items(0).writeToNBT) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + if (!items(0).isEmpty) nbt.setNewCompoundTag(DiskTag, items(0).save) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala b/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala index f5e8e9bb89..ea5f551cd5 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala @@ -1,20 +1,21 @@ package li.cil.oc.common.tileentity import li.cil.oc.server.component -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity -class Geolyzer extends traits.Environment { +class Geolyzer extends TileEntity(null) with traits.Environment { val geolyzer = new component.Geolyzer(this) def node = geolyzer.node - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - geolyzer.load(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + geolyzer.loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - geolyzer.save(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + geolyzer.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala index 74ef1c8aa3..63164a7164 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala @@ -13,18 +13,19 @@ import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network._ import li.cil.oc.common.SaveHandler import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB -import net.minecraft.util.math.Vec3d -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.util.math.vector.Vector3d +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.mutable -class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment with Analyzable with traits.RotatableTile with traits.Tickable with DeviceInfo { +class Hologram(var tier: Int) extends TileEntity(null) with traits.Environment with SidedEnvironment with Analyzable with traits.RotatableTile with traits.Tickable with DeviceInfo { def this() = this(0) val node = api.Network.newNode(this, Visibility.Network). @@ -57,7 +58,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w var scale = 1.0 // Projection Y position offset - consider adding X,Z later perhaps - var translation = new Vec3d(0, 0, 0) + var translation = new Vector3d(0, 0, 0) // Relative number of lit columns (for energy cost). var litRatio = -1.0 @@ -130,13 +131,13 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = toLocal(side) == EnumFacing.DOWN + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = toLocal(side) == Direction.DOWN - override def sidedNode(side: EnumFacing) = if (toLocal(side) == EnumFacing.DOWN) node else null + override def sidedNode(side: Direction) = if (toLocal(side) == Direction.DOWN) node else null // Override automatic analyzer implementation for sided environments. - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(node) // ----------------------------------------------------------------------- // @@ -289,7 +290,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w val ty = math.max(0, math.min(maxTranslation * 2, args.checkDouble(1))) val tz = math.max(-maxTranslation, math.min(maxTranslation, args.checkDouble(2))) - translation = new Vec3d(tx, ty, tz) + translation = new Vector3d(tx, ty, tz) ServerPacketSender.sendHologramOffset(this) null @@ -410,7 +411,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w ServerPacketSender.sendHologramValues(this) resetDirtyFlag() } - if (getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (getLevel.getGameTime % Settings.get.tickFrequency == 0) { if (litRatio < 0) this.synchronized { litRatio = 0 for (i <- volume.indices) { @@ -431,9 +432,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w // ----------------------------------------------------------------------- // - override def shouldRenderInPass(pass: Int) = pass == 1 - - override def getMaxRenderDistanceSquared = scale / Settings.get.hologramMaxScaleByTier.max * Settings.get.hologramRenderDistance * Settings.get.hologramRenderDistance + override def getViewDistance = scale / Settings.get.hologramMaxScaleByTier.max * Settings.get.hologramRenderDistance def getFadeStartDistanceSquared = scale / Settings.get.hologramMaxScaleByTier.max * Settings.get.hologramFadeStartDistance * Settings.get.hologramFadeStartDistance @@ -476,9 +475,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w private final val RotationSpeedZTag = Settings.namespace + "rotationSpeedZ" private final val HasPowerTag = Settings.namespace + "hasPower" - override def readFromNBTForServer(nbt: NBTTagCompound) { + override def loadForServer(nbt: CompoundNBT) { tier = nbt.getByte(TierTag) max 0 min 1 - super.readFromNBTForServer(nbt) + super.loadForServer(nbt) val tag = SaveHandler.loadNBT(nbt, dataPath) tag.getIntArray(VolumeTag).copyToArray(volume) tag.getIntArray(ColorsTag).map(convertColor).copyToArray(colors) @@ -486,7 +485,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w val tx = nbt.getDouble(OffsetXTag) val ty = nbt.getDouble(OffsetYTag) val tz = nbt.getDouble(OffsetZTag) - translation = new Vec3d(tx, ty, tz) + translation = new Vector3d(tx, ty, tz) rotationAngle = nbt.getFloat(RotationAngleTag) rotationX = nbt.getFloat(RotationXTag) rotationY = nbt.getFloat(RotationYTag) @@ -497,30 +496,30 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w rotationSpeedZ = nbt.getFloat(RotationSpeedZTag) } - override def writeToNBTForServer(nbt: NBTTagCompound) = this.synchronized { - nbt.setByte(TierTag, tier.toByte) - super.writeToNBTForServer(nbt) - SaveHandler.scheduleSave(getWorld, x, z, nbt, dataPath, tag => { - tag.setIntArray(VolumeTag, volume) - tag.setIntArray(ColorsTag, colors.map(convertColor)) + override def saveForServer(nbt: CompoundNBT) = this.synchronized { + nbt.putByte(TierTag, tier.toByte) + super.saveForServer(nbt) + SaveHandler.scheduleSave(getLevel, x, z, nbt, dataPath, tag => { + tag.putIntArray(VolumeTag, volume) + tag.putIntArray(ColorsTag, colors.map(convertColor)) }) - nbt.setDouble(ScaleTag, scale) - nbt.setDouble(OffsetXTag, translation.x) - nbt.setDouble(OffsetYTag, translation.y) - nbt.setDouble(OffsetZTag, translation.z) - nbt.setFloat(RotationAngleTag, rotationAngle) - nbt.setFloat(RotationXTag, rotationX) - nbt.setFloat(RotationYTag, rotationY) - nbt.setFloat(RotationZTag, rotationZ) - nbt.setFloat(RotationSpeedTag, rotationSpeed) - nbt.setFloat(RotationSpeedXTag, rotationSpeedX) - nbt.setFloat(RotationSpeedYTag, rotationSpeedY) - nbt.setFloat(RotationSpeedZTag, rotationSpeedZ) + nbt.putDouble(ScaleTag, scale) + nbt.putDouble(OffsetXTag, translation.x) + nbt.putDouble(OffsetYTag, translation.y) + nbt.putDouble(OffsetZTag, translation.z) + nbt.putFloat(RotationAngleTag, rotationAngle) + nbt.putFloat(RotationXTag, rotationX) + nbt.putFloat(RotationYTag, rotationY) + nbt.putFloat(RotationZTag, rotationZ) + nbt.putFloat(RotationSpeedTag, rotationSpeed) + nbt.putFloat(RotationSpeedXTag, rotationSpeedX) + nbt.putFloat(RotationSpeedYTag, rotationSpeedY) + nbt.putFloat(RotationSpeedZTag, rotationSpeedZ) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) nbt.getIntArray(VolumeTag).copyToArray(volume) nbt.getIntArray(ColorsTag).copyToArray(colors) scale = nbt.getDouble(ScaleTag) @@ -528,7 +527,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w val tx = nbt.getDouble(OffsetXTag) val ty = nbt.getDouble(OffsetYTag) val tz = nbt.getDouble(OffsetZTag) - translation = new Vec3d(tx, ty, tz) + translation = new Vector3d(tx, ty, tz) rotationAngle = nbt.getFloat(RotationAngleTag) rotationX = nbt.getFloat(RotationXTag) rotationY = nbt.getFloat(RotationYTag) @@ -539,22 +538,22 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w rotationSpeedZ = nbt.getFloat(RotationSpeedZTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setIntArray(VolumeTag, volume) - nbt.setIntArray(ColorsTag, colors) - nbt.setDouble(ScaleTag, scale) - nbt.setBoolean(HasPowerTag, hasPower) - nbt.setDouble(OffsetXTag, translation.x) - nbt.setDouble(OffsetYTag, translation.y) - nbt.setDouble(OffsetZTag, translation.z) - nbt.setFloat(RotationAngleTag, rotationAngle) - nbt.setFloat(RotationXTag, rotationX) - nbt.setFloat(RotationYTag, rotationY) - nbt.setFloat(RotationZTag, rotationZ) - nbt.setFloat(RotationSpeedTag, rotationSpeed) - nbt.setFloat(RotationSpeedXTag, rotationSpeedX) - nbt.setFloat(RotationSpeedYTag, rotationSpeedY) - nbt.setFloat(RotationSpeedZTag, rotationSpeedZ) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putIntArray(VolumeTag, volume) + nbt.putIntArray(ColorsTag, colors) + nbt.putDouble(ScaleTag, scale) + nbt.putBoolean(HasPowerTag, hasPower) + nbt.putDouble(OffsetXTag, translation.x) + nbt.putDouble(OffsetYTag, translation.y) + nbt.putDouble(OffsetZTag, translation.z) + nbt.putFloat(RotationAngleTag, rotationAngle) + nbt.putFloat(RotationXTag, rotationX) + nbt.putFloat(RotationYTag, rotationY) + nbt.putFloat(RotationZTag, rotationZ) + nbt.putFloat(RotationSpeedTag, rotationSpeed) + nbt.putFloat(RotationSpeedXTag, rotationSpeedX) + nbt.putFloat(RotationSpeedYTag, rotationSpeedY) + nbt.putFloat(RotationSpeedZTag, rotationSpeedZ) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala index 1ffbfe301a..80c327e020 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala @@ -6,14 +6,15 @@ import li.cil.oc.api import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Keyboard extends traits.Environment with traits.Rotatable with traits.ImmibisMicroblock with SidedEnvironment with Analyzable { - override def validFacings = EnumFacing.values +class Keyboard extends TileEntity(null) with traits.Environment with traits.Rotatable with traits.ImmibisMicroblock with SidedEnvironment with Analyzable { + override def validFacings = Direction.values val keyboard = { val keyboardItem = api.Items.get(Constants.BlockName.Keyboard).createItemStack(1) @@ -22,18 +23,18 @@ class Keyboard extends traits.Environment with traits.Rotatable with traits.Immi override def node = keyboard.node - def hasNodeOnSide(side: EnumFacing) : Boolean = + def hasNodeOnSide(side: Direction) : Boolean = side != facing && (isOnWall || side.getOpposite != forward) // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = hasNodeOnSide(side) + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = hasNodeOnSide(side) - override def sidedNode(side: EnumFacing) = if (hasNodeOnSide(side)) node else null + override def sidedNode(side: Direction) = if (hasNodeOnSide(side)) node else null // Override automatic analyzer implementation for sided environments. - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(node) // ----------------------------------------------------------------------- // @@ -41,23 +42,23 @@ class Keyboard extends traits.Environment with traits.Rotatable with traits.Immi private final val KeyboardTag = Settings.namespace + "keyboard" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) if (isServer) { - keyboard.load(nbt.getCompoundTag(KeyboardTag)) + keyboard.loadData(nbt.getCompound(KeyboardTag)) } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) if (isServer) { - nbt.setNewCompoundTag(KeyboardTag, keyboard.save) + nbt.setNewCompoundTag(KeyboardTag, keyboard.saveData) } } // ----------------------------------------------------------------------- // - private def isOnWall = facing != EnumFacing.UP && facing != EnumFacing.DOWN + private def isOnWall = facing != Direction.UP && facing != Direction.DOWN - private def forward = if (isOnWall) EnumFacing.UP else yaw + private def forward = if (isOnWall) Direction.UP else yaw } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index b3c256206f..8ac3d04273 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -19,18 +19,19 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.Computer with ISidedInventory with internal.Microcontroller with DeviceInfo { +class Microcontroller extends TileEntity(null) with traits.PowerAcceptor with traits.Hub with traits.Computer with ISidedInventory with internal.Microcontroller with DeviceInfo { val info = new MicrocontrollerData() override def node = null @@ -60,31 +61,31 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C DeviceAttribute.Description -> "Microcontroller", DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor, DeviceAttribute.Product -> "Cubicle", - DeviceAttribute.Capacity -> getSizeInventory.toString + DeviceAttribute.Capacity -> getContainerSize.toString ) override def getDeviceInfo: util.Map[String, String] = deviceInfo // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing): Boolean = side != facing + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction): Boolean = side != facing - override def sidedNode(side: EnumFacing): Node = if (side != facing) super.sidedNode(side) else null + override def sidedNode(side: Direction): Node = if (side != facing) super.sidedNode(side) else null - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing): Boolean = side != facing + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction): Boolean = side != facing - override protected def connector(side: EnumFacing) = Option(if (side != facing) snooperNode else null) + override protected def connector(side: Direction) = Option(if (side != facing) snooperNode else null) override def energyThroughput: Double = Settings.get.caseRate(Tier.One) // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { super.onAnalyze(player, side, hitX, hitY, hitZ) if (side != facing) - Array(componentNodes(side.getIndex)) + Array(componentNodes(side.get3DDataValue)) else Array(machine.node) } @@ -133,8 +134,8 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C super.updateEntity() // Pump energy into the internal network. - if (isServer && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { - for (side <- EnumFacing.values if side != facing) { + if (isServer && getLevel.getGameTime % Settings.get.tickFrequency == 0) { + for (side <- Direction.values if side != facing) { sidedNode(side) match { case connector: Connector => val demand = snooperNode.globalBufferSize - snooperNode.globalBuffer @@ -192,7 +193,7 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C override def onMessage(message: Message): Unit = { if (message.name == "network.message" && message.source.network == snooperNode.network) { - for (side <- EnumFacing.values if outputSides(side.ordinal) && side != facing) { + for (side <- Direction.values if outputSides(side.ordinal) && side != facing) { sidedNode(side).sendToReachable(message.name, message.data: _*) } } @@ -205,44 +206,44 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C private final val ComponentNodesTag = Settings.namespace + "componentNodes" private final val SnooperTag = Settings.namespace + "snooper" - override def readFromNBTForServer(nbt: NBTTagCompound) { + override def loadForServer(nbt: CompoundNBT) { // Load info before inventory and such, to avoid initializing components // to empty inventory. - info.load(nbt.getCompoundTag(InfoTag)) + info.loadData(nbt.getCompound(InfoTag)) nbt.getBooleanArray(OutputsTag) - nbt.getTagList(ComponentNodesTag, NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + nbt.getList(ComponentNodesTag, NBT.TAG_COMPOUND).toTagArray[CompoundNBT]. zipWithIndex.foreach { - case (tag, index) => componentNodes(index).load(tag) + case (tag, index) => componentNodes(index).loadData(tag) } - snooperNode.load(nbt.getCompoundTag(SnooperTag)) - super.readFromNBTForServer(nbt) + snooperNode.loadData(nbt.getCompound(SnooperTag)) + super.loadForServer(nbt) api.Network.joinNewNetwork(machine.node) machine.node.connect(snooperNode) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setNewCompoundTag(InfoTag, info.save) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.setNewCompoundTag(InfoTag, info.saveData) nbt.setBooleanArray(OutputsTag, outputSides) nbt.setNewTagList(ComponentNodesTag, componentNodes.map { case node: Node => - val tag = new NBTTagCompound() - node.save(tag) + val tag = new CompoundNBT() + node.saveData(tag) tag - case _ => new NBTTagCompound() + case _ => new CompoundNBT() }) - nbt.setNewCompoundTag(SnooperTag, snooperNode.save) + nbt.setNewCompoundTag(SnooperTag, snooperNode.saveData) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { - info.load(nbt.getCompoundTag(InfoTag)) - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { + info.loadData(nbt.getCompound(InfoTag)) + super.loadForClient(nbt) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setNewCompoundTag(InfoTag, info.save) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.setNewCompoundTag(InfoTag, info.saveData) } // ----------------------------------------------------------------------- // @@ -251,37 +252,37 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C override def updateItems(slot: Int, stack: ItemStack): Unit = info.components(slot) = stack - override def getSizeInventory: Int = info.components.length + override def getContainerSize: Int = info.components.length - override def isItemValidForSlot(slot: Int, stack: ItemStack) = false + override def canPlaceItem(slot: Int, stack: ItemStack) = false // Nope. - override def setInventorySlotContents(slot: Int, stack: ItemStack) {} + override def setItem(slot: Int, stack: ItemStack) {} // Nope. - override def decrStackSize(slot: Int, amount: Int) = ItemStack.EMPTY + override def removeItem(slot: Int, amount: Int) = ItemStack.EMPTY // Nope. - override def removeStackFromSlot(slot: Int) = ItemStack.EMPTY + override def removeItemNoUpdate(slot: Int) = ItemStack.EMPTY // Nope. - override def canExtractItem(slot: Int, stack: ItemStack, side: EnumFacing) = false + override def canTakeItemThroughFace(slot: Int, stack: ItemStack, side: Direction) = false - override def canInsertItem(slot: Int, stack: ItemStack, side: EnumFacing) = false + override def canPlaceItemThroughFace(slot: Int, stack: ItemStack, side: Direction) = false - override def getSlotsForFace(side: EnumFacing): Array[Int] = Array() + override def getSlotsForFace(side: Direction): Array[Int] = Array() // For hotswapping EEPROMs. def changeEEPROM(newEeprom: ItemStack): StackOption = { val oldEepromIndex = info.components.indexWhere(api.Items.get(_) == api.Items.get(Constants.ItemName.EEPROM)) if (oldEepromIndex >= 0) { val oldEeprom = info.components(oldEepromIndex) - super.setInventorySlotContents(oldEepromIndex, newEeprom) + super.setItem(oldEepromIndex, newEeprom) SomeStack(oldEeprom) } else { - assert(info.components(getSizeInventory - 1).isEmpty) - super.setInventorySlotContents(getSizeInventory - 1, newEeprom) + assert(info.components(getContainerSize - 1).isEmpty) + super.setItem(getContainerSize - 1, newEeprom) EmptyStack } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala b/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala index 9d09d5d0d5..2c264bb58b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala @@ -2,9 +2,10 @@ package li.cil.oc.common.tileentity import li.cil.oc.api.network.Node import li.cil.oc.server.component -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity -class MotionSensor extends traits.Environment with traits.Tickable { +class MotionSensor extends TileEntity(null) with traits.Environment with traits.Tickable { val motionSensor = new component.MotionSensor(this) def node: Node = motionSensor.node @@ -16,13 +17,13 @@ class MotionSensor extends traits.Environment with traits.Tickable { } } - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - motionSensor.load(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + motionSensor.loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - motionSensor.save(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + motionSensor.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala index d7d8e264e0..8aa8f9259d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala @@ -10,17 +10,19 @@ import li.cil.oc.api.network.{Node, Visibility} import li.cil.oc.common.EventHandler import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.init.SoundEvents -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import li.cil.oc.util.RotationHelper +import net.minecraft.util.SoundEvents +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.SoundCategory -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.mutable -class NetSplitter extends traits.Environment with traits.OpenSides with traits.RedstoneAware with api.network.SidedEnvironment with DeviceInfo { +class NetSplitter extends TileEntity(null) with traits.Environment with traits.OpenSides with traits.RedstoneAware with api.network.SidedEnvironment with DeviceInfo { private lazy val deviceInfo: util.Map[String, String] = Map( DeviceAttribute.Class -> DeviceClass.Network, DeviceAttribute.Description -> "Ethernet controller", @@ -40,9 +42,9 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R var isInverted = false - override def isSideOpen(side: EnumFacing): Boolean = if (isInverted) !super.isSideOpen(side) else super.isSideOpen(side) + override def isSideOpen(side: Direction): Boolean = if (isInverted) !super.isSideOpen(side) else super.isSideOpen(side) - override def setSideOpen(side: EnumFacing, value: Boolean) { + override def setSideOpen(side: Direction, value: Boolean) { val previous = isSideOpen(side) super.setSideOpen(side, value) if (previous != isSideOpen(side)) { @@ -50,21 +52,21 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R node.remove() api.Network.joinOrCreateNetwork(this) ServerPacketSender.sendNetSplitterState(this) - getWorld.playSound(null, x + 0.5, y + 0.5, z + 0.5, SoundEvents.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5f, getWorld.rand.nextFloat() * 0.25f + 0.7f) - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, false) + getLevel.playSound(null, getBlockPos, SoundEvents.PISTON_EXTEND, SoundCategory.BLOCKS, 0.5f, getLevel.random.nextFloat() * 0.25f + 0.7f) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) } else { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } } // ----------------------------------------------------------------------- // - override def sidedNode(side: EnumFacing): Node = if (isSideOpen(side)) node else null + override def sidedNode(side: Direction): Node = if (isSideOpen(side)) node else null - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing): Boolean = isSideOpen(side) + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction): Boolean = isSideOpen(side) // ----------------------------------------------------------------------- // @@ -84,10 +86,10 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R node.remove() api.Network.joinOrCreateNetwork(this) ServerPacketSender.sendNetSplitterState(this) - getWorld.playSound(null, x + 0.5, y + 0.5, z + 0.5, SoundEvents.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5f, getWorld.rand.nextFloat() * 0.25f + 0.7f) + getLevel.playSound(null, getBlockPos, SoundEvents.PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5f, getLevel.random.nextFloat() * 0.25f + 0.7f) } else { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } } @@ -97,37 +99,37 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R private final val IsInvertedTag = Settings.namespace + "isInverted" private final val OpenSidesTag = Settings.namespace + "openSides" - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) isInverted = nbt.getBoolean(IsInvertedTag) } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) - nbt.setBoolean(IsInvertedTag, isInverted) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) + nbt.putBoolean(IsInvertedTag, isInverted) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound): Unit = { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT): Unit = { + super.loadForClient(nbt) isInverted = nbt.getBoolean(IsInvertedTag) } - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { - super.writeToNBTForClient(nbt) - nbt.setBoolean(IsInvertedTag, isInverted) + override def saveForClient(nbt: CompoundNBT): Unit = { + super.saveForClient(nbt) + nbt.putBoolean(IsInvertedTag, isInverted) } // component api def currentStatus(): mutable.Map[Int, Boolean] = { val openStatus = mutable.Map[Int, Boolean]() - for (side <- EnumFacing.VALUES) { + for (side <- Direction.values) { openStatus += side.ordinal() -> isSideOpen(side) } openStatus } - def setSide(side: EnumFacing, state: Boolean): Boolean = { + def setSide(side: Direction, state: Boolean): Boolean = { val previous = isSideOpen(side) // isSideOpen uses inverter setSideOpen(side, if (isInverted) !state else state) // but setSideOpen does not previous != state @@ -137,7 +139,7 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R def setSides(context: Context, args: Arguments): Array[AnyRef] = { val settings = args.checkTable(0) val previous = currentStatus() - for (side <- EnumFacing.VALUES) { + for (side <- Direction.values) { val ordinal = side.ordinal() val value = if (settings.containsKey(ordinal)) { settings.get(ordinal) match { @@ -157,7 +159,7 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R val sideIndex = args.checkInteger(0) if (sideIndex < 0 || sideIndex > 5) return result(Unit, "invalid direction") - val side = EnumFacing.getFront(sideIndex) + val side = Direction.from3DDataValue(sideIndex) result(setSide(side, value)) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala index cf6ad933b8..be21a3f2d0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala @@ -9,13 +9,14 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.network._ -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class PowerConverter extends traits.PowerAcceptor with traits.Environment with traits.NotAnalyzable with DeviceInfo { +class PowerConverter extends TileEntity(null) with traits.PowerAcceptor with traits.Environment with traits.NotAnalyzable with DeviceInfo { val node = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -30,10 +31,10 @@ class PowerConverter extends traits.PowerAcceptor with traits.Environment with t override def getDeviceInfo: util.Map[String, String] = deviceInfo - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing) = true + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction) = true - override protected def connector(side: EnumFacing) = Option(node) + override protected def connector(side: Direction) = Option(node) override def energyThroughput = Settings.get.powerConverterRate } diff --git a/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala b/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala index 12984473d9..bbe585591c 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala @@ -4,13 +4,14 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.network._ import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class PowerDistributor extends traits.Environment with traits.PowerBalancer with traits.NotAnalyzable { +class PowerDistributor extends TileEntity(null) with traits.Environment with traits.PowerBalancer with traits.NotAnalyzable { val node = null private val nodes = Array.fill(6)(api.Network.newNode(this, Visibility.None). @@ -21,30 +22,30 @@ class PowerDistributor extends traits.Environment with traits.PowerBalancer with // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = true + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = true - override def sidedNode(side: EnumFacing): Connector = nodes(side.ordinal) + override def sidedNode(side: Direction): Connector = nodes(side.ordinal) // ----------------------------------------------------------------------- // private final val ConnectorTag = Settings.namespace + "connector" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - nbt.getTagList(ConnectorTag, NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + nbt.getList(ConnectorTag, NBT.TAG_COMPOUND).toTagArray[CompoundNBT]. zipWithIndex.foreach { - case (tag, index) => nodes(index).load(tag) + case (tag, index) => nodes(index).loadData(tag) } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) // Side check for Waila (and other mods that may call this client side). if (isServer) { nbt.setNewTagList(ConnectorTag, nodes.map(connector => { - val connectorNbt = new NBTTagCompound() - connector.save(connectorNbt) + val connectorNbt = new CompoundNBT() + connector.saveData(connectorNbt) connectorNbt })) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index 1214010bc7..27162cef6c 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -6,23 +6,27 @@ import com.google.common.base.Strings import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.common.block.{Print => PrintBlock} import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs import li.cil.oc.util.ExtendedAABB import li.cil.oc.util.ExtendedAABB._ import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.init.SoundEvents -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util._ +import net.minecraft.util.SoundEvents +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraft.util.SoundCategory import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.world.server.ServerWorld +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) extends traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { +class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) extends TileEntity(null) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { def this() = this(None, None, None) def this(canToggle: () => Boolean, scheduleUpdate: Int => Unit, onStateChange: () => Unit) = this(Option(canToggle), Option(scheduleUpdate), Option(onStateChange)) @@ -38,72 +42,6 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int def noclip = if (state) data.noclipOn else data.noclipOff def shapes = if (state) data.stateOn else data.stateOff - def isSideSolid(side: EnumFacing): Boolean = { - for (shape <- shapes if !Strings.isNullOrEmpty(shape.texture)) { - val bounds = shape.bounds.rotateTowards(facing) - val fullX = bounds.minX == 0 && bounds.maxX == 1 - val fullY = bounds.minY == 0 && bounds.maxY == 1 - val fullZ = bounds.minZ == 0 && bounds.maxZ == 1 - if (side match { - case EnumFacing.DOWN => bounds.minY == 0 && fullX && fullZ - case EnumFacing.UP => bounds.maxY == 1 && fullX && fullZ - case EnumFacing.NORTH => bounds.minZ == 0 && fullX && fullY - case EnumFacing.SOUTH => bounds.maxZ == 1 && fullX && fullY - case EnumFacing.WEST => bounds.minX == 0 && fullY && fullZ - case EnumFacing.EAST => bounds.maxX == 1 && fullY && fullZ - case _ => false - }) return true - } - false - } - - def addCollisionBoxesToList(mask: AxisAlignedBB, list: util.List[AxisAlignedBB], pos: BlockPos = BlockPos.ORIGIN): Unit = { - if (!noclip) { - if (shapes.isEmpty) { - val unitBounds = new AxisAlignedBB(0, 0, 0, 1, 1, 1).offset(pos) - if (mask == null || unitBounds.intersects(mask)) { - list.add(unitBounds) - } - } else { - for (shape <- shapes) { - val bounds = shape.bounds.rotateTowards(facing).offset(pos) - if (mask == null || bounds.intersects(mask)) { - list.add(bounds) - } - } - } - } - } - - def rayTrace(start: Vec3d, end: Vec3d, pos: BlockPos = BlockPos.ORIGIN): RayTraceResult = { - var closestDistance = Double.PositiveInfinity - var closest: Option[RayTraceResult] = None - if (shapes.isEmpty) { - val bounds = new AxisAlignedBB(0, 0, 0, 1, 1, 1).offset(pos) - val hit = bounds.calculateIntercept(start, end) - if (hit != null) { - val distance = hit.hitVec.distanceTo(start) - if (distance < closestDistance) { - closestDistance = distance - closest = Option(hit) - } - } - } else { - for (shape <- shapes) { - val bounds = shape.bounds.rotateTowards(facing).offset(pos) - val hit = bounds.calculateIntercept(start, end) - if (hit != null) { - val distance = hit.hitVec.distanceTo(start) - if (distance < closestDistance) { - closestDistance = distance - closest = Option(hit) - } - } - } - } - closest.map(hit => new RayTraceResult(hit.hitVec, hit.sideHit, pos)).orNull - } - def activate(): Boolean = { if (data.hasActiveState) { if (!state || !data.isButtonMode) { @@ -116,7 +54,7 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int private def buildValueSet(value: Int): util.Map[AnyRef, AnyRef] = { val map: util.Map[AnyRef, AnyRef] = new util.HashMap[AnyRef, AnyRef]() - EnumFacing.values.foreach { + Direction.values.foreach { side => map.put(new java.lang.Integer(side.ordinal), new java.lang.Integer(value)) } map @@ -125,15 +63,15 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int def toggleState(): Unit = { if (canToggle.fold(true)(_.apply())) { state = !state - world.playSound(null, x + 0.5, y + 0.5, z + 0.5, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 0.3F, if (state) 0.6F else 0.5F) - world.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.playSound(null, getBlockPos, SoundEvents.LEVER_CLICK, SoundCategory.BLOCKS, 0.3F, if (state) 0.6F else 0.5F) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) updateRedstone() if (state && data.isButtonMode) { - val block = api.Items.get(Constants.BlockName.Print).block() - val delay = block.tickRate(world) + val block = api.Items.get(Constants.BlockName.Print).block().asInstanceOf[PrintBlock] + val delay = block.tickRate(getLevel) scheduleUpdate match { case Some(callback) => callback(delay) - case _ => world.scheduleUpdate(getPos, block, delay) + case _ if !getLevel.isClientSide => getLevel.asInstanceOf[ServerWorld].getBlockTicks.scheduleTick(getBlockPos, block, delay) } } onStateChange.foreach(_.apply()) @@ -141,10 +79,10 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int } def updateBounds(): Unit = { - boundsOff = data.stateOff.drop(1).foldLeft(data.stateOff.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.union(b.bounds)) + boundsOff = data.stateOff.drop(1).foldLeft(data.stateOff.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.minmax(b.bounds)) if (boundsOff.volume == 0) boundsOff = ExtendedAABB.unitBounds else boundsOff = boundsOff.rotateTowards(facing) - boundsOn = data.stateOn.drop(1).foldLeft(data.stateOn.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.union(b.bounds)) + boundsOn = data.stateOn.drop(1).foldLeft(data.stateOn.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.minmax(b.bounds)) if (boundsOn.volume == 0) boundsOn = ExtendedAABB.unitBounds else boundsOn = boundsOn.rotateTowards(facing) } @@ -170,44 +108,46 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int // ----------------------------------------------------------------------- // private final val DataTag = Settings.namespace + "data" + @Deprecated private final val DataTagCompat = "data" private final val StateTag = Settings.namespace + "state" + @Deprecated private final val StateTagCompat = "state" - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(DataTagCompat)) - data.load(nbt.getCompoundTag(DataTagCompat)) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) + if (nbt.contains(DataTagCompat)) + data.loadData(nbt.getCompound(DataTagCompat)) else - data.load(nbt.getCompoundTag(DataTag)) - if (nbt.hasKey(StateTagCompat)) + data.loadData(nbt.getCompound(DataTag)) + if (nbt.contains(StateTagCompat)) state = nbt.getBoolean(StateTagCompat) else state = nbt.getBoolean(StateTag) updateBounds() } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) - nbt.setNewCompoundTag(DataTag, data.save) - nbt.setBoolean(StateTag, state) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) + nbt.setNewCompoundTag(DataTag, data.saveData) + nbt.putBoolean(StateTag, state) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound): Unit = { - super.readFromNBTForClient(nbt) - data.load(nbt.getCompoundTag(DataTag)) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT): Unit = { + super.loadForClient(nbt) + data.loadData(nbt.getCompound(DataTag)) state = nbt.getBoolean(StateTag) updateBounds() - if (world != null) { - world.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) - if (data.emitLight) world.checkLight(getPos) + if (getLevel != null) { + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) + if (data.emitLight) getLevel.getLightEngine.checkBlock(getBlockPos) } } - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { - super.writeToNBTForClient(nbt) - nbt.setNewCompoundTag(DataTag, data.save) - nbt.setBoolean(StateTag, state) + override def saveForClient(nbt: CompoundNBT): Unit = { + super.saveForClient(nbt) + nbt.setNewCompoundTag(DataTag, data.saveData) + nbt.putBoolean(StateTag, state) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index f60e2e2d45..5a5ca5b965 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -20,15 +20,16 @@ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ -class Printer extends traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { +class Printer extends TileEntity(null) with traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { val node: ComponentConnector = api.Network.newNode(this, Visibility.Network). withComponent("printer3d"). withConnector(Settings.get.bufferConverter). @@ -61,10 +62,10 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing): Boolean = side != EnumFacing.UP + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction): Boolean = side != Direction.UP - override def sidedNode(side: EnumFacing): ComponentConnector = if (side != EnumFacing.UP) node else null + override def sidedNode(side: Direction): ComponentConnector = if (side != Direction.UP) node else null override def getCurrentState: util.EnumSet[StateAware.State] = { if (isPrinting) util.EnumSet.of(api.util.StateAware.State.IsWorking) @@ -197,7 +198,7 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat texture, tint) isActive = false // Needs committing. - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) result(true) } @@ -235,9 +236,9 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat } def canMergeOutput = { - val presentStack = getStackInSlot(slotOutput) + val presentStack = getItem(slotOutput) val outputStack = data.createItemStack() - presentStack.isEmpty || (presentStack.isItemEqual(outputStack) && ItemStack.areItemStackTagsEqual(presentStack, outputStack)) + presentStack.isEmpty || (presentStack.sameItem(outputStack) && ItemStack.tagMatches(presentStack, outputStack)) } if (isActive && output.isEmpty && canMergeOutput) { @@ -265,13 +266,13 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat val have = want + (if (Settings.get.ignorePower) 0 else node.changeBuffer(-want)) requiredEnergy -= have if (requiredEnergy <= 0) { - val result = getStackInSlot(slotOutput) + val result = getItem(slotOutput) if (result.isEmpty) { - setInventorySlotContents(slotOutput, output.get) + setItem(slotOutput, output.get) } else if (result.getCount < result.getMaxStackSize && canMergeOutput /* Should never fail, but just in case... */ ) { result.grow(1) - markDirty() + setChanged() } else { return @@ -282,21 +283,21 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat ServerPacketSender.sendPrinting(this, have > 0.5 && output.isDefined) } - val inputValue = PrintData.materialValue(getStackInSlot(slotMaterial)) + val inputValue = PrintData.materialValue(getItem(slotMaterial)) if (inputValue > 0 && maxAmountMaterial - amountMaterial >= inputValue) { - val material = decrStackSize(slotMaterial, 1) + val material = removeItem(slotMaterial, 1) if (material != null) { amountMaterial += inputValue } } - val inkValue = PrintData.inkValue(getStackInSlot(slotInk)) + val inkValue = PrintData.inkValue(getItem(slotInk)) if (inkValue > 0 && maxAmountInk - amountInk >= inkValue) { - val material = decrStackSize(slotInk, 1) + val material = removeItem(slotInk, 1) if (material != null) { amountInk += inkValue - if (material.getItem.hasContainerItem(material)) { - setInventorySlotContents(slotInk, material.getItem.getContainerItem(material)) + if (material.hasContainerItem()) { + setItem(slotInk, material.getContainerItem()) } } } @@ -313,15 +314,15 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat private final val TotalTag = Settings.namespace + "total" private final val RemainingTag = Settings.namespace + "remaining" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - amountMaterial = nbt.getInteger(AmountMaterialTag) - amountInk = nbt.getInteger(AmountInkTag) - data.load(nbt.getCompoundTag(DataTag)) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + amountMaterial = nbt.getInt(AmountMaterialTag) + amountInk = nbt.getInt(AmountInkTag) + data.loadData(nbt.getCompound(DataTag)) isActive = nbt.getBoolean(IsActiveTag) - limit = nbt.getInteger(LimitTag) - if (nbt.hasKey(OutputTag)) { - output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTag))) + limit = nbt.getInt(LimitTag) + if (nbt.contains(OutputTag)) { + output = StackOption(ItemStack.of(nbt.getCompound(OutputTag))) } else { output = EmptyStack @@ -330,36 +331,36 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat requiredEnergy = nbt.getDouble(RemainingTag) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setInteger(AmountMaterialTag, amountMaterial) - nbt.setInteger(AmountInkTag, amountInk) - nbt.setNewCompoundTag(DataTag, data.save) - nbt.setBoolean(IsActiveTag, isActive) - nbt.setInteger(LimitTag, limit) - output.foreach(stack => nbt.setNewCompoundTag(OutputTag, stack.writeToNBT)) - nbt.setDouble(TotalTag, totalRequiredEnergy) - nbt.setDouble(RemainingTag, requiredEnergy) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.putInt(AmountMaterialTag, amountMaterial) + nbt.putInt(AmountInkTag, amountInk) + nbt.setNewCompoundTag(DataTag, data.saveData) + nbt.putBoolean(IsActiveTag, isActive) + nbt.putInt(LimitTag, limit) + output.foreach(stack => nbt.setNewCompoundTag(OutputTag, stack.save)) + nbt.putDouble(TotalTag, totalRequiredEnergy) + nbt.putDouble(RemainingTag, requiredEnergy) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - data.load(nbt.getCompoundTag(DataTag)) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + data.loadData(nbt.getCompound(DataTag)) requiredEnergy = nbt.getDouble(RemainingTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setNewCompoundTag(DataTag, data.save) - nbt.setDouble(RemainingTag, requiredEnergy) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.setNewCompoundTag(DataTag, data.saveData) + nbt.putDouble(RemainingTag, requiredEnergy) } // ----------------------------------------------------------------------- // - override def getSizeInventory = 3 + override def getContainerSize = 3 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = if (slot == slotMaterial) PrintData.materialValue(stack) > 0 else if (slot == slotInk) @@ -368,9 +369,9 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat // ----------------------------------------------------------------------- // - override def getSlotsForFace(side: EnumFacing): Array[Int] = Array(slotMaterial, slotInk, slotOutput) + override def getSlotsForFace(side: Direction): Array[Int] = Array(slotMaterial, slotInk, slotOutput) - override def canExtractItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = !isItemValidForSlot(slot, stack) + override def canTakeItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = !canPlaceItem(slot, stack) - override def canInsertItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = slot != slotOutput + override def canPlaceItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = slot != slotOutput } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index eff335baf4..184e342ff8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -21,20 +21,22 @@ import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedInventory._ import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import li.cil.oc.util.RotationHelper +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagIntArray -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.IntArrayNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware { +class Rack extends TileEntity(null) with traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware { var isRelayEnabled = false - val lastData = new Array[NBTTagCompound](getSizeInventory) - val hasChanged: Array[Boolean] = Array.fill(getSizeInventory)(true) + val lastData = new Array[CompoundNBT](getContainerSize) + val hasChanged: Array[Boolean] = Array.fill(getContainerSize)(true) // Map node connections for each installed mountable. Each mountable may // have up to four outgoing connections, with the first one always being @@ -43,12 +45,12 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // The other nodes are "secondary" connections and merely transfer network // messages. // mountable -> connectable -> side - val nodeMapping: Array[Array[Option[EnumFacing]]] = Array.fill(getSizeInventory)(Array.fill[Option[EnumFacing]](4)(None)) - val snifferNodes: Array[Array[Node]] = Array.fill(getSizeInventory)(Array.fill(3)(api.Network.newNode(this, Visibility.Neighbors).create())) + val nodeMapping: Array[Array[Option[Direction]]] = Array.fill(getContainerSize)(Array.fill[Option[Direction]](4)(None)) + val snifferNodes: Array[Array[Node]] = Array.fill(getContainerSize)(Array.fill(3)(api.Network.newNode(this, Visibility.Neighbors).create())) - def connect(slot: Int, connectableIndex: Int, side: Option[EnumFacing]): Unit = { + def connect(slot: Int, connectableIndex: Int, side: Option[Direction]): Unit = { val newSide = side match { - case Some(direction) if direction != EnumFacing.SOUTH => Option(direction) + case Some(direction) if direction != Direction.SOUTH => Option(direction) case _ => None } @@ -93,8 +95,8 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance } } - private def reconnect(plugSide: EnumFacing): Unit = { - for (slot <- 0 until getSizeInventory) { + private def reconnect(plugSide: Direction): Unit = { + for (slot <- 0 until getContainerSize) { val mapping = nodeMapping(slot) mapping(0) match { case Some(side) if toGlobal(side) == plugSide => @@ -125,11 +127,11 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance } } - protected def sendPacketToMountables(sourceSide: Option[EnumFacing], packet: Packet): Unit = { + protected def sendPacketToMountables(sourceSide: Option[Direction], packet: Packet): Unit = { // When a message arrives on a bus, also send it to all secondary nodes // connected to it. Only deliver it to that very node, if it's not the // sender, to avoid loops. - for (slot <- 0 until getSizeInventory) { + for (slot <- 0 until getContainerSize) { val mapping = nodeMapping(slot) for (connectableIndex <- 0 until 3) { mapping(connectableIndex + 1) match { @@ -150,7 +152,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // ----------------------------------------------------------------------- // // Hub - override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + override def tryEnqueuePacket(sourceSide: Option[Direction], packet: Packet): Boolean = { sendPacketToMountables(sourceSide, packet) if (isRelayEnabled) super.tryEnqueuePacket(sourceSide, packet) @@ -158,7 +160,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance true } - override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet): Unit = { + override protected def relayPacket(sourceSide: Option[Direction], packet: Packet): Unit = { if (isRelayEnabled) super.relayPacket(sourceSide, packet) } @@ -190,7 +192,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance } private def relayIfMessageFromConnectable(message: Message, packet: Packet): Unit = { - for (slot <- 0 until getSizeInventory) { + for (slot <- 0 until getContainerSize) { val mountable = getMountable(slot) if (mountable != null) { val mapping = nodeMapping(slot) @@ -212,8 +214,8 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance } } - private def relayToConnectablesOnSide(message: Message, packet: Packet, sourceSide: EnumFacing): Unit = { - for (slot <- 0 until getSizeInventory) { + private def relayToConnectablesOnSide(message: Message, packet: Packet, sourceSide: Direction): Unit = { + for (slot <- 0 until getContainerSize) { val mountable = getMountable(slot) if (mountable != null) { val mapping = nodeMapping(slot) @@ -236,24 +238,24 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // ----------------------------------------------------------------------- // // SidedEnvironment - override def canConnect(side: EnumFacing): Boolean = side != facing + override def canConnect(side: Direction): Boolean = side != facing - override def sidedNode(side: EnumFacing): Node = if (side != facing) super.sidedNode(side) else null + override def sidedNode(side: Direction): Node = if (side != facing) super.sidedNode(side) else null // ----------------------------------------------------------------------- // // power.Common - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing): Boolean = side != facing + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction): Boolean = side != facing - override protected def connector(side: EnumFacing) = Option(if (side != facing) sidedNode(side).asInstanceOf[Connector] else null) + override protected def connector(side: Direction) = Option(if (side != facing) sidedNode(side).asInstanceOf[Connector] else null) override def energyThroughput: Double = Settings.get.serverRackRate // ----------------------------------------------------------------------- // // Analyzable - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { slotAt(side, hitX, hitY, hitZ) match { case Some(slot) => components(slot) match { case Some(analyzable: Analyzable) => analyzable.onAnalyze(player, side, hitX, hitY, hitZ) @@ -273,7 +275,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance case _ => null } - override def getMountableData(slot: Int): NBTTagCompound = lastData(slot) + override def getMountableData(slot: Int): CompoundNBT = lastData(slot) override def markChanged(slot: Int): Unit = { hasChanged.synchronized(hasChanged(slot) = true) @@ -314,23 +316,23 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // ----------------------------------------------------------------------- // // IInventory - override def getSizeInventory = 4 + override def getContainerSize = 4 - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { case (_, Some(driver)) => driver.slot(stack) == Slot.RackMountable case _ => false } - override def markDirty() { - super.markDirty() + override def setChanged() { + super.setChanged() if (isServer) { setOutputEnabled(hasRedstoneCard) ServerPacketSender.sendRackInventory(this) } else { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } @@ -370,7 +372,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance override def updateEntity() { super.updateEntity() if (isServer && isConnected) { - lazy val connectors = EnumFacing.VALUES.map(sidedNode).collect { + lazy val connectors = Direction.values.map(sidedNode).collect { case connector: Connector => connector } components.zipWithIndex.collect { @@ -379,7 +381,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance hasChanged(slot) = false lastData(slot) = mountable.getData ServerPacketSender.sendRackMountableData(this, slot) - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, false) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) // These are working state dependent, so recompute them. setOutputEnabled(hasRedstoneCard) } @@ -409,54 +411,54 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance private final val LastDataTag = Settings.namespace + "lastData" private final val RackDataTag = Settings.namespace + "rackData" - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) isRelayEnabled = nbt.getBoolean(IsRelayEnabledTag) - nbt.getTagList(NodeMappingTag, NBT.TAG_INT_ARRAY).map((buses: NBTTagIntArray) => - buses.getIntArray.map(id => if (id < 0 || id == EnumFacing.SOUTH.ordinal()) None else Option(EnumFacing.getFront(id)))). + nbt.getList(NodeMappingTag, NBT.TAG_INT_ARRAY).map((buses: IntArrayNBT) => + buses.getAsIntArray.map(id => if (id < 0 || id == Direction.SOUTH.ordinal()) None else Option(Direction.from3DDataValue(id)))). copyToArray(nodeMapping) // Kickstart initialization. _isOutputEnabled = hasRedstoneCard } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) - nbt.setBoolean(IsRelayEnabledTag, isRelayEnabled) + nbt.putBoolean(IsRelayEnabledTag, isRelayEnabled) nbt.setNewTagList(NodeMappingTag, nodeMapping.map(buses => toNbt(buses.map(side => side.fold(-1)(_.ordinal()))))) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound): Unit = { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT): Unit = { + super.loadForClient(nbt) - val data = nbt.getTagList(LastDataTag, NBT.TAG_COMPOUND). - toArray[NBTTagCompound] + val data = nbt.getList(LastDataTag, NBT.TAG_COMPOUND). + toTagArray[CompoundNBT] data.copyToArray(lastData) - load(nbt.getCompoundTag(RackDataTag)) + loadData(nbt.getCompound(RackDataTag)) connectComponents() } - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { - super.writeToNBTForClient(nbt) + override def saveForClient(nbt: CompoundNBT): Unit = { + super.saveForClient(nbt) - val data = lastData.map(tag => if (tag == null) new NBTTagCompound() else tag) + val data = lastData.map(tag => if (tag == null) new CompoundNBT() else tag) nbt.setNewTagList(LastDataTag, data) - nbt.setNewCompoundTag(RackDataTag, save) + nbt.setNewCompoundTag(RackDataTag, saveData) } // ----------------------------------------------------------------------- // - def slotAt(side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Option[Int] = { + def slotAt(side: Direction, hitX: Float, hitY: Float, hitZ: Float): Option[Int] = { if (side == facing) { val globalY = (hitY * 16).toInt // [0, 15] val l = 2 val h = 14 - val slot = ((15 - globalY) - l) * getSizeInventory / (h - l) - Some(math.max(0, math.min(getSizeInventory - 1, slot))) + val slot = ((15 - globalY) - l) * getContainerSize / (h - l) + Some(math.max(0, math.min(getContainerSize - 1, slot))) } else None } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 9b21c5523a..27db6cf3f4 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -14,14 +14,15 @@ import li.cil.oc.common.item.data.NodeData import li.cil.oc.server.component.FileSystem import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Raid extends traits.Environment with traits.Inventory with traits.Rotatable with Analyzable { +class Raid extends TileEntity(null) with traits.Environment with traits.Inventory with traits.Rotatable with Analyzable { val node = api.Network.newNode(this, Visibility.None).create() var filesystem: Option[FileSystem] = None @@ -32,19 +33,19 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl var lastAccess = 0L // For client side rendering. - val presence = Array.fill(getSizeInventory)(false) + val presence = Array.fill(getContainerSize)(false) // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(filesystem.map(_.node).orNull) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(filesystem.map(_.node).orNull) // ----------------------------------------------------------------------- // - override def getSizeInventory = 3 + override def getContainerSize = 3 - override def getInventoryStackLimit = 1 + override def getMaxStackSize = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack) = Option(Driver.driverFor(stack, getClass)) match { + override def canPlaceItem(slot: Int, stack: ItemStack) = Option(Driver.driverFor(stack, getClass)) match { case Some(driver) => driver.slot(stack) == Slot.HDD case _ => false } @@ -57,8 +58,8 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl } } - override def markDirty() { - super.markDirty() + override def setChanged() { + super.setChanged() // Makes the implementation of the comparator output easier. items.map(!_.isEmpty).copyToArray(presence) } @@ -70,7 +71,7 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl filesystem.foreach(fs => { fs.fileSystem.close() fs.fileSystem.list("/").foreach(fs.fileSystem.delete) - fs.save(new NBTTagCompound()) // Flush buffered fs. + fs.saveData(new CompoundNBT()) // Flush buffered fs. fs.node.remove() filesystem = None }) @@ -84,15 +85,15 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl val drive = new DriveData(fsStack) drive.lockInfo = "" drive.isUnmanaged = false - drive.save(fsStack) + drive.saveData(fsStack) }) val fs = api.FileSystem.asManagedEnvironment( api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges), label, this, Settings.resourceDomain + ":hdd_access", 6). asInstanceOf[FileSystem] - val nbtToSetAddress = new NBTTagCompound() - nbtToSetAddress.setString(NodeData.AddressTag, id) - fs.node.load(nbtToSetAddress) + val nbtToSetAddress = new CompoundNBT() + nbtToSetAddress.putString(NodeData.AddressTag, id) + fs.node.loadData(nbtToSetAddress) fs.node.setVisibility(Visibility.Network) // Ensure we're in a network before connecting the raid fs. api.Network.joinNewNetwork(node) @@ -106,10 +107,10 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl case Some(driver) => driver.createEnvironment(hdd, this) match { case fs: FileSystem => val nbt = driver.dataTag(hdd) - fs.load(nbt) + fs.loadData(nbt) fs.fileSystem.close() fs.fileSystem.list("/").foreach(fs.fileSystem.delete) - fs.save(nbt) + fs.saveData(nbt) fs.fileSystem.spaceTotal.toInt case _ => 0L // Ignore. } @@ -124,36 +125,36 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl private final val PresenceTag = Settings.namespace + "presence" private final val LabelTag = Settings.namespace + "label" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(FileSystemTag)) { - val tag = nbt.getCompoundTag(FileSystemTag) - tryCreateRaid(tag.getCompoundTag(NodeData.NodeTag).getString(NodeData.AddressTag)) - filesystem.foreach(fs => fs.load(tag)) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + if (nbt.contains(FileSystemTag)) { + val tag = nbt.getCompound(FileSystemTag) + tryCreateRaid(tag.getCompound(NodeData.NodeTag).getString(NodeData.AddressTag)) + filesystem.foreach(fs => fs.loadData(tag)) } - label.load(nbt) + label.loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - filesystem.foreach(fs => nbt.setNewCompoundTag(FileSystemTag, fs.save)) - label.save(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + filesystem.foreach(fs => nbt.setNewCompoundTag(FileSystemTag, fs.saveData)) + label.saveData(nbt) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) nbt.getByteArray(PresenceTag). map(_ != 0). copyToArray(presence) label.setLabel(nbt.getString(LabelTag)) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setTag(PresenceTag, items.map(!_.isEmpty)) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.put(PresenceTag, items.map(!_.isEmpty)) if (label.getLabel != null) - nbt.setString(LabelTag, label.getLabel) + nbt.putString(LabelTag, label.getLabel) } // ----------------------------------------------------------------------- // @@ -165,14 +166,14 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl override def setLabel(value: String) = label = Option(value).map(_.take(16)).orNull - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey(Settings.namespace + "label")) { + override def loadData(nbt: CompoundNBT) { + if (nbt.contains(Settings.namespace + "label")) { label = nbt.getString(Settings.namespace + "label") } } - override def save(nbt: NBTTagCompound) { - nbt.setString(Settings.namespace + "label", label) + override def saveData(nbt: CompoundNBT) { + nbt.putString(Settings.namespace + "label", label) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala index 19acb84f6f..b69e3b4467 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala @@ -10,10 +10,11 @@ import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.server.component import li.cil.oc.server.component.RedstoneVanilla import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction -class Redstone extends traits.Environment with traits.BundledRedstoneAware with traits.Tickable { +class Redstone extends TileEntity(null) with traits.Environment with traits.BundledRedstoneAware with traits.Tickable { val instance: RedstoneVanilla = if (BundledRedstone.isAvailable) new component.Redstone.Bundled(this) @@ -32,14 +33,14 @@ class Redstone extends traits.Environment with traits.BundledRedstoneAware with private final val RedstoneTag = Settings.namespace + "redstone" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - instance.load(nbt.getCompoundTag(RedstoneTag)) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + instance.loadData(nbt.getCompound(RedstoneTag)) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setNewCompoundTag(RedstoneTag, instance.save) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.setNewCompoundTag(RedstoneTag, instance.saveData) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index d1410d5b04..8f892923e8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -30,15 +30,17 @@ import li.cil.oc.integration.Mods import li.cil.oc.integration.opencomputers.DriverLinkedCard import li.cil.oc.server.network.QuantumNetwork import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraft.util.Util import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { +class Relay extends TileEntity(null) with traits.Hub with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { lazy final val WirelessNetworkCardTier1: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier1) lazy final val WirelessNetworkCardTier2: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier2) lazy final val LinkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) @@ -79,10 +81,10 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override protected def hasConnector(side: EnumFacing) = true + @OnlyIn(Dist.CLIENT) + override protected def hasConnector(side: Direction) = true - override protected def connector(side: EnumFacing): Option[Connector] = sidedNode(side) match { + override protected def connector(side: Direction): Option[Connector] = sidedNode(side) match { case connector: Connector => Option(connector) case _ => None } @@ -91,10 +93,10 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { if (isWirelessEnabled) { - player.sendMessage(Localization.Analyzer.WirelessStrength(strength)) - Array(componentNodes(side.getIndex)) + player.sendMessage(Localization.Analyzer.WirelessStrength(strength), Util.NIL_UUID) + Array(componentNodes(side.get3DDataValue)) } else null } @@ -148,7 +150,7 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA val computers = mutable.Buffer.empty[AnyRef] - override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + override def tryEnqueuePacket(sourceSide: Option[Direction], packet: Packet): Boolean = { if (Mods.ComputerCraft.isModAvailable) { packet.data.headOption match { case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) @@ -158,7 +160,7 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA super.tryEnqueuePacket(sourceSide, packet) } - override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet): Unit = { + override protected def relayPacket(sourceSide: Option[Direction], packet: Packet): Unit = { super.relayPacket(sourceSide, packet) val tryChangeBuffer = sourceSide match { @@ -240,7 +242,7 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA wirelessTier = if (descriptor == WirelessNetworkCardTier1) Tier.One else Tier.Two if (descriptor == LinkedCard) { val data = DriverLinkedCard.dataTag(stack) - if (data.hasKey(Settings.namespace + "tunnel")) { + if (data.contains(Settings.namespace + "tunnel")) { tunnel = data.getString(Settings.namespace + "tunnel") isLinkedEnabled = true QuantumNetwork.add(this) @@ -263,9 +265,9 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA } } - override def getSizeInventory: Int = InventorySlots.relay.length + override def getContainerSize: Int = InventorySlots.relay.length - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = Option(Driver.driverFor(stack, getClass)).fold(false)(driver => { val provided = InventorySlots.relay(slot) val tierSatisfied = driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier @@ -280,34 +282,34 @@ class Relay extends traits.Hub with traits.ComponentInventory with traits.PowerA private final val IsRepeaterTag = Settings.namespace + "isRepeater" private final val ComponentNodesTag = Settings.namespace + "componentNodes" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) for (slot <- items.indices) if (!items(slot).isEmpty) { updateLimits(slot, items(slot)) } - if (nbt.hasKey(StrengthTag)) { + if (nbt.contains(StrengthTag)) { strength = nbt.getDouble(StrengthTag) max 0 min maxWirelessRange } - if (nbt.hasKey(IsRepeaterTag)) { + if (nbt.contains(IsRepeaterTag)) { isRepeater = nbt.getBoolean(IsRepeaterTag) } - nbt.getTagList(ComponentNodesTag, NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + nbt.getList(ComponentNodesTag, NBT.TAG_COMPOUND).toTagArray[CompoundNBT]. zipWithIndex.foreach { - case (tag, index) => componentNodes(index).load(tag) + case (tag, index) => componentNodes(index).loadData(tag) } } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) - nbt.setDouble(StrengthTag, strength) - nbt.setBoolean(IsRepeaterTag, isRepeater) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) + nbt.putDouble(StrengthTag, strength) + nbt.putBoolean(IsRepeaterTag, isRepeater) nbt.setNewTagList(ComponentNodesTag, componentNodes.map { case node: Node => - val tag = new NBTTagCompound() - node.save(tag) + val tag = new CompoundNBT() + node.saveData(tag) tag - case _ => new NBTTagCompound() + case _ => new CompoundNBT() }) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 5ab733c9b4..6801562fad 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -32,27 +32,31 @@ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.block.Block -import net.minecraft.block.BlockLiquid +import net.minecraft.block.Blocks +import net.minecraft.block.FlowingFluidBlock import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.init.Blocks -import net.minecraft.init.SoundEvents -import net.minecraft.inventory.EntityEquipmentSlot +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.Fluid +import net.minecraft.inventory.EquipmentSlotType import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.SoundCategory +import net.minecraft.util.SoundEvents +import net.minecraft.util.Util import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier import net.minecraftforge.fluids._ import net.minecraftforge.fluids.capability.CapabilityFluidHandler -import net.minecraftforge.fluids.capability.FluidTankProperties import net.minecraftforge.fluids.capability.IFluidHandler -import net.minecraftforge.fluids.capability.IFluidTankProperties -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable @@ -62,23 +66,26 @@ import scala.collection.mutable // robot moves we only create a new proxy tile entity, hook the instance of this // class that was held by the old proxy to it and can then safely forget the // old proxy, which will be cleaned up by Minecraft like any other tile entity. -class Robot extends traits.Computer with traits.PowerInformation with traits.RotatableTile with IFluidHandler with internal.Robot with InventorySelection with TankSelection { +class Robot extends TileEntity(null) with traits.Computer with traits.PowerInformation with traits.RotatableTile with IFluidHandler with internal.Robot with InventorySelection with TankSelection { var proxy: RobotProxy = _ val info = new RobotData() val bot: component.Robot = if (isServer) new component.Robot(this) else null + val fluidCap: LazyOptional[IFluidHandler] = LazyOptional.of(new NonNullSupplier[IFluidHandler] { + override def get = Robot.this + }) + if (isServer) { machine.setCostPerTick(Settings.get.robotCost) } - // ----------------------------------------------------------------------- // - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) - capability.cast(this.asInstanceOf[T]) + fluidCap.cast() else super.getCapability(capability, facing) } @@ -90,29 +97,29 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot val equipmentInventory = new InventoryProxy { override def inventory: Robot = Robot.this - override def getSizeInventory = 4 + override def getContainerSize = 4 } // Wrapper for the part of the inventory that is mutable. val mainInventory = new InventoryProxy { override def inventory: Robot = Robot.this - override def getSizeInventory: Int = Robot.this.inventorySize + override def getContainerSize: Int = Robot.this.inventorySize - override def offset: Int = equipmentInventory.getSizeInventory + override def offset: Int = equipmentInventory.getContainerSize } val actualInventorySize = 100 - def maxInventorySize: Int = actualInventorySize - equipmentInventory.getSizeInventory - componentCount + def maxInventorySize: Int = actualInventorySize - equipmentInventory.getContainerSize - componentCount var inventorySize: Int = -1 var selectedSlot = 0 override def setSelectedSlot(index: Int): Unit = { - selectedSlot = index max 0 min mainInventory.getSizeInventory - 1 - if (getWorld != null) { + selectedSlot = index max 0 min mainInventory.getContainerSize - 1 + if (getLevel != null) { ServerPacketSender.sendRobotSelectedSlotChange(this) } } @@ -136,12 +143,12 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot override def player: Player = { agent.Player.updatePositionAndRotation(player_, facing, facing) - agent.Player.setInventoryPlayerItems(player_) + agent.Player.setPlayerInventoryItems(player_) player_ } - override def synchronizeSlot(slot: Int): Unit = if (slot >= 0 && slot < getSizeInventory) this.synchronized { - val stack = getStackInSlot(slot) + override def synchronizeSlot(slot: Int): Unit = if (slot >= 0 && slot < getContainerSize) this.synchronized { + val stack = getItem(slot) components(slot) match { case Some(component) => // We're guaranteed to have a driver for entries. @@ -153,9 +160,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot def containerSlots: Range.Inclusive = 1 to info.containers.length - def componentSlots: Range = getSizeInventory - componentCount until getSizeInventory + def componentSlots: Range = getContainerSize - componentCount until getContainerSize - def inventorySlots: Range = equipmentInventory.getSizeInventory until (equipmentInventory.getSizeInventory + mainInventory.getSizeInventory) + def inventorySlots: Range = equipmentInventory.getContainerSize until (equipmentInventory.getContainerSize + mainInventory.getContainerSize) def setLightColor(value: Int): Unit = { info.lightColor = value @@ -196,17 +203,17 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot override def setName(name: String): Unit = info.name = name - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { - player.sendMessage(Localization.Analyzer.RobotOwner(ownerName)) - player.sendMessage(Localization.Analyzer.RobotName(player_.getName)) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + player.sendMessage(Localization.Analyzer.RobotOwner(ownerName), Util.NIL_UUID) + player.sendMessage(Localization.Analyzer.RobotName(player_.getName.getString), Util.NIL_UUID) MinecraftForge.EVENT_BUS.post(new RobotAnalyzeEvent(this, player)) super.onAnalyze(player, side, hitX, hitY, hitZ) } - def move(direction: EnumFacing): Boolean = { - val oldPosition = getPos - val newPosition = oldPosition.offset(direction) - if (!getWorld.isBlockLoaded(newPosition)) { + def move(direction: Direction): Boolean = { + val oldPosition = getBlockPos + val newPosition = oldPosition.relative(direction) + if (!getLevel.isLoaded(newPosition)) { return false // Don't fall off the earth. } @@ -218,10 +225,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot val blockRobotProxy = api.Items.get(Constants.BlockName.Robot).block.asInstanceOf[common.block.RobotProxy] val blockRobotAfterImage = api.Items.get(Constants.BlockName.RobotAfterimage).block.asInstanceOf[common.block.RobotAfterimage] - val wasAir = getWorld.isAirBlock(newPosition) - val state = getWorld.getBlockState(newPosition) + val wasAir = getLevel.isEmptyBlock(newPosition) + val state = getLevel.getBlockState(newPosition) val block = state.getBlock - val metadata = block.getMetaFromState(state) try { // Setting this will make the tile entity created via the following call // to setBlock to re-use our "real" instance as the inner object, instead @@ -231,16 +237,16 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // worked before the client is notified so that we can use the same trick on // the client by sending a corresponding packet. This also saves us from // having to send the complete state again (e.g. screen buffer) each move. - getWorld.setBlockToAir(newPosition) + getLevel.setBlockAndUpdate(newPosition, Blocks.AIR.defaultBlockState) // In some cases (though I couldn't quite figure out which one) setBlock // will return true, even though the block was not created / adjusted. - val created = getWorld.setBlockState(newPosition, getWorld.getBlockState(oldPosition), 1) && - getWorld.getTileEntity(newPosition) == proxy + val created = getLevel.setBlock(newPosition, getLevel.getBlockState(oldPosition), 1) && + getLevel.getBlockEntity(newPosition) == proxy if (created) { - assert(getPos == newPosition) - getWorld.setBlockState(oldPosition, net.minecraft.init.Blocks.AIR.getDefaultState, 1) - getWorld.setBlockState(oldPosition, blockRobotAfterImage.getDefaultState, 1) - assert(getWorld.getBlockState(oldPosition).getBlock == blockRobotAfterImage) + assert(getBlockPos == newPosition) + getLevel.setBlock(oldPosition, Blocks.AIR.defaultBlockState, 1) + getLevel.setBlock(oldPosition, blockRobotAfterImage.defaultBlockState, 1) + assert(getLevel.getBlockState(oldPosition).getBlock == blockRobotAfterImage) // Here instead of Lua callback so that it gets called on client, too. val moveTicks = math.max((Settings.get.moveDelay * 20).toInt, 1) setAnimateMove(oldPosition, moveTicks) @@ -253,29 +259,24 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // If we broke some replaceable block (like grass) play its break sound. if (!wasAir) { if (block != Blocks.AIR && block != blockRobotAfterImage) { - if (FluidRegistry.lookupFluidForBlock(block) == null && - !block.isInstanceOf[BlockFluidBase] && - !block.isInstanceOf[BlockLiquid]) { - getWorld.playEvent(2001, newPosition, Block.getIdFromBlock(block) + (metadata << 12)) + if (!state.getFluidState.isEmpty) { + getLevel.playLocalSound(newPosition.getX + 0.5, newPosition.getY + 0.5, newPosition.getZ + 0.5, SoundEvents.WATER_AMBIENT, SoundCategory.BLOCKS, + getLevel.random.nextFloat * 0.25f + 0.75f, getLevel.random.nextFloat * 1.0f + 0.5f, false) } - else { - val sx = newPosition.getX + 0.5 - val sy = newPosition.getY + 0.5 - val sz = newPosition.getZ + 0.5 - getWorld.playSound(sx, sy, sz, SoundEvents.BLOCK_WATER_AMBIENT, SoundCategory.BLOCKS, - getWorld.rand.nextFloat * 0.25f + 0.75f, getWorld.rand.nextFloat * 1.0f + 0.5f, false) + if (!block.isInstanceOf[FlowingFluidBlock]) { + getLevel.levelEvent(2001, newPosition, Block.getId(state)) } } } - getWorld.notifyBlockUpdate(oldPosition) - getWorld.notifyBlockUpdate(newPosition) + getLevel.notifyBlockUpdate(oldPosition) + getLevel.notifyBlockUpdate(newPosition) } - assert(!isInvalid) + assert(!isRemoved) } else { - getWorld.setBlockToAir(newPosition) + getLevel.setBlockAndUpdate(newPosition, Blocks.AIR.defaultBlockState) } - created && this.pos == newPosition + created && getBlockPos == newPosition } finally { blockRobotProxy.moving.set(None) @@ -327,16 +328,6 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // ----------------------------------------------------------------------- // - override def shouldRenderInPass(pass: Int) = true - - override def getRenderBoundingBox: AxisAlignedBB = - if (getBlockType != null && getWorld != null) - getBlockType.getCollisionBoundingBox(getWorld.getBlockState(getPos), getWorld, getPos).grow(0.5, 0.5, 0.5).offset(getPos) - else - new AxisAlignedBB(0, 0, 0, 1, 1, 1).offset(getPos) - - // ----------------------------------------------------------------------- // - override def updateEntity() { if (animationTicksLeft > 0) { animationTicksLeft -= 1 @@ -348,7 +339,7 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot } super.updateEntity() if (isServer) { - if (getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (getLevel.getGameTime % Settings.get.tickFrequency == 0) { if (info.tier == 3) { bot.node.changeBuffer(Double.PositiveInfinity) } @@ -360,8 +351,8 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot } if (!appliedToolEnchantments) { appliedToolEnchantments = true - StackOption(getStackInSlot(0)) match { - case SomeStack(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)) + StackOption(getItem(0)) match { + case SomeStack(item) => player_.getAttributes.addTransientAttributeModifiers(item.getAttributeModifiers(EquipmentSlotType.MAINHAND)) case _ => } } @@ -370,9 +361,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot client.Sound.updatePosition(this) } - for (slot <- 0 until equipmentInventory.getSizeInventory + mainInventory.getSizeInventory) { - getStackInSlot(slot) match { - case stack: ItemStack => try stack.updateAnimation(getWorld, if (!getWorld.isRemote) player_ else null, slot, slot == 0) catch { + for (slot <- 0 until equipmentInventory.getContainerSize + mainInventory.getContainerSize) { + getItem(slot) match { + case stack: ItemStack => try stack.inventoryTick(getLevel, if (!getLevel.isClientSide) player_ else null, slot, slot == 0) catch { case ignored: NullPointerException => // Client side item updates that need a player instance... } case _ => @@ -401,9 +392,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot override def dispose() { super.dispose() if (isClient) { - Minecraft.getMinecraft.currentScreen match { + Minecraft.getInstance.screen match { case robotGui: gui.Robot if robotGui.robot == this => - Minecraft.getMinecraft.displayGuiScreen(null) + robotGui.onClose() case _ => } } @@ -425,28 +416,28 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot private final val SwingingToolTag = Settings.namespace + "swingingTool" private final val TurnAxisTag = Settings.namespace + "turnAxis" - override def readFromNBTForServer(nbt: NBTTagCompound) { + override def loadForServer(nbt: CompoundNBT) { updateInventorySize() machine.onHostChanged() - bot.load(nbt.getCompoundTag(RobotTag)) - if (nbt.hasKey(OwnerTag)) { + bot.loadData(nbt.getCompound(RobotTag)) + if (nbt.contains(OwnerTag)) { ownerName = nbt.getString(OwnerTag) } - if (nbt.hasKey(OwnerUUIDTag)) { + if (nbt.contains(OwnerUUIDTag)) { ownerUUID = UUID.fromString(nbt.getString(OwnerUUIDTag)) } if (inventorySize > 0) { - selectedSlot = nbt.getInteger(SelectedSlotTag) max 0 min mainInventory.getSizeInventory - 1 + selectedSlot = nbt.getInt(SelectedSlotTag) max 0 min mainInventory.getContainerSize - 1 } - selectedTank = nbt.getInteger(SelectedTankTag) - animationTicksTotal = nbt.getInteger(AnimationTicksTotalTag) - animationTicksLeft = nbt.getInteger(AnimationTicksLeftTag) + selectedTank = nbt.getInt(SelectedTankTag) + animationTicksTotal = nbt.getInt(AnimationTicksTotalTag) + animationTicksLeft = nbt.getInt(AnimationTicksLeftTag) if (animationTicksLeft > 0) { - if (nbt.hasKey(MoveFromXTag)) { - val moveFromX = nbt.getInteger(MoveFromXTag) - val moveFromY = nbt.getInteger(MoveFromYTag) - val moveFromZ = nbt.getInteger(MoveFromZTag) + if (nbt.contains(MoveFromXTag)) { + val moveFromX = nbt.getInt(MoveFromXTag) + val moveFromY = nbt.getInt(MoveFromYTag) + val moveFromZ = nbt.getInt(MoveFromZTag) moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) } swingingTool = nbt.getBoolean(SwingingToolTag) @@ -460,47 +451,47 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot } // Side check for Waila (and other mods that may call this client side). - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = if (isServer) this.synchronized { - info.save(nbt) + override def saveForServer(nbt: CompoundNBT): Unit = if (isServer) this.synchronized { + info.saveData(nbt) - // Note: computer is saved when proxy is saved (in proxy's super writeToNBT) + // Note: computer is saved when proxy is saved (in proxy's super save) // which is a bit ugly, and may be refactored some day, but it works. - nbt.setNewCompoundTag(RobotTag, bot.save) - nbt.setString(OwnerTag, ownerName) - nbt.setString(OwnerUUIDTag, ownerUUID.toString) - nbt.setInteger(SelectedSlotTag, selectedSlot) - nbt.setInteger(SelectedTankTag, selectedTank) + nbt.setNewCompoundTag(RobotTag, bot.saveData) + nbt.putString(OwnerTag, ownerName) + nbt.putString(OwnerUUIDTag, ownerUUID.toString) + nbt.putInt(SelectedSlotTag, selectedSlot) + nbt.putInt(SelectedTankTag, selectedTank) if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { - nbt.setInteger(AnimationTicksTotalTag, animationTicksTotal) - nbt.setInteger(AnimationTicksLeftTag, animationTicksLeft) + nbt.putInt(AnimationTicksTotalTag, animationTicksTotal) + nbt.putInt(AnimationTicksLeftTag, animationTicksLeft) moveFrom match { case Some(blockPos) => - nbt.setInteger(MoveFromXTag, blockPos.getX) - nbt.setInteger(MoveFromYTag, blockPos.getY) - nbt.setInteger(MoveFromZTag, blockPos.getZ) + nbt.putInt(MoveFromXTag, blockPos.getX) + nbt.putInt(MoveFromYTag, blockPos.getY) + nbt.putInt(MoveFromZTag, blockPos.getZ) case _ => } - nbt.setBoolean(SwingingToolTag, swingingTool) - nbt.setByte(TurnAxisTag, turnAxis.toByte) + nbt.putBoolean(SwingingToolTag, swingingTool) + nbt.putByte(TurnAxisTag, turnAxis.toByte) } } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - load(nbt) - info.load(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + loadData(nbt) + info.loadData(nbt) updateInventorySize() - selectedSlot = nbt.getInteger(SelectedSlotTag) - animationTicksTotal = nbt.getInteger(AnimationTicksTotalTag) - animationTicksLeft = nbt.getInteger(AnimationTicksLeftTag) + selectedSlot = nbt.getInt(SelectedSlotTag) + animationTicksTotal = nbt.getInt(AnimationTicksTotalTag) + animationTicksLeft = nbt.getInt(AnimationTicksLeftTag) if (animationTicksLeft > 0) { - if (nbt.hasKey(MoveFromXTag)) { - val moveFromX = nbt.getInteger(MoveFromXTag) - val moveFromY = nbt.getInteger(MoveFromYTag) - val moveFromZ = nbt.getInteger(MoveFromZTag) + if (nbt.contains(MoveFromXTag)) { + val moveFromX = nbt.getInt(MoveFromXTag) + val moveFromY = nbt.getInt(MoveFromYTag) + val moveFromZ = nbt.getInt(MoveFromZTag) moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) } swingingTool = nbt.getBoolean(SwingingToolTag) @@ -509,24 +500,24 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot connectComponents() } - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = this.synchronized { - super.writeToNBTForClient(nbt) - save(nbt) - info.save(nbt) + override def saveForClient(nbt: CompoundNBT): Unit = this.synchronized { + super.saveForClient(nbt) + saveData(nbt) + info.saveData(nbt) - nbt.setInteger(SelectedSlotTag, selectedSlot) + nbt.putInt(SelectedSlotTag, selectedSlot) if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { - nbt.setInteger(AnimationTicksTotalTag, animationTicksTotal) - nbt.setInteger(AnimationTicksLeftTag, animationTicksLeft) + nbt.putInt(AnimationTicksTotalTag, animationTicksTotal) + nbt.putInt(AnimationTicksLeftTag, animationTicksLeft) moveFrom match { case Some(blockPos) => - nbt.setInteger(MoveFromXTag, blockPos.getX) - nbt.setInteger(MoveFromYTag, blockPos.getY) - nbt.setInteger(MoveFromZTag, blockPos.getZ) + nbt.putInt(MoveFromXTag, blockPos.getX) + nbt.putInt(MoveFromYTag, blockPos.getY) + nbt.putInt(MoveFromZTag, blockPos.getZ) case _ => } - nbt.setBoolean(SwingingToolTag, swingingTool) - nbt.setByte(TurnAxisTag, turnAxis.toByte) + nbt.putBoolean(SwingingToolTag, swingingTool) + nbt.putByte(TurnAxisTag, turnAxis.toByte) } } @@ -556,7 +547,7 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot override protected def onItemAdded(slot: Int, stack: ItemStack) { if (isServer) { if (isToolSlot(slot)) { - player_.getAttributeMap.applyAttributeModifiers(stack.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)) + player_.getAttributes.addTransientAttributeModifiers(stack.getAttributeModifiers(EquipmentSlotType.MAINHAND)) ServerPacketSender.sendRobotInventory(this, slot, stack) } if (isUpgradeSlot(slot)) { @@ -567,10 +558,10 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot } if (isComponentSlot(slot, stack)) { super.onItemAdded(slot, stack) - getWorld.notifyBlocksOfNeighborChange(position, getBlockType, updateObservers = false) + getLevel.notifyBlocksOfNeighborChange(position, getBlockState.getBlock, updateObservers = false) } if (isInventorySlot(slot)) { - machine.signal("inventory_changed", Int.box(slot - equipmentInventory.getSizeInventory + 1)) + machine.signal("inventory_changed", Int.box(slot - equipmentInventory.getContainerSize + 1)) } } else super.onItemAdded(slot, stack) @@ -580,7 +571,7 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot super.onItemRemoved(slot, stack) if (isServer) { if (isToolSlot(slot)) { - player_.getAttributeMap.removeAttributeModifiers(stack.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)) + player_.getAttributes.removeAttributeModifiers(stack.getAttributeModifiers(EquipmentSlotType.MAINHAND)) ServerPacketSender.sendRobotInventory(this, slot, ItemStack.EMPTY) } if (isUpgradeSlot(slot)) { @@ -590,16 +581,16 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot common.Sound.playDiskEject(this) } if (isInventorySlot(slot)) { - machine.signal("inventory_changed", Int.box(slot - equipmentInventory.getSizeInventory + 1)) + machine.signal("inventory_changed", Int.box(slot - equipmentInventory.getContainerSize + 1)) } if (isComponentSlot(slot, stack)) { - getWorld.notifyBlocksOfNeighborChange(position, getBlockType, updateObservers = false) + getLevel.notifyBlocksOfNeighborChange(position, getBlockState.getBlock, updateObservers = false) } } } - override def markDirty() { - super.markDirty() + override def setChanged() { + super.setChanged() // Avoid getting into a bad state on the client when updating before we // got the descriptor packet from the server. If we manage to open the // GUI before the descriptor packet arrived, close it again because it is @@ -608,9 +599,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot updateInventorySize() } else if (isClient) { - Minecraft.getMinecraft.currentScreen match { + Minecraft.getInstance.screen match { case robotGui: gui.Robot if robotGui.robot == this => - Minecraft.getMinecraft.displayGuiScreen(null) + robotGui.onClose() case _ => } } @@ -665,8 +656,8 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot def isInventorySlot(slot: Int): Boolean = inventorySlots contains slot - def isFloppySlot(slot: Int): Boolean = !getStackInSlot(slot).isEmpty && isComponentSlot(slot, getStackInSlot(slot)) && { - val stack = getStackInSlot(slot) + def isFloppySlot(slot: Int): Boolean = !getItem(slot).isEmpty && isComponentSlot(slot, getItem(slot)) && { + val stack = getItem(slot) Option(Driver.driverFor(stack, getClass)) match { case Some(driver) => driver.slot(stack) == Slot.Floppy case _ => false @@ -679,9 +670,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot override def componentSlot(address: String): Int = components.indexWhere(_.exists(env => env.node != null && env.node.address == address)) - override def hasRedstoneCard: Boolean = (containerSlots ++ componentSlots).exists(slot => StackOption(getStackInSlot(slot)).fold(false)(DriverRedstoneCard.worksWith(_, getClass))) + override def hasRedstoneCard: Boolean = (containerSlots ++ componentSlots).exists(slot => StackOption(getItem(slot)).fold(false)(DriverRedstoneCard.worksWith(_, getClass))) - private def computeInventorySize() = math.min(maxInventorySize, (containerSlots ++ componentSlots).foldLeft(0)((acc, slot) => acc + (StackOption(getStackInSlot(slot)) match { + private def computeInventorySize() = math.min(maxInventorySize, (containerSlots ++ componentSlots).foldLeft(0)((acc, slot) => acc + (StackOption(getItem(slot)) match { case SomeStack(stack) => Option(Driver.driverFor(stack, getClass)) match { case Some(driver: item.Inventory) => driver.inventoryCapacity(stack) case _ => 0 @@ -696,23 +687,23 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot val newInventorySize = computeInventorySize() if (newInventorySize != inventorySize) { inventorySize = newInventorySize - val realSize = equipmentInventory.getSizeInventory + mainInventory.getSizeInventory + val realSize = equipmentInventory.getContainerSize + mainInventory.getContainerSize val oldSelected = selectedSlot val removed = mutable.ArrayBuffer.empty[ItemStack] - for (slot <- realSize until getSizeInventory - componentCount) { - val stack = getStackInSlot(slot) - setInventorySlotContents(slot, ItemStack.EMPTY) + for (slot <- realSize until getContainerSize - componentCount) { + val stack = getItem(slot) + setItem(slot, ItemStack.EMPTY) if (!stack.isEmpty) removed += stack } - val copyComponentCount = math.min(getSizeInventory, componentCount) - Array.copy(components, getSizeInventory - copyComponentCount, components, realSize, copyComponentCount) - for (slot <- math.max(0, getSizeInventory - componentCount) until getSizeInventory if slot < realSize || slot >= realSize + componentCount) { + val copyComponentCount = math.min(getContainerSize, componentCount) + Array.copy(components, getContainerSize - copyComponentCount, components, realSize, copyComponentCount) + for (slot <- math.max(0, getContainerSize - componentCount) until getContainerSize if slot < realSize || slot >= realSize + componentCount) { components(slot) = None } - getSizeInventory = realSize + componentCount - if (getWorld != null && isServer) { + getContainerSize = realSize + componentCount + if (getLevel != null && isServer) { for (stack <- removed) { - player().inventory.addItemStackToInventory(stack) + player().inventory.add(stack) spawnStackInWorld(stack, Option(facing)) } setSelectedSlot(oldSelected) @@ -725,36 +716,36 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // ----------------------------------------------------------------------- // - var getSizeInventory: Int = actualInventorySize + var getContainerSize: Int = actualInventorySize - override def getInventoryStackLimit = 64 + override def getMaxStackSize = 64 - override def getStackInSlot(slot: Int): ItemStack = { - if (slot >= getSizeInventory) null // Required to always show 16 inventory slots in GUI. - else if (slot >= getSizeInventory - componentCount) { - info.components(slot - (getSizeInventory - componentCount)) + override def getItem(slot: Int): ItemStack = { + if (slot >= getContainerSize) null // Required to always show 16 inventory slots in GUI. + else if (slot >= getContainerSize - componentCount) { + info.components(slot - (getContainerSize - componentCount)) } - else super.getStackInSlot(slot) + else super.getItem(slot) } - override def setInventorySlotContents(slot: Int, stack: ItemStack) { - if (slot < getSizeInventory - componentCount && (isItemValidForSlot(slot, stack) || stack.isEmpty)) { + override def setItem(slot: Int, stack: ItemStack) { + if (slot < getContainerSize - componentCount && (canPlaceItem(slot, stack) || stack.isEmpty)) { if (!stack.isEmpty && stack.getCount > 1 && isComponentSlot(slot, stack)) { - super.setInventorySlotContents(slot, stack.splitStack(1)) + super.setItem(slot, stack.split(1)) if (stack.getCount > 0 && isServer) { - player().inventory.addItemStackToInventory(stack) + player().inventory.add(stack) spawnStackInWorld(stack, Option(facing)) } } - else super.setInventorySlotContents(slot, stack) + else super.setItem(slot, stack) } - else if (!stack.isEmpty && stack.getCount > 0 && !getWorld.isRemote) spawnStackInWorld(stack, Option(EnumFacing.UP)) + else if (!stack.isEmpty && stack.getCount > 0 && !getLevel.isClientSide) spawnStackInWorld(stack, Option(Direction.UP)) } - override def isUsableByPlayer(player: EntityPlayer): Boolean = - super.isUsableByPlayer(player) && (!isCreative || player.capabilities.isCreativeMode) + override def stillValid(player: PlayerEntity): Boolean = + super.stillValid(player) && (!isCreative || player.isCreative) - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack, getClass))) match { case (0, _) => true // Allow anything in the tool slot. case (i, Some(driver)) if isContainerSlot(i) => // Yay special cases! Dynamic screens kind of work, but are pretty derpy @@ -774,30 +765,30 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // ----------------------------------------------------------------------- // - override def dropSlot(slot: Int, count: Int, direction: Option[EnumFacing]): Boolean = - InventoryUtils.dropSlot(BlockPosition(x, y, z, getWorld), mainInventory, slot, count, direction) + override def dropSlot(slot: Int, count: Int, direction: Option[Direction]): Boolean = + InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), mainInventory, slot, count, direction) override def dropAllSlots(): Unit = { - InventoryUtils.dropSlot(BlockPosition(x, y, z, getWorld), this, 0, Int.MaxValue) + InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), this, 0, Int.MaxValue) for (slot <- containerSlots) { - InventoryUtils.dropSlot(BlockPosition(x, y, z, getWorld), this, slot, Int.MaxValue) + InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), this, slot, Int.MaxValue) } - InventoryUtils.dropAllSlots(BlockPosition(x, y, z, getWorld), mainInventory) + InventoryUtils.dropAllSlots(BlockPosition(x, y, z, getLevel), mainInventory) } // ----------------------------------------------------------------------- // - override def canExtractItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = - getSlotsForFace(side).contains(slot) + override def canTakeItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = + getSlotsForFace(side).toSeq.contains(slot) - override def canInsertItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = - getSlotsForFace(side).contains(slot) && - isItemValidForSlot(slot, stack) + override def canPlaceItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = + getSlotsForFace(side).toSeq.contains(slot) && + canPlaceItem(slot, stack) - override def getSlotsForFace(side: EnumFacing): Array[Int] = + override def getSlotsForFace(side: Direction): Array[Int] = toLocal(side) match { - case EnumFacing.WEST => Array(0) // Tool - case EnumFacing.EAST => containerSlots.toArray + case Direction.WEST => Array(0) // Tool + case Direction.EAST => containerSlots.toArray case _ => inventorySlots.toArray } @@ -820,43 +811,43 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot // ----------------------------------------------------------------------- // - override def fill(resource: FluidStack, doFill: Boolean): Int = + override def getTanks = tankCount + + override def getFluidInTank(tank: Int): FluidStack = tryGetTank(selectedTank) match { case Some(t) => - t.fill(resource, doFill) - case _ => 0 + t.getFluid + case _ => FluidStack.EMPTY } - override def drain(resource: FluidStack, doDrain: Boolean): FluidStack = + override def getTankCapacity(tank: Int): Int = tryGetTank(selectedTank) match { - case Some(t) if t.getFluid != null && t.getFluid.isFluidEqual(resource) => - t.drain(resource.amount, doDrain) - case _ => null + case Some(t) => + t.getCapacity + case _ => 0 } - override def drain(maxDrain: Int, doDrain: Boolean): FluidStack = { + override def isFluidValid(tank: Int, resource: FluidStack) = true + + override def fill(resource: FluidStack, action: FluidAction): Int = tryGetTank(selectedTank) match { case Some(t) => - t.drain(maxDrain, doDrain) - case _ => null + t.fill(resource, action) + case _ => 0 } - } - def canFill(fluid: Fluid): Boolean = { + override def drain(resource: FluidStack, action: FluidAction): FluidStack = tryGetTank(selectedTank) match { - case Some(t) => t.getFluid == null || t.getFluid.getFluid == fluid - case _ => false + case Some(t) if t.getFluid != null && t.getFluid.isFluidEqual(resource) => + t.drain(resource.getAmount, action) + case _ => FluidStack.EMPTY } - } - def canDrain(fluid: Fluid): Boolean = { + override def drain(maxDrain: Int, action: FluidAction): FluidStack = { tryGetTank(selectedTank) match { - case Some(t) => t.getFluid != null && t.getFluid.getFluid == fluid - case _ => false + case Some(t) => + t.drain(maxDrain, action) + case _ => FluidStack.EMPTY } } - - override def getTankProperties: Array[IFluidTankProperties] = FluidTankProperties.convert(components.collect { - case Some(t: IFluidTank) => t.getInfo - }).map(_.asInstanceOf[IFluidTankProperties]) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 815659d344..f04258dcb8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -15,30 +15,43 @@ import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.server.agent.Player import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier import net.minecraftforge.fluids.capability.CapabilityFluidHandler import net.minecraftforge.fluids.capability.IFluidHandler -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import net.minecraft.entity.Entity -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.Fluid import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB -import net.minecraftforge.fluids.Fluid +import net.minecraft.util.text.ITextComponent import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.IFluidTank -import net.minecraftforge.fluids.capability.IFluidTankProperties -class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInformation with traits.RotatableTile with ISidedInventory with IFluidHandler with internal.Robot { +class RobotProxy(val robot: Robot) extends TileEntity(null) with traits.Computer with traits.PowerInformation with traits.RotatableTile with ISidedInventory with IFluidHandler with internal.Robot { def this() = this(new Robot()) // ----------------------------------------------------------------------- // - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { + private val wrapper = LazyOptional.of(new NonNullSupplier[IFluidHandler] { + override def get = RobotProxy.this + }) + + override def invalidateCaps() { + super.invalidateCaps() + wrapper.invalidate() + } + + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) - capability.cast(this.asInstanceOf[T]) + wrapper.cast[T] else super.getCapability(capability, facing) } @@ -53,7 +66,7 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo override def equipmentInventory: InventoryProxy { def inventory: Robot - def getSizeInventory: Int + def getContainerSize: Int } = robot.equipmentInventory override def mainInventory: InventoryProxy { @@ -61,7 +74,7 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo def inventory: Robot - def getSizeInventory: Int + def getContainerSize: Int } = robot.mainInventory override def tank: MultiTank { @@ -149,20 +162,19 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo robot.updateEntity() } - override def validate() { - super.validate() + override def clearRemoved() { + super.clearRemoved() val firstProxy = robot.proxy == null robot.proxy = this - robot.setWorld(getWorld) - robot.setPos(getPos) + robot.setLevelAndPosition(getLevel, getBlockPos) if (firstProxy) { - robot.validate() + robot.clearRemoved() } if (isServer) { // Use the same address we use internally on the outside. - val nbt = new NBTTagCompound() - nbt.setString("address", robot.node.address) - node.load(nbt) + val nbt = new CompoundNBT() + nbt.putString("address", robot.node.address) + node.loadData(nbt) } } @@ -173,37 +185,36 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo } } - override def readFromNBTForServer(nbt: NBTTagCompound) { - robot.info.load(nbt) - super.readFromNBTForServer(nbt) - robot.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + robot.info.loadData(nbt) + super.loadForServer(nbt) + robot.loadForServer(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - robot.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + robot.saveForServer(nbt) } - override def save(nbt: NBTTagCompound): Unit = robot.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = robot.saveData(nbt) - override def load(nbt: NBTTagCompound): Unit = robot.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = robot.loadData(nbt) - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound): Unit = robot.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT): Unit = robot.loadForClient(nbt) - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = robot.writeToNBTForClient(nbt) + override def saveForClient(nbt: CompoundNBT): Unit = robot.saveForClient(nbt) - override def getMaxRenderDistanceSquared: Double = robot.getMaxRenderDistanceSquared + @OnlyIn(Dist.CLIENT) + override def getViewDistance: Double = robot.getViewDistance override def getRenderBoundingBox: AxisAlignedBB = robot.getRenderBoundingBox - override def shouldRenderInPass(pass: Int): Boolean = robot.shouldRenderInPass(pass) - - override def markDirty(): Unit = robot.markDirty() + override def setChanged(): Unit = robot.setChanged() // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = robot.onAnalyze(player, side, hitX, hitY, hitZ) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = robot.onAnalyze(player, side, hitX, hitY, hitZ) // ----------------------------------------------------------------------- // @@ -225,67 +236,67 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo // ----------------------------------------------------------------------- // - override def pitch: EnumFacing = robot.pitch + override def pitch: Direction = robot.pitch - override def pitch_=(value: EnumFacing): Unit = robot.pitch_=(value) + override def pitch_=(value: Direction): Unit = robot.pitch_=(value) - override def yaw: EnumFacing = robot.yaw + override def yaw: Direction = robot.yaw - override def yaw_=(value: EnumFacing): Unit = robot.yaw_=(value) + override def yaw_=(value: Direction): Unit = robot.yaw_=(value) override def setFromEntityPitchAndYaw(entity: Entity): Boolean = robot.setFromEntityPitchAndYaw(entity) - override def setFromFacing(value: EnumFacing): Boolean = robot.setFromFacing(value) + override def setFromFacing(value: Direction): Boolean = robot.setFromFacing(value) override def invertRotation(): Boolean = robot.invertRotation() - override def facing: EnumFacing = robot.facing + override def facing: Direction = robot.facing - override def rotate(axis: EnumFacing): Boolean = robot.rotate(axis) + override def rotate(axis: Direction): Boolean = robot.rotate(axis) - override def toLocal(value: EnumFacing): EnumFacing = robot.toLocal(value) + override def toLocal(value: Direction): Direction = robot.toLocal(value) - override def toGlobal(value: EnumFacing): EnumFacing = robot.toGlobal(value) + override def toGlobal(value: Direction): Direction = robot.toGlobal(value) // ----------------------------------------------------------------------- // - override def getStackInSlot(i: Int): ItemStack = robot.getStackInSlot(i) + override def getItem(i: Int): ItemStack = robot.getItem(i) - override def decrStackSize(slot: Int, amount: Int): ItemStack = robot.decrStackSize(slot, amount) + override def removeItem(slot: Int, amount: Int): ItemStack = robot.removeItem(slot, amount) - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = robot.setInventorySlotContents(slot, stack) + override def setItem(slot: Int, stack: ItemStack): Unit = robot.setItem(slot, stack) - override def removeStackFromSlot(slot: Int): ItemStack = robot.removeStackFromSlot(slot) + override def removeItemNoUpdate(slot: Int): ItemStack = robot.removeItemNoUpdate(slot) - override def openInventory(player: EntityPlayer): Unit = robot.openInventory(player) + override def startOpen(player: PlayerEntity): Unit = robot.startOpen(player) - override def closeInventory(player: EntityPlayer): Unit = robot.closeInventory(player) + override def stopOpen(player: PlayerEntity): Unit = robot.stopOpen(player) override def hasCustomName: Boolean = robot.hasCustomName - override def isUsableByPlayer(player: EntityPlayer): Boolean = robot.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity): Boolean = robot.stillValid(player) - override def dropSlot(slot: Int, count: Int, direction: Option[EnumFacing]): Boolean = robot.dropSlot(slot, count, direction) + override def dropSlot(slot: Int, count: Int, direction: Option[Direction]): Boolean = robot.dropSlot(slot, count, direction) override def dropAllSlots(): Unit = robot.dropAllSlots() - override def getInventoryStackLimit: Int = robot.getInventoryStackLimit + override def getMaxStackSize: Int = robot.getMaxStackSize override def componentSlot(address: String): Int = robot.componentSlot(address) - override def getName: String = robot.getName + override def getName: ITextComponent = robot.getName - override def getSizeInventory: Int = robot.getSizeInventory + override def getContainerSize: Int = robot.getContainerSize - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = robot.isItemValidForSlot(slot, stack) + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = robot.canPlaceItem(slot, stack) // ----------------------------------------------------------------------- // - override def canExtractItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = robot.canExtractItem(slot, stack, side) + override def canTakeItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = robot.canTakeItemThroughFace(slot, stack, side) - override def canInsertItem(slot: Int, stack: ItemStack, side: EnumFacing): Boolean = robot.canInsertItem(slot, stack, side) + override def canPlaceItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = robot.canPlaceItemThroughFace(slot, stack, side) - override def getSlotsForFace(side: EnumFacing): Array[Int] = robot.getSlotsForFace(side) + override def getSlotsForFace(side: Direction): Array[Int] = robot.getSlotsForFace(side) // ----------------------------------------------------------------------- // @@ -303,15 +314,17 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo // ----------------------------------------------------------------------- // - override def fill(resource: FluidStack, doFill: Boolean): Int = robot.fill(resource, doFill) + override def getTanks: Int = robot.getTanks + + override def getFluidInTank(tank: Int): FluidStack = robot.getFluidInTank(tank) - override def drain(resource: FluidStack, doDrain: Boolean): FluidStack = robot.drain(resource, doDrain) + override def getTankCapacity(tank: Int): Int = robot.getTankCapacity(tank) - override def drain(maxDrain: Int, doDrain: Boolean): FluidStack = robot.drain(maxDrain, doDrain) + override def isFluidValid(tank: Int, resource: FluidStack): Boolean = robot.isFluidValid(tank, resource) - def canFill(fluid: Fluid): Boolean = robot.canFill(fluid) + override def fill(resource: FluidStack, action: FluidAction): Int = robot.fill(resource, action) - def canDrain(fluid: Fluid): Boolean = robot.canDrain(fluid) + override def drain(resource: FluidStack, action: FluidAction): FluidStack = robot.drain(resource, action) - override def getTankProperties: Array[IFluidTankProperties] = robot.getTankProperties + override def drain(maxDrain: Int, action: FluidAction): FluidStack = robot.drain(maxDrain, action) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 28233912ab..338d3c3e26 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -5,30 +5,31 @@ import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network._ import li.cil.oc.client.gui import li.cil.oc.common.component.TextBuffer -import li.cil.oc.util.BlockPosition import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs +import li.cil.oc.util.BlockPosition import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import net.minecraft.client.Minecraft import net.minecraft.entity.Entity -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.projectile.EntityArrow -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.projectile.ArrowEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable import scala.language.postfixOps -class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] { +class Screen(var tier: Int) extends TileEntity(null) with traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] { def this() = this(0) // Enable redstone functionality. _isOutputEnabled = true - override def validFacings = EnumFacing.values + override def validFacings = Direction.values // ----------------------------------------------------------------------- // @@ -57,17 +58,17 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with var invertTouchMode = false - private val arrows = mutable.Set.empty[EntityArrow] + private val arrows = mutable.Set.empty[ArrowEntity] private val lastWalked = mutable.WeakHashMap.empty[Entity, (Int, Int)] setColor(Color.rgbValues(Color.byTier(tier))) - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = side != facing + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = side != facing // Allow connections from front for keyboards, and keyboards only... - override def sidedNode(side: EnumFacing) = if (side != facing || (getWorld.isBlockLoaded(getPos.offset(side)) && getWorld.getTileEntity(getPos.offset(side)).isInstanceOf[Keyboard])) node else null + override def sidedNode(side: Direction) = if (side != facing || (getLevel.isLoaded(getBlockPos.relative(side)) && getLevel.getBlockEntity(getBlockPos.relative(side)).isInstanceOf[Keyboard])) node else null // ----------------------------------------------------------------------- // @@ -80,9 +81,9 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } def hasKeyboard = screens.exists(screen => - EnumFacing.values.map(side => (side, { + Direction.values.map(side => (side, { val blockPos = BlockPosition(screen).offset(side) - if (getWorld.blockExists(blockPos)) getWorld.getTileEntity(blockPos) + if (getLevel.blockExists(blockPos)) getLevel.getBlockEntity(blockPos) else null })).exists { case (side, keyboard: Keyboard) => keyboard.hasNodeOnSide(side.getOpposite) @@ -102,8 +103,8 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with def toScreenCoordinates(hitX: Double, hitY: Double, hitZ: Double): (Boolean, Option[(Double, Double)]) = { // Compute absolute position of the click on the face, measured in blocks. - def dot(f: EnumFacing) = f.getFrontOffsetX * hitX + f.getFrontOffsetY * hitY + f.getFrontOffsetZ * hitZ - val (hx, hy) = (dot(toGlobal(EnumFacing.EAST)), dot(toGlobal(EnumFacing.UP))) + def dot(f: Direction) = f.getStepX * hitX + f.getStepY * hitY + f.getStepZ * hitZ + val (hx, hy) = (dot(toGlobal(Direction.EAST)), dot(toGlobal(Direction.UP))) val tx = if (hx < 0) 1 + hx else hx val ty = 1 - (if (hy < 0) 1 + hy else hy) val (lx, ly) = localPosition @@ -114,7 +115,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with if (ax <= border || ay <= border || ax >= width - border || ay >= height - border) { return (false, None) } - if (!getWorld.isRemote) return (true, None) + if (!getLevel.isClientSide) return (true, None) val (iw, ih) = (width - border * 2, height - border * 2) val (rx, ry) = ((ax - border) / iw, (ay - border) / ih) @@ -170,7 +171,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with origin.lastWalked.put(entity, localPosition) match { case Some((oldX, oldY)) if oldX == x && oldY == y => // Ignore case _ => entity match { - case player: EntityPlayer if Settings.get.inputUsername => + case player: PlayerEntity if Settings.get.inputUsername => origin.node.sendToReachable("computer.signal", "walk", Int.box(x + 1), Int.box(height - y), player.getName) case _ => origin.node.sendToReachable("computer.signal", "walk", Int.box(x + 1), Int.box(height - y)) @@ -178,7 +179,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } } - def shot(arrow: EntityArrow) { + def shot(arrow: ArrowEntity) { arrows.add(arrow) } @@ -198,7 +199,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with val lpos = project(current) def tryQueue(dx: Int, dy: Int) { val npos = unproject(lpos.x + dx, lpos.y + dy, lpos.z) - if (getWorld.blockExists(npos)) getWorld.getTileEntity(npos) match { + if (getLevel.blockExists(npos)) getLevel.getBlockEntity(npos) match { case s: Screen if s.pitch == pitch && s.yaw == yaw && pending.add(s) => queue += s case _ => // Ignore. } @@ -218,11 +219,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with pending.remove(screen) queue += screen } - if (isClient) { - val bounds = current.origin.getRenderBoundingBox - getWorld.markBlockRangeForRenderUpdate(bounds.minX.toInt, bounds.minY.toInt, bounds.minZ.toInt, - bounds.maxX.toInt, bounds.maxY.toInt, bounds.maxZ.toInt) - } } // Update visibility after everything is done, to avoid noise. queue.foreach(screen => { @@ -250,11 +246,11 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } if (arrows.nonEmpty) { for (arrow <- arrows) { - val hitX = arrow.posX - x - val hitY = arrow.posY - y - val hitZ = arrow.posZ - z - arrow.shootingEntity match { - case player: EntityPlayer if player == Minecraft.getMinecraft.player => click(hitX, hitY, hitZ) + val hitX = arrow.getX - x + val hitY = arrow.getY - y + val hitZ = arrow.getZ - z + arrow.getOwner match { + case player: PlayerEntity if player == Minecraft.getInstance.player => click(hitX, hitY, hitZ) case _ => } } @@ -271,9 +267,8 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with super.dispose() screens.clone().foreach(_.checkMultiBlock()) if (isClient) { - Minecraft.getMinecraft.currentScreen match { - case screenGui: gui.Screen if screenGui.buffer == buffer => - Minecraft.getMinecraft.displayGuiScreen(null) + Minecraft.getInstance.screen match { + case screenGui: gui.Screen if screenGui.buffer == buffer => screenGui.onClose() case _ => } } @@ -290,37 +285,37 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with private final val HadRedstoneInputTag = Settings.namespace + "hadRedstoneInput" private final val InvertTouchModeTag = Settings.namespace + "invertTouchMode" - override def readFromNBTForServer(nbt: NBTTagCompound) { + override def loadForServer(nbt: CompoundNBT) { tier = nbt.getByte(TierTag) max 0 min 2 setColor(Color.rgbValues(Color.byTier(tier))) - super.readFromNBTForServer(nbt) + super.loadForServer(nbt) hadRedstoneInput = nbt.getBoolean(HadRedstoneInputTag) invertTouchMode = nbt.getBoolean(InvertTouchModeTag) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - nbt.setByte(TierTag, tier.toByte) - super.writeToNBTForServer(nbt) - nbt.setBoolean(HadRedstoneInputTag, hadRedstoneInput) - nbt.setBoolean(InvertTouchModeTag, invertTouchMode) + override def saveForServer(nbt: CompoundNBT) { + nbt.putByte(TierTag, tier.toByte) + super.saveForServer(nbt) + nbt.putBoolean(HadRedstoneInputTag, hadRedstoneInput) + nbt.putBoolean(InvertTouchModeTag, invertTouchMode) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound) { + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT) { tier = nbt.getByte(TierTag) max 0 min 2 - super.readFromNBTForClient(nbt) + super.loadForClient(nbt) invertTouchMode = nbt.getBoolean(InvertTouchModeTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - nbt.setByte(TierTag, tier.toByte) - super.writeToNBTForClient(nbt) - nbt.setBoolean(InvertTouchModeTag, invertTouchMode) + override def saveForClient(nbt: CompoundNBT) { + nbt.putByte(TierTag, tier.toByte) + super.saveForClient(nbt) + nbt.putBoolean(InvertTouchModeTag, invertTouchMode) } // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) override def getRenderBoundingBox = if ((width == 1 && height == 1) || !isOrigin) super.getRenderBoundingBox else cachedBounds match { @@ -338,12 +333,12 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with b } - @SideOnly(Side.CLIENT) - override def getMaxRenderDistanceSquared = if (isOrigin) super.getMaxRenderDistanceSquared else 0 + @OnlyIn(Dist.CLIENT) + override def getViewDistance = if (isOrigin) super.getViewDistance else 0 // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(origin.node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(origin.node) override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) { super.onRedstoneInputChanged(args) @@ -374,7 +369,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with val opos = project(origin) def tryMergeTowards(dx: Int, dy: Int) = { val npos = unproject(opos.x + dx, opos.y + dy, opos.z) - getWorld.blockExists(npos) && (getWorld.getTileEntity(npos) match { + getLevel.blockExists(npos) && (getLevel.getBlockEntity(npos) match { case s: Screen if s.tier == tier && s.pitch == pitch && s.getColor == getColor && s.yaw == yaw && !screens.contains(s) => val spos = project(s.origin) val canMergeAlongX = spos.y == opos.y && s.height == height && s.width + width <= Settings.get.maxScreenWidth @@ -408,12 +403,12 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } private def project(t: Screen) = { - def dot(f: EnumFacing, s: Screen) = f.getFrontOffsetX * s.x + f.getFrontOffsetY * s.y + f.getFrontOffsetZ * s.z - BlockPosition(dot(toGlobal(EnumFacing.EAST), t), dot(toGlobal(EnumFacing.UP), t), dot(toGlobal(EnumFacing.SOUTH), t)) + def dot(f: Direction, s: Screen) = f.getStepX * s.x + f.getStepY * s.y + f.getStepZ * s.z + BlockPosition(dot(toGlobal(Direction.EAST), t), dot(toGlobal(Direction.UP), t), dot(toGlobal(Direction.SOUTH), t)) } private def unproject(x: Int, y: Int, z: Int) = { - def dot(f: EnumFacing) = f.getFrontOffsetX * x + f.getFrontOffsetY * y + f.getFrontOffsetZ * z - BlockPosition(dot(toLocal(EnumFacing.EAST)), dot(toLocal(EnumFacing.UP)), dot(toLocal(EnumFacing.SOUTH))) + def dot(f: Direction) = f.getStepX * x + f.getStepY * y + f.getStepZ * z + BlockPosition(dot(toLocal(Direction.EAST)), dot(toLocal(Direction.UP)), dot(toLocal(Direction.SOUTH))) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala index 78a7fc8e9e..d542235949 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala @@ -1,9 +1,10 @@ package li.cil.oc.common.tileentity import li.cil.oc.server.component -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.TileEntity -class Transposer extends traits.Environment { +class Transposer extends TileEntity(null) with traits.Environment { val transposer = new component.Transposer.Block(this) def node = transposer.node @@ -11,13 +12,13 @@ class Transposer extends traits.Environment { // Used on client side to check whether to render activity indicators. var lastOperation = 0L - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - transposer.load(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + transposer.loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - transposer.save(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + transposer.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala b/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala index 058a291765..92d6c0f278 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala @@ -8,19 +8,21 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Visibility import li.cil.oc.common.EventHandler import li.cil.oc.server.network.Waypoints -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.{EnumFacing, EnumParticleTypes} -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly - -class Waypoint extends traits.Environment with traits.Rotatable with traits.RedstoneAware with traits.Tickable { +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.ParticleTypes +import net.minecraft.tileentity.TileEntity +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn + +class Waypoint extends TileEntity(null) with traits.Environment with traits.Rotatable with traits.RedstoneAware with traits.Tickable { val node = api.Network.newNode(this, Visibility.Network). withComponent("waypoint"). create() var label = "" - override def validFacings: Array[EnumFacing] = EnumFacing.values + override def validFacings: Array[Direction] = Direction.values // ----------------------------------------------------------------------- // @@ -39,14 +41,14 @@ class Waypoint extends traits.Environment with traits.Rotatable with traits.Reds override def updateEntity(): Unit = { super.updateEntity() if (isClient) { - val origin = position.toVec3.addVector(facing.getFrontOffsetX * 0.5, facing.getFrontOffsetY * 0.5, facing.getFrontOffsetZ * 0.5) - val dx = (getWorld.rand.nextFloat() - 0.5f) * 0.8f - val dy = (getWorld.rand.nextFloat() - 0.5f) * 0.8f - val dz = (getWorld.rand.nextFloat() - 0.5f) * 0.8f - val vx = (getWorld.rand.nextFloat() - 0.5f) * 0.2f + facing.getFrontOffsetX * 0.3f - val vy = (getWorld.rand.nextFloat() - 0.5f) * 0.2f + facing.getFrontOffsetY * 0.3f - 0.5f - val vz = (getWorld.rand.nextFloat() - 0.5f) * 0.2f + facing.getFrontOffsetZ * 0.3f - getWorld.spawnParticle(EnumParticleTypes.PORTAL, origin.x + dx, origin.y + dy, origin.z + dz, vx, vy, vz) + val origin = position.toVec3.add(facing.getStepX * 0.5, facing.getStepY * 0.5, facing.getStepZ * 0.5) + val dx = (getLevel.random.nextFloat() - 0.5f) * 0.8f + val dy = (getLevel.random.nextFloat() - 0.5f) * 0.8f + val dz = (getLevel.random.nextFloat() - 0.5f) * 0.8f + val vx = (getLevel.random.nextFloat() - 0.5f) * 0.2f + facing.getStepX * 0.3f + val vy = (getLevel.random.nextFloat() - 0.5f) * 0.2f + facing.getStepY * 0.3f - 0.5f + val vz = (getLevel.random.nextFloat() - 0.5f) * 0.2f + facing.getStepZ * 0.3f + getLevel.addParticle(ParticleTypes.PORTAL, origin.x + dx, origin.y + dy, origin.z + dz, vx, vy, vz) } } @@ -64,24 +66,24 @@ class Waypoint extends traits.Environment with traits.Rotatable with traits.Reds private final val LabelTag = Settings.namespace + "label" - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) label = nbt.getString(LabelTag) } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) - nbt.setString(LabelTag, label) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) + nbt.putString(LabelTag, label) } - @SideOnly(Side.CLIENT) override - def readFromNBTForClient(nbt: NBTTagCompound): Unit = { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) override + def loadForClient(nbt: CompoundNBT): Unit = { + super.loadForClient(nbt) label = nbt.getString(LabelTag) } - override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { - super.writeToNBTForClient(nbt) - nbt.setString(LabelTag, label) + override def saveForClient(nbt: CompoundNBT): Unit = { + super.saveForClient(nbt) + nbt.putString(LabelTag, label) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index 904beb9ec2..60dde87d0d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -3,17 +3,15 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.RotationHelper import li.cil.oc.integration.Mods import mrtjp.projectred.api.IBundledTile -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagIntArray -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.IntArrayNBT +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.common.Optional import java.util -import net.minecraftforge.common.capabilities.Capability - trait BundledRedstoneAware extends RedstoneAware with IBundledTile { protected[tileentity] val _bundledInput: Array[Array[Int]] = Array.fill(6)(Array.fill(16)(-1)) @@ -41,7 +39,7 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { (0 until 6).map(side => (0 until 16).map(color => _bundledInput(side)(color) max _rednetInput(side)(color) max 0).toArray).toArray } - private def checkSide(side: EnumFacing): Int = { + private def checkSide(side: Direction): Int = { val index = side.ordinal if (index >= 6) throw new IndexOutOfBoundsException(s"Bad side $side") index @@ -52,14 +50,14 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { color } - def getBundledInput(side: EnumFacing): Array[Int] = { + def getBundledInput(side: Direction): Array[Int] = { val sideIndex = checkSide(side) val bundled = _bundledInput(sideIndex) val rednet = _rednetInput(sideIndex) (bundled, rednet).zipped.map((a, b) => a max b max 0) } - def getBundledInput(side: EnumFacing, color: Int): Int = { + def getBundledInput(side: Direction, color: Int): Int = { val sideIndex = checkSide(side) val colorIndex = checkColor(color) val bundled = _bundledInput(sideIndex)(colorIndex) @@ -67,20 +65,20 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { bundled max rednet max 0 } - def setBundledInput(side: EnumFacing, color: Int, newValue: Int): Unit = { + def setBundledInput(side: Direction, color: Int, newValue: Int): Unit = { updateInput(_bundledInput, side, color, newValue) } - def setBundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = { + def setBundledInput(side: Direction, newBundledInput: Array[Int]): Unit = { for (color <- 0 until 16) { val value = if (newBundledInput == null || color >= newBundledInput.length) 0 else newBundledInput(color) setBundledInput(side, color, value) } } - def setRednetInput(side: EnumFacing, color: Int, value: Int): Unit = updateInput(_rednetInput, side, color, value) + def setRednetInput(side: Direction, color: Int, value: Int): Unit = updateInput(_rednetInput, side, color, value) - def updateInput(inputs: Array[Array[Int]], side: EnumFacing, color: Int, newValue: Int): Unit = { + def updateInput(inputs: Array[Array[Int]], side: Direction, color: Int, newValue: Int): Unit = { val sideIndex = checkSide(side) val colorIndex = checkColor(color) val oldValue = inputs(sideIndex)(colorIndex) @@ -94,17 +92,17 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { def getBundledOutput: Array[Array[Int]] = _bundledInput - def getBundledOutput(side: EnumFacing): Array[Int] = _bundledOutput(checkSide(toLocal(side))) + def getBundledOutput(side: Direction): Array[Int] = _bundledOutput(checkSide(toLocal(side))) - def getBundledOutput(side: EnumFacing, color: Int): Int = getBundledOutput(side)(checkColor(color)) + def getBundledOutput(side: Direction, color: Int): Int = getBundledOutput(side)(checkColor(color)) - def setBundledOutput(side: EnumFacing, color: Int, value: Int): Boolean = if (value != getBundledOutput(side, color)) { + def setBundledOutput(side: Direction, color: Int, value: Int): Boolean = if (value != getBundledOutput(side, color)) { _bundledOutput(checkSide(toLocal(side)))(checkColor(color)) = value onRedstoneOutputChanged(side) true } else false - def setBundledOutput(side: EnumFacing, values: util.Map[_, _]): Boolean = { + def setBundledOutput(side: Direction, values: util.Map[_, _]): Boolean = { val sideIndex = toLocal(side).ordinal var changed: Boolean = false (0 until 16).foreach(color => { @@ -126,7 +124,7 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { def setBundledOutput(values: util.Map[_, _]): Boolean = { var changed: Boolean = false - EnumFacing.values.foreach(side => { + Direction.values.foreach(side => { val sideIndex = toLocal(side).ordinal // due to a bug in our jnlua layer, I cannot loop the map getObjectFuzzy(values, sideIndex) match { @@ -139,7 +137,7 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { // ----------------------------------------------------------------------- // - override def updateRedstoneInput(side: EnumFacing) { + override def updateRedstoneInput(side: Direction) { super.updateRedstoneInput(side) setBundledInput(side, BundledRedstone.computeBundledInput(position, side)) } @@ -150,26 +148,26 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { private final val BundledOutputTag = Settings.namespace + "rs.bundledOutput" private final val RednetInputTag = Settings.namespace + "rs.rednetInput" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) - nbt.getTagList(BundledInputTag, NBT.TAG_INT_ARRAY).toArray[NBTTagIntArray]. - map(_.getIntArray).zipWithIndex.foreach { + nbt.getList(BundledInputTag, NBT.TAG_INT_ARRAY).toTagArray[IntArrayNBT]. + map(_.getAsIntArray).zipWithIndex.foreach { case (input, index) if index < _bundledInput.length => val safeLength = input.length min _bundledInput(index).length input.copyToArray(_bundledInput(index), 0, safeLength) case _ => } - nbt.getTagList(BundledOutputTag, NBT.TAG_INT_ARRAY).toArray[NBTTagIntArray]. - map(_.getIntArray).zipWithIndex.foreach { + nbt.getList(BundledOutputTag, NBT.TAG_INT_ARRAY).toTagArray[IntArrayNBT]. + map(_.getAsIntArray).zipWithIndex.foreach { case (input, index) if index < _bundledOutput.length => val safeLength = input.length min _bundledOutput(index).length input.copyToArray(_bundledOutput(index), 0, safeLength) case _ => } - nbt.getTagList(RednetInputTag, NBT.TAG_INT_ARRAY).toArray[NBTTagIntArray]. - map(_.getIntArray).zipWithIndex.foreach { + nbt.getList(RednetInputTag, NBT.TAG_INT_ARRAY).toTagArray[IntArrayNBT]. + map(_.getAsIntArray).zipWithIndex.foreach { case (input, index) if index < _rednetInput.length => val safeLength = input.length min _rednetInput(index).length input.copyToArray(_rednetInput(index), 0, safeLength) @@ -177,8 +175,8 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) nbt.setNewTagList(BundledInputTag, _bundledInput.view) nbt.setNewTagList(BundledOutputTag, _bundledOutput.view) @@ -186,25 +184,7 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { nbt.setNewTagList(RednetInputTag, _rednetInput.view) } - override def hasCapability(capability: Capability[_], side: EnumFacing): Boolean = { - if (capability == CapabilitiesCharset.BUNDLED_EMITTER || capability == CapabilitiesCharset.BUNDLED_RECEIVER) { - true - } else { - super.hasCapability(capability, side) - } - } - - override def getCapability[T](capability: Capability[T], side: EnumFacing): T = { - if (capability == CapabilitiesCharset.BUNDLED_EMITTER || capability == CapabilitiesCharset.BUNDLED_RECEIVER) { - new ModCharset.BundledRedstoneView(getBundledOutput(side), () => setBundledInput(side, BundledRedstone.computeBundledInput(position, side))).asInstanceOf[T] - } else { - super.getCapability(capability, side) - } - } - - @Optional.Method(modid = Mods.IDs.ProjectRedTransmission) override def canConnectBundled(side: Int): Boolean = isOutputEnabled - @Optional.Method(modid = Mods.IDs.ProjectRedTransmission) - override def getBundledSignal(side: Int): Array[Byte] = getBundledOutput(EnumFacing.getFront(side)).map(value => math.min(math.max(value, 0), 255).toByte) + override def getBundledSignal(side: Int): Array[Byte] = getBundledOutput(Direction.from3DDataValue(side)).map(value => math.min(math.max(value, 0), 255).toByte) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Colored.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Colored.scala index 66129b6e5a..9d68f9b9f9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Colored.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Colored.scala @@ -4,10 +4,10 @@ import li.cil.oc.Settings import li.cil.oc.api.internal import li.cil.oc.server.PacketSender import li.cil.oc.util.Color -import net.minecraft.item.EnumDyeColor -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.item.DyeColor +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait Colored extends TileEntity with internal.Colored { private var _color = 0 @@ -24,7 +24,7 @@ trait Colored extends TileEntity with internal.Colored { override def controlsConnectivity = false protected def onColorChanged() { - if (getWorld != null && isServer) { + if (getLevel != null && isServer) { PacketSender.sendColorChange(this) } } @@ -34,29 +34,29 @@ trait Colored extends TileEntity with internal.Colored { private final val RenderColorTag = Settings.namespace + "renderColorRGB" private final val RenderColorTagCompat = Settings.namespace + "renderColor" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(RenderColorTagCompat)) { - _color = Color.rgbValues(EnumDyeColor.byMetadata(nbt.getInteger(RenderColorTagCompat))) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + if (nbt.contains(RenderColorTagCompat)) { + _color = Color.rgbValues(DyeColor.byId(nbt.getInt(RenderColorTagCompat))) } - if (nbt.hasKey(RenderColorTag)) { - _color = nbt.getInteger(RenderColorTag) + if (nbt.contains(RenderColorTag)) { + _color = nbt.getInt(RenderColorTag) } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setInteger(RenderColorTag, _color) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.putInt(RenderColorTag, _color) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - _color = nbt.getInteger(RenderColorTag) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + _color = nbt.getInt(RenderColorTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setInteger(RenderColorTag, _color) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putInt(RenderColorTag, _color) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala index af21bd4945..a8297148e9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala @@ -9,12 +9,13 @@ import li.cil.oc.util.ExtendedInventory._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable @@ -26,8 +27,8 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo // Cache changes to inventory slots on the client side to avoid recreating // components when we don't have to and the slots are just cleared by MC // temporarily. - private lazy val pendingRemovalsActual = mutable.ArrayBuffer.fill(getSizeInventory)(EmptyStack: StackOption) - private lazy val pendingAddsActual = mutable.ArrayBuffer.fill(getSizeInventory)(EmptyStack: StackOption) + private lazy val pendingRemovalsActual = mutable.ArrayBuffer.fill(getContainerSize)(EmptyStack: StackOption) + private lazy val pendingAddsActual = mutable.ArrayBuffer.fill(getContainerSize)(EmptyStack: StackOption) private var updateScheduled = false def pendingRemovals = { adjustSize(pendingRemovalsActual) @@ -39,12 +40,12 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo } private def adjustSize(buffer: mutable.ArrayBuffer[StackOption]): Unit = { - val delta = buffer.length - getSizeInventory + val delta = buffer.length - getContainerSize if (delta > 0) { buffer.remove(buffer.length - delta, delta) } else if (delta < 0) { - buffer.sizeHint(getSizeInventory) + buffer.sizeHint(getContainerSize) for (i <- 0 until -delta) { buffer += EmptyStack } @@ -56,17 +57,17 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo for (slot <- this.indices) { (pendingRemovals(slot), pendingAdds(slot)) match { case (SomeStack(removed), SomeStack(added)) => - if (!removed.isItemEqual(added) || !ItemStack.areItemStackTagsEqual(removed, added)) { + if (!removed.sameItem(added) || !ItemStack.tagMatches(removed, added)) { super.onItemRemoved(slot, removed) super.onItemAdded(slot, added) - markDirty() + setChanged() } // else: No change, ignore. case (SomeStack(removed), EmptyStack) => super.onItemRemoved(slot, removed) - markDirty() + setChanged() case (EmptyStack, SomeStack(added)) => super.onItemAdded(slot, added) - markDirty() + setChanged() case _ => // No change. } @@ -86,7 +87,7 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo if (isServer) super.onItemAdded(slot, stack) else { pendingRemovals(slot) match { - case SomeStack(removed) if removed.isItemEqual(stack) && ItemStack.areItemStackTagsEqual(removed, stack) => + case SomeStack(removed) if removed.sameItem(stack) && ItemStack.tagMatches(removed, stack) => // Reverted to original state. pendingAdds(slot) = EmptyStack pendingRemovals(slot) = EmptyStack @@ -153,37 +154,31 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo } } - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = { + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { val localFacing = this match { case rotatable: Rotatable => rotatable.toLocal(facing) case _ => facing } - super.hasCapability(capability, facing) || components.exists { - case Some(component: ICapabilityProvider) => component.hasCapability(capability, localFacing) - case _ => false - } - } - - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { - val localFacing = this match { - case rotatable: Rotatable => rotatable.toLocal(facing) - case _ => facing + for (curr <- components) curr match { + case Some(comp: ICapabilityProvider) => { + val cap = comp.getCapability(capability, localFacing) + if (cap.isPresent) return cap + } + case _ => } - (if (super.hasCapability(capability, facing)) Option(super.getCapability(capability, facing)) else None).orElse(components.collectFirst { - case Some(component: ICapabilityProvider) if component.hasCapability(capability, localFacing) => component.getCapability(capability, localFacing) - }).getOrElse(null.asInstanceOf[T]) + super.getCapability(capability, facing) } - override def writeToNBTForClient(nbt: NBTTagCompound) { + override def saveForClient(nbt: CompoundNBT) { connectComponents() - super.writeToNBTForClient(nbt) - save(nbt) + super.saveForClient(nbt) + saveData(nbt) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - load(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + loadData(nbt) connectComponents() } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index c05425bf82..60069ab11f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -13,14 +13,14 @@ import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.server.agent import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagString -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.StringNBT +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.WrapAsJava._ import scala.collection.mutable @@ -54,18 +54,18 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B if (value) { hasErrored = false } - if (getWorld != null) { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) - if (getWorld.isRemote) { + if (getLevel != null) { + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) + if (getLevel.isClientSide) { runSound.foreach(sound => - if (_isRunning) Sound.startLoop(this, sound, 0.5f, 50 + getWorld.rand.nextInt(50)) + if (_isRunning) Sound.startLoop(this, sound, 0.5f, 50 + getLevel.random.nextInt(50)) else Sound.stopLoop(this) ) } } } - @SideOnly(Side.CLIENT) + @OnlyIn(Dist.CLIENT) def setUsers(list: Iterable[String]) { _users.clear() _users ++= list @@ -78,8 +78,8 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B // ----------------------------------------------------------------------- // - override def internalComponents(): lang.Iterable[ItemStack] = (0 until getSizeInventory).collect { - case slot if !getStackInSlot(slot).isEmpty && isComponentSlot(slot, getStackInSlot(slot)) => getStackInSlot(slot) + override def internalComponents(): lang.Iterable[ItemStack] = (0 until getContainerSize).collect { + case slot if !getItem(slot).isEmpty && isComponentSlot(slot, getItem(slot)) => getItem(slot) } @@ -122,7 +122,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B } protected def onRunningChanged(): Unit = { - markDirty() + setChanged() ServerPacketSender.sendComputerState(this) } @@ -140,62 +140,62 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B private final val IsRunningTag = Settings.namespace + "isRunning" private final val UsersTag = Settings.namespace + "users" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) // God, this is so ugly... will need to rework the robot architecture. // This is required for loading auxiliary data (kernel state), because the // coordinates in the actual robot won't be set properly, otherwise. this match { - case proxy: RobotProxy => proxy.robot.setPos(getPos) + case proxy: RobotProxy => proxy.robot.setLevelAndPosition(getLevel, getBlockPos) case _ => } - machine.load(nbt.getCompoundTag(ComputerTag)) + machine.loadData(nbt.getCompound(ComputerTag)) // Kickstart initialization to avoid values getting overwritten by - // readFromNBTForClient if that packet is handled after a manual + // loadForClient if that packet is handled after a manual // initialization / state change packet. setRunning(machine.isRunning) _isOutputEnabled = hasRedstoneCard } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) if (machine != null) { - nbt.setNewCompoundTag(ComputerTag, machine.save) + nbt.setNewCompoundTag(ComputerTag, machine.saveData) } } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) hasErrored = nbt.getBoolean(HasErroredTag) setRunning(nbt.getBoolean(IsRunningTag)) _users.clear() - _users ++= nbt.getTagList(UsersTag, NBT.TAG_STRING).map((tag: NBTTagString) => tag.getString) - if (_isRunning) runSound.foreach(sound => Sound.startLoop(this, sound, 0.5f, 1000 + getWorld.rand.nextInt(2000))) + _users ++= nbt.getList(UsersTag, NBT.TAG_STRING).map((tag: StringNBT) => tag.getAsString) + if (_isRunning) runSound.foreach(sound => Sound.startLoop(this, sound, 0.5f, 1000 + getLevel.random.nextInt(2000))) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setBoolean(HasErroredTag, machine != null && machine.lastError != null) - nbt.setBoolean(IsRunningTag, isRunning) - nbt.setNewTagList(UsersTag, machine.users.map(user => new NBTTagString(user))) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putBoolean(HasErroredTag, machine != null && machine.lastError != null) + nbt.putBoolean(IsRunningTag, isRunning) + nbt.setNewTagList(UsersTag, machine.users.map(user => StringNBT.valueOf(user))) } // ----------------------------------------------------------------------- // - override def markDirty() { - super.markDirty() + override def setChanged() { + super.setChanged() if (isServer) { machine.onHostChanged() setOutputEnabled(hasRedstoneCard) } } - override def isUsableByPlayer(player: EntityPlayer): Boolean = - super.isUsableByPlayer(player) && (player match { + override def stillValid(player: PlayerEntity): Boolean = + super.stillValid(player) && (player match { case fakePlayer: agent.Player => canInteract(fakePlayer.agent.ownerName()) - case _ => canInteract(player.getName) + case _ => canInteract(player.getName.getString) }) override protected def onRotationChanged() { @@ -211,5 +211,5 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B // ----------------------------------------------------------------------- // - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala index ce5c928a73..9f71cb00e2 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala @@ -6,13 +6,15 @@ import li.cil.oc.api.network.Connector import li.cil.oc.api.network.SidedEnvironment import li.cil.oc.common.EventHandler import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraftforge.client.model.data.IModelData +import net.minecraftforge.client.model.data.ModelProperty -trait Environment extends TileEntity with network.Environment with network.EnvironmentHost { +trait Environment extends TileEntity with network.Environment with network.EnvironmentHost with IModelData { protected var isChangeScheduled = false - override def world = getWorld + override def world = getLevel override def xPosition = x + 0.5 @@ -20,7 +22,7 @@ trait Environment extends TileEntity with network.Environment with network.Envir override def zPosition = z + 0.5 - override def markChanged() = if (this.isInstanceOf[Tickable]) isChangeScheduled = true else getWorld.markChunkDirty(getPos, this) + override def markChanged() = if (this.isInstanceOf[Tickable]) isChangeScheduled = true else getLevel.blockEntityChanged(getBlockPos, this) protected def isConnected = node != null && node.address != null && node.network != null @@ -36,7 +38,7 @@ trait Environment extends TileEntity with network.Environment with network.Envir override def updateEntity() { super.updateEntity() if (isChangeScheduled) { - getWorld.markChunkDirty(getPos, this) + getLevel.blockEntityChanged(getBlockPos, this) isChangeScheduled = false } } @@ -46,7 +48,7 @@ trait Environment extends TileEntity with network.Environment with network.Envir if (isServer) { Option(node).foreach(_.remove) this match { - case sidedEnvironment: SidedEnvironment => for (side <- EnumFacing.values) { + case sidedEnvironment: SidedEnvironment => for (side <- Direction.values) { Option(sidedEnvironment.sidedNode(side)).foreach(_.remove()) } case _ => @@ -58,17 +60,17 @@ trait Environment extends TileEntity with network.Environment with network.Envir private final val NodeTag = Settings.namespace + "node" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) if (node != null && node.host == this) { - node.load(nbt.getCompoundTag(NodeTag)) + node.loadData(nbt.getCompound(NodeTag)) } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) if (node != null && node.host == this) { - nbt.setNewCompoundTag(NodeTag, node.save) + nbt.setNewCompoundTag(NodeTag, node.saveData) } } @@ -95,4 +97,18 @@ trait Environment extends TileEntity with network.Environment with network.Envir // ----------------------------------------------------------------------- // protected def result(args: Any*) = li.cil.oc.util.ResultWrapper.result(args: _*) + + // ----------------------------------------------------------------------- // + + @Deprecated + override def getModelData() = this + + @Deprecated + override def hasProperty(prop: ModelProperty[_]) = false + + @Deprecated + override def getData[T](prop: ModelProperty[T]): T = null.asInstanceOf[T] + + @Deprecated + override def setData[T](prop: ModelProperty[T], value: T): T = null.asInstanceOf[T] } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Hub.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Hub.scala index 82ef43ca12..d21fb66f19 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Hub.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Hub.scala @@ -6,11 +6,11 @@ import li.cil.oc.api.network._ import li.cil.oc.common.tileentity.traits import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.MovingAverage -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable @@ -23,9 +23,9 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable { plug.node.address != null && plug.node.network != null) - protected val plugs = EnumFacing.values.map(side => createPlug(side)) + protected val plugs = Direction.values.map(side => createPlug(side)) - val queue = mutable.Queue.empty[(Option[EnumFacing], Packet)] + val queue = mutable.Queue.empty[(Option[Direction], Packet)] var maxQueueSize = queueBaseSize @@ -54,10 +54,10 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable { // ----------------------------------------------------------------------- // - @SideOnly(Side.CLIENT) - override def canConnect(side: EnumFacing) = side != null + @OnlyIn(Dist.CLIENT) + override def canConnect(side: Direction) = side != null - override def sidedNode(side: EnumFacing) = if (side != null) plugs(side.ordinal).node else null + override def sidedNode(side: Direction) = if (side != null) plugs(side.ordinal).node else null // ----------------------------------------------------------------------- // @@ -79,13 +79,13 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable { relayCooldown = relayDelay - 1 } } - else if (getWorld.getTotalWorldTime % relayDelay == 0) { + else if (getLevel.getGameTime % relayDelay == 0) { packetsPerCycleAvg += 0 } } } - def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet) = queue.synchronized { + def tryEnqueuePacket(sourceSide: Option[Direction], packet: Packet) = queue.synchronized { if (packet.ttl > 0 && queue.size < maxQueueSize) { queue += sourceSide -> packet.hop() if (relayCooldown < 0) { @@ -96,8 +96,8 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable { else false } - protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet) { - for (side <- EnumFacing.values) { + protected def relayPacket(sourceSide: Option[Direction], packet: Packet) { + for (side <- Direction.values) { if (sourceSide.isEmpty || sourceSide.get != side) { val node = sidedNode(side) if (node != null) { @@ -114,51 +114,51 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable { private final val SideTag = "side" private final val RelayCooldownTag = Settings.namespace + "relayCooldown" - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - nbt.getTagList(PlugsTag, NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + nbt.getList(PlugsTag, NBT.TAG_COMPOUND).toTagArray[CompoundNBT]. zipWithIndex.foreach { - case (tag, index) => plugs(index).node.load(tag) + case (tag, index) => plugs(index).node.loadData(tag) } - nbt.getTagList(QueueTag, NBT.TAG_COMPOUND).foreach( - (tag: NBTTagCompound) => { + nbt.getList(QueueTag, NBT.TAG_COMPOUND).foreach( + (tag: CompoundNBT) => { val side = tag.getDirection(SideTag) val packet = api.Network.newPacket(tag) queue += side -> packet }) - if (nbt.hasKey(RelayCooldownTag)) { - relayCooldown = nbt.getInteger(RelayCooldownTag) + if (nbt.contains(RelayCooldownTag)) { + relayCooldown = nbt.getInt(RelayCooldownTag) } } - override def writeToNBTForServer(nbt: NBTTagCompound) = queue.synchronized { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) = queue.synchronized { + super.saveForServer(nbt) // Side check for Waila (and other mods that may call this client side). if (isServer) { nbt.setNewTagList(PlugsTag, plugs.map(plug => { - val plugNbt = new NBTTagCompound() + val plugNbt = new CompoundNBT() if (plug.node != null) - plug.node.save(plugNbt) + plug.node.saveData(plugNbt) plugNbt })) nbt.setNewTagList(QueueTag, queue.map { case (sourceSide, packet) => - val tag = new NBTTagCompound() + val tag = new CompoundNBT() tag.setDirection(SideTag, sourceSide) - packet.save(tag) + packet.saveData(tag) tag }) if (relayCooldown > 0) { - nbt.setInteger(RelayCooldownTag, relayCooldown) + nbt.putInt(RelayCooldownTag, relayCooldown) } } } // ----------------------------------------------------------------------- // - protected def createPlug(side: EnumFacing) = new Plug(side) + protected def createPlug(side: Direction) = new Plug(side) - protected class Plug(val side: EnumFacing) extends api.network.Environment { + protected class Plug(val side: Direction) extends api.network.Environment { val node = createNode(this) override def onMessage(message: Message) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala index 38ae6cad4e..52ad951e11 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala @@ -3,14 +3,14 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.common.inventory import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction import net.minecraft.util.text.ITextComponent trait Inventory extends TileEntity with inventory.Inventory { - private lazy val inventory = Array.fill[ItemStack](getSizeInventory)(ItemStack.EMPTY) + private lazy val inventory = Array.fill[ItemStack](getContainerSize)(ItemStack.EMPTY) def items = inventory @@ -18,29 +18,29 @@ trait Inventory extends TileEntity with inventory.Inventory { override def getDisplayName: ITextComponent = super[Inventory].getDisplayName - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - load(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - save(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + saveData(nbt) } // ----------------------------------------------------------------------- // - override def isUsableByPlayer(player: EntityPlayer) = - player.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64 + override def stillValid(player: PlayerEntity) = + player.distanceToSqr(x + 0.5, y + 0.5, z + 0.5) <= 64 // ----------------------------------------------------------------------- // - def dropSlot(slot: Int, count: Int = getInventoryStackLimit, direction: Option[EnumFacing] = None) = - InventoryUtils.dropSlot(BlockPosition(x, y, z, getWorld), this, slot, count, direction) + def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction] = None) = + InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), this, slot, count, direction) def dropAllSlots() = - InventoryUtils.dropAllSlots(BlockPosition(x, y, z, getWorld), this) + InventoryUtils.dropAllSlots(BlockPosition(x, y, z, getLevel), this) - def spawnStackInWorld(stack: ItemStack, direction: Option[EnumFacing] = None) = - InventoryUtils.spawnStackInWorld(BlockPosition(x, y, z, getWorld), stack, direction) + def spawnStackInWorld(stack: ItemStack, direction: Option[Direction] = None) = + InventoryUtils.spawnStackInWorld(BlockPosition(x, y, z, getLevel), stack, direction) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/NotAnalyzable.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/NotAnalyzable.scala index ab04a1141a..45f1692a61 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/NotAnalyzable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/NotAnalyzable.scala @@ -2,9 +2,9 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network.Node -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.util.EnumFacing +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.Direction trait NotAnalyzable extends Analyzable { - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = null + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = null } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/OpenSides.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/OpenSides.scala index cc8f3bb63d..389776ac81 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/OpenSides.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/OpenSides.scala @@ -1,50 +1,51 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import li.cil.oc.util.RotationHelper +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn /** * @author Vexatos */ trait OpenSides extends TileEntity { - protected def SideCount = EnumFacing.VALUES.length + protected def SideCount = RotationHelper.getNumDirections protected def defaultState: Boolean = false var openSides = Array.fill(SideCount)(defaultState) - def compressSides = (EnumFacing.values(), openSides).zipped.foldLeft(0)((acc, entry) => acc | (if (entry._2) 1 << entry._1.ordinal() else 0)).toByte + def compressSides = (Direction.values(), openSides).zipped.foldLeft(0)((acc, entry) => acc | (if (entry._2) 1 << entry._1.ordinal() else 0)).toByte - def uncompressSides(byte: Byte) = EnumFacing.values().map(d => ((1 << d.ordinal()) & byte) != 0) + def uncompressSides(byte: Byte) = Direction.values().map(d => ((1 << d.ordinal()) & byte) != 0) - def isSideOpen(side: EnumFacing) = side != null && openSides(side.ordinal()) + def isSideOpen(side: Direction) = side != null && openSides(side.ordinal()) - def setSideOpen(side: EnumFacing, value: Boolean): Unit = if (side != null && openSides(side.ordinal()) != value) { + def setSideOpen(side: Direction, value: Boolean): Unit = if (side != null && openSides(side.ordinal()) != value) { openSides(side.ordinal()) = value } - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(Settings.namespace + "openSides")) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) + if (nbt.contains(Settings.namespace + "openSides")) openSides = uncompressSides(nbt.getByte(Settings.namespace + "openSides")) } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) - nbt.setByte(Settings.namespace + "openSides", compressSides) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) + nbt.putByte(Settings.namespace + "openSides", compressSides) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) openSides = uncompressSides(nbt.getByte(Settings.namespace + "openSides")) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setByte(Settings.namespace + "openSides", compressSides) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putByte(Settings.namespace + "openSides", compressSides) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala index 7701b48b0f..885a92b3c9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PlayerInputAware.scala @@ -1,14 +1,14 @@ package li.cil.oc.common.tileentity.traits -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack // Used to get notifications from containers when a player changes a slot in -// this inventory. Normally the player causing a setInventorySlotContents is +// this inventory. Normally the player causing a setItem is // unavailable. Using this we gain access to the causing player, allowing for // some player-specific logic, such as the disassembler working instantaneously // when used by a player in creative mode. trait PlayerInputAware extends IInventory { - def onSetInventorySlotContents(player: EntityPlayer, slot: Int, stack: ItemStack): Unit + def onSetInventorySlotContents(player: PlayerEntity, slot: Int, stack: ItemStack): Unit } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala index cfdade18dd..6bcb8f90b7 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala @@ -2,5 +2,4 @@ package li.cil.oc.common.tileentity.traits trait PowerAcceptor extends power.Common - with power.IndustrialCraft2Experimental with power.AppliedEnergistics2 diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerBalancer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerBalancer.scala index f66269a245..b533f6a1a4 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerBalancer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerBalancer.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings import li.cil.oc.api.network.Connector import li.cil.oc.api.network.SidedEnvironment -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction trait PowerBalancer extends PowerInformation with SidedEnvironment with Tickable { var globalBuffer, globalBufferSize = 0.0 @@ -12,7 +12,7 @@ trait PowerBalancer extends PowerInformation with SidedEnvironment with Tickable override def updateEntity() { super.updateEntity() - if (isServer && isConnected && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (isServer && isConnected && getLevel.getGameTime % Settings.get.tickFrequency == 0) { val nodes = connectors def network(connector: Connector) = if (connector != null && connector.network != null) connector.network else this // Yeeeeah, so that just happened... it's not a beauty, but it works. This @@ -55,7 +55,7 @@ trait PowerBalancer extends PowerInformation with SidedEnvironment with Tickable (sumBuffer, sumSize) } - private def connectors = EnumFacing.values.view.map(sidedNode(_) match { + private def connectors = Direction.values.view.map(sidedNode(_) match { case connector: Connector => connector case _ => null }) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerInformation.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerInformation.scala index 43dff2cf3c..0bf77a62b4 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerInformation.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerInformation.scala @@ -2,9 +2,9 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait PowerInformation extends TileEntity { private var lastSentRatio = -1.0 @@ -43,17 +43,17 @@ trait PowerInformation extends TileEntity { private final val GlobalBufferTag = Settings.namespace + "globalBuffer" private final val GlobalBufferSizeTag = Settings.namespace + "globalBufferSize" - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) globalBuffer = nbt.getDouble(GlobalBufferTag) globalBufferSize = nbt.getDouble(GlobalBufferSizeTag) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) lastSentRatio = if (globalBufferSize > 0) globalBuffer / globalBufferSize else 0 - nbt.setDouble(GlobalBufferTag, globalBuffer) - nbt.setDouble(GlobalBufferSizeTag, globalBufferSize) + nbt.putDouble(GlobalBufferTag, globalBuffer) + nbt.putDouble(GlobalBufferSizeTag, globalBufferSize) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala index c2a3563220..d2a35706b0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala @@ -5,12 +5,12 @@ import li.cil.oc.Settings import li.cil.oc.common.EventHandler import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -case class RedstoneChangedEventArgs (side: EnumFacing, oldValue: Int, newValue: Int, color: Int = -1) +case class RedstoneChangedEventArgs (side: Direction, oldValue: Int, newValue: Int, color: Int = -1) trait RedstoneAware extends RotationAware { protected[tileentity] val _input: Array[Int] = Array.fill(6)(-1) @@ -59,9 +59,9 @@ trait RedstoneAware extends RotationAware { def getInput: Array[Int] = _input.map(math.max(_, 0)) - def getInput(side: EnumFacing): Int = _input(side.ordinal) max 0 + def getInput(side: Direction): Int = _input(side.ordinal) max 0 - def setInput(side: EnumFacing, newInput: Int): Unit = { + def setInput(side: Direction, newInput: Int): Unit = { val oldInput = _input(side.ordinal()) _input(side.ordinal()) = newInput if (oldInput >= 0 && newInput != oldInput) { @@ -70,7 +70,7 @@ trait RedstoneAware extends RotationAware { } def setInput(values: Array[Int]): Unit = { - for (side <- EnumFacing.values) { + for (side <- Direction.values) { val value = if (side.ordinal <= values.length) values(side.ordinal) else 0 setInput(side, value) } @@ -78,13 +78,13 @@ trait RedstoneAware extends RotationAware { def maxInput: Int = _input.map(math.max(_, 0)).max - def getOutput: Array[Int] = EnumFacing.values.map{ side: EnumFacing => _output(toLocal(side).ordinal) } + def getOutput: Array[Int] = Direction.values.map{ side: Direction => _output(toLocal(side).ordinal) } - def getOutput(side: EnumFacing) = if (_output != null && _output.length > toLocal(side).ordinal()) + def getOutput(side: Direction) = if (_output != null && _output.length > toLocal(side).ordinal()) _output(toLocal(side).ordinal()) else 0 - def setOutput(side: EnumFacing, value: Int): Boolean = { + def setOutput(side: Direction, value: Int): Boolean = { if (value == getOutput(side)) return false _output(toLocal(side).ordinal()) = value onRedstoneOutputChanged(side) @@ -93,7 +93,7 @@ trait RedstoneAware extends RotationAware { def setOutput(values: util.Map[_, _]): Boolean = { var changed: Boolean = false - EnumFacing.values.foreach(side => { + Direction.values.foreach(side => { val sideIndex = toLocal(side).ordinal // due to a bug in our jnlua layer, I cannot loop the map valueToInt(getObjectFuzzy(values, sideIndex)) match { @@ -108,7 +108,7 @@ trait RedstoneAware extends RotationAware { if (this.isInstanceOf[Tickable]) { shouldUpdateInput = isServer } else { - EnumFacing.values().foreach(updateRedstoneInput) + Direction.values().foreach(updateRedstoneInput) } } @@ -119,24 +119,24 @@ trait RedstoneAware extends RotationAware { if (isServer) { if (shouldUpdateInput) { shouldUpdateInput = false - EnumFacing.values().foreach(updateRedstoneInput) + Direction.values().foreach(updateRedstoneInput) } } } - override def validate(): Unit = { - super.validate() + override def clearRemoved(): Unit = { + super.clearRemoved() if (!this.isInstanceOf[Tickable]) { - EventHandler.scheduleServer(() => EnumFacing.values().foreach(updateRedstoneInput)) + EventHandler.scheduleServer(() => Direction.values().foreach(updateRedstoneInput)) } } - def updateRedstoneInput(side: EnumFacing): Unit = setInput(side, BundledRedstone.computeInput(position, side)) + def updateRedstoneInput(side: Direction): Unit = setInput(side, BundledRedstone.computeInput(position, side)) // ----------------------------------------------------------------------- // - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) val input = nbt.getIntArray(Settings.namespace + "rs.input") input.copyToArray(_input, 0, input.length min _input.length) @@ -144,24 +144,24 @@ trait RedstoneAware extends RotationAware { output.copyToArray(_output, 0, output.length min _output.length) } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) - nbt.setIntArray(Settings.namespace + "rs.input", _input) - nbt.setIntArray(Settings.namespace + "rs.output", _output) + nbt.putIntArray(Settings.namespace + "rs.input", _input) + nbt.putIntArray(Settings.namespace + "rs.output", _output) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) _isOutputEnabled = nbt.getBoolean("isOutputEnabled") nbt.getIntArray("output").copyToArray(_output) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setBoolean("isOutputEnabled", _isOutputEnabled) - nbt.setIntArray("output", _output) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putBoolean("isOutputEnabled", _isOutputEnabled) + nbt.putIntArray("output", _output) } // ----------------------------------------------------------------------- // @@ -169,19 +169,19 @@ trait RedstoneAware extends RotationAware { protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {} protected def onRedstoneOutputEnabledChanged() { - if (getWorld != null) { - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, true) + if (getLevel != null) { + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) if (isServer) ServerPacketSender.sendRedstoneState(this) - else getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + else getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } - protected def onRedstoneOutputChanged(side: EnumFacing) { - val blockPos = getPos.offset(side) - getWorld.neighborChanged(blockPos, getBlockType, blockPos) - getWorld.notifyNeighborsOfStateExcept(blockPos, getWorld.getBlockState(blockPos).getBlock, side.getOpposite) + protected def onRedstoneOutputChanged(side: Direction) { + val blockPos = getBlockPos.relative(side) + getLevel.neighborChanged(blockPos, getBlockState.getBlock, blockPos) + getLevel.updateNeighborsAtExceptFromFacing(blockPos, getLevel.getBlockState(blockPos).getBlock, side.getOpposite) if (isServer) ServerPacketSender.sendRedstoneState(this) - else getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + else getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala index 9a223605d9..fab750e853 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala @@ -1,14 +1,16 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.api.internal +import li.cil.oc.common.block.SimpleBlock import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedEnumFacing._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.RotationHelper -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.entity.Entity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.Rotation /** TileEntity base class for rotatable blocks. */ trait Rotatable extends RotationAware with internal.Rotatable { @@ -16,92 +18,98 @@ trait Rotatable extends RotationAware with internal.Rotatable { // Lookup tables // ----------------------------------------------------------------------- // - private val pitch2Direction = Array(EnumFacing.UP, EnumFacing.NORTH, EnumFacing.DOWN) + private val pitch2Direction = Array(Direction.UP, Direction.NORTH, Direction.DOWN) - private val yaw2Direction = Array(EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.EAST) + private val yaw2Direction = Array(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST) // ----------------------------------------------------------------------- // // Accessors // ----------------------------------------------------------------------- // - def pitch = if (getWorld != null && getWorld.isBlockLoaded(getPos)) getBlockType match { - case rotatable if getWorld.getBlockState(getPos).getProperties.containsKey(PropertyRotatable.Pitch) => getWorld.getBlockState(getPos).getValue(PropertyRotatable.Pitch) - case _ => EnumFacing.NORTH + def pitch = if (getLevel != null && getLevel.isLoaded(getBlockPos)) getBlockState.getBlock match { + case rotatable if getLevel.getBlockState(getBlockPos).getProperties.contains(PropertyRotatable.Pitch) => getLevel.getBlockState(getBlockPos).getValue(PropertyRotatable.Pitch) + case _ => Direction.NORTH } else null - def pitch_=(value: EnumFacing): Unit = + def pitch_=(value: Direction): Unit = trySetPitchYaw(value match { - case EnumFacing.DOWN | EnumFacing.UP => value - case _ => EnumFacing.NORTH + case Direction.DOWN | Direction.UP => value + case _ => Direction.NORTH }, yaw) - def yaw = if (getWorld != null && getWorld.isBlockLoaded(getPos)) getBlockType match { - case rotatable if getWorld.getBlockState(getPos).getProperties.containsKey(PropertyRotatable.Yaw) => getWorld.getBlockState(getPos).getValue(PropertyRotatable.Yaw) - case rotatable if getWorld.getBlockState(getPos).getProperties.containsKey(PropertyRotatable.Facing) => getWorld.getBlockState(getPos).getValue(PropertyRotatable.Facing) - case _ => EnumFacing.SOUTH + def yaw = if (getLevel != null && getLevel.isLoaded(getBlockPos)) getBlockState.getBlock match { + case rotatable if getLevel.getBlockState(getBlockPos).getProperties.contains(PropertyRotatable.Yaw) => getLevel.getBlockState(getBlockPos).getValue(PropertyRotatable.Yaw) + case rotatable if getLevel.getBlockState(getBlockPos).getProperties.contains(PropertyRotatable.Facing) => getLevel.getBlockState(getBlockPos).getValue(PropertyRotatable.Facing) + case _ => Direction.SOUTH } else null - def yaw_=(value: EnumFacing): Unit = + def yaw_=(value: Direction): Unit = trySetPitchYaw(pitch, value match { - case EnumFacing.DOWN | EnumFacing.UP => yaw + case Direction.DOWN | Direction.UP => yaw case _ => value }) def setFromEntityPitchAndYaw(entity: Entity) = trySetPitchYaw( - pitch2Direction((entity.rotationPitch / 90).round + 1), - yaw2Direction((entity.rotationYaw / 360 * 4).round & 3)) + pitch2Direction((entity.xRot / 90).round + 1), + yaw2Direction((entity.yRot / 360 * 4).round & 3)) - def setFromFacing(value: EnumFacing) = + def setFromFacing(value: Direction) = value match { - case EnumFacing.DOWN | EnumFacing.UP => + case Direction.DOWN | Direction.UP => trySetPitchYaw(value, yaw) case yaw => - trySetPitchYaw(EnumFacing.NORTH, yaw) + trySetPitchYaw(Direction.NORTH, yaw) } def invertRotation() = trySetPitchYaw(pitch match { - case EnumFacing.DOWN | EnumFacing.UP => pitch.getOpposite - case _ => EnumFacing.NORTH + case Direction.DOWN | Direction.UP => pitch.getOpposite + case _ => Direction.NORTH }, yaw.getOpposite) override def facing = pitch match { - case EnumFacing.DOWN | EnumFacing.UP => pitch + case Direction.DOWN | Direction.UP => pitch case _ => yaw } - def rotate(axis: EnumFacing) = { - val block = getWorld.getBlock(position) - if (block != null) { - val valid = block.getValidRotations(getWorld, getPos) - if (valid != null && valid.contains(axis)) { - val (newPitch, newYaw) = facing.getRotation(axis) match { - case value@(EnumFacing.UP | EnumFacing.DOWN) => - if (value == pitch) (value, yaw.getRotation(axis)) - else (value, yaw) - case value => (EnumFacing.NORTH, value) + def rotate(axis: Direction) = { + val state = getLevel.getBlockState(getBlockPos) + state.getBlock match { + case simple: SimpleBlock => { + val valid = simple.getValidRotations(getLevel, getBlockPos) + if (valid != null && valid.contains(axis)) { + val (newPitch, newYaw) = facing.getRotation(axis) match { + case value@(Direction.UP | Direction.DOWN) => + if (value == pitch) (value, yaw.getRotation(axis)) + else (value, yaw) + case value => (Direction.NORTH, value) + } + trySetPitchYaw(newPitch, newYaw) } - trySetPitchYaw(newPitch, newYaw) + else false } - else false + case _ if axis == Direction.UP || axis == Direction.DOWN => { + val updated = state.rotate(getLevel, getBlockPos, if (axis == Direction.DOWN) Rotation.COUNTERCLOCKWISE_90 else Rotation.CLOCKWISE_90) + updated != state && getLevel.setBlockAndUpdate(getBlockPos, updated) + } + case _ => false } - else false } - override def toLocal(value: EnumFacing) = if (value == null) null else { + override def toLocal(value: Direction) = if (value == null) null else { val p = pitch val y = yaw if (p != null && y != null) RotationHelper.toLocal(pitch, yaw, value) else null } - override def toGlobal(value: EnumFacing) = if (value == null) null else { + override def toGlobal(value: Direction) = if (value == null) null else { val p = pitch val y = yaw if (p != null && y != null) RotationHelper.toGlobal(pitch, yaw, value) else null } - def validFacings = Array(EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.EAST) + def validFacings = Array(Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) // ----------------------------------------------------------------------- // @@ -110,36 +118,36 @@ trait Rotatable extends RotationAware with internal.Rotatable { ServerPacketSender.sendRotatableState(this) } else { - getWorld.notifyBlockUpdate(getPos) + getLevel.notifyBlockUpdate(getBlockPos) } - getWorld.notifyNeighborsOfStateChange(getPos, getBlockType, false) + getLevel.updateNeighborsAt(getBlockPos, getBlockState.getBlock) } // ----------------------------------------------------------------------- // /** Updates cached translation array and sends notification to clients. */ protected def updateTranslation(): Unit = { - if (getWorld != null) { + if (getLevel != null) { onRotationChanged() } } /** Validates new values against the allowed rotations as set in our block. */ - protected def trySetPitchYaw(pitch: EnumFacing, yaw: EnumFacing) = { - val oldState = getWorld.getBlockState(getPos) - def setState(newState: IBlockState): Boolean = { + protected def trySetPitchYaw(pitch: Direction, yaw: Direction) = { + val oldState = getLevel.getBlockState(getBlockPos) + def setState(newState: BlockState): Boolean = { if (oldState.hashCode() != newState.hashCode()) { - getWorld.setBlockState(getPos, newState) + getLevel.setBlockAndUpdate(getBlockPos, newState) updateTranslation() true } else false } - getBlockType match { - case rotatable if oldState.getProperties.containsKey(PropertyRotatable.Pitch) && oldState.getProperties.containsKey(PropertyRotatable.Yaw) => - setState(oldState.withProperty(PropertyRotatable.Pitch, pitch).withProperty(PropertyRotatable.Yaw, yaw)) - case rotatable if oldState.getProperties.containsKey(PropertyRotatable.Facing) => - setState(oldState.withProperty(PropertyRotatable.Facing, yaw)) + getBlockState.getBlock match { + case rotatable if oldState.hasProperty(PropertyRotatable.Pitch) && oldState.hasProperty(PropertyRotatable.Yaw) => + setState(oldState.setValue(PropertyRotatable.Pitch, pitch).setValue(PropertyRotatable.Yaw, yaw)) + case rotatable if oldState.hasProperty(PropertyRotatable.Facing) => + setState(oldState.setValue(PropertyRotatable.Facing, yaw)) case _ => false } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala index 0ea62e3c16..c7979fa09e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala @@ -1,10 +1,11 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import li.cil.oc.util.RotationHelper +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn /** * Like Rotatable, but stores the rotation information in the TE's NBT instead @@ -16,10 +17,10 @@ trait RotatableTile extends Rotatable { // ----------------------------------------------------------------------- // /** One of Up, Down and North (where north means forward/no pitch). */ - private var _pitch = EnumFacing.NORTH + private var _pitch = Direction.NORTH /** One of the four cardinal directions. */ - private var _yaw = EnumFacing.SOUTH + private var _yaw = Direction.SOUTH // ----------------------------------------------------------------------- // // Accessors @@ -34,43 +35,43 @@ trait RotatableTile extends Rotatable { private final val PitchTag = Settings.namespace + "pitch" private final val YawTag = Settings.namespace + "yaw" - override def readFromNBTForServer(nbt: NBTTagCompound) = { - super.readFromNBTForServer(nbt) - if (nbt.hasKey(PitchTag)) { - pitch = EnumFacing.getFront(nbt.getInteger(PitchTag)) + override def loadForServer(nbt: CompoundNBT) = { + super.loadForServer(nbt) + if (nbt.contains(PitchTag)) { + pitch = Direction.from3DDataValue(nbt.getInt(PitchTag)) } - if (nbt.hasKey(YawTag)) { - yaw = EnumFacing.getFront(nbt.getInteger(YawTag)) + if (nbt.contains(YawTag)) { + yaw = Direction.from3DDataValue(nbt.getInt(YawTag)) } validatePitchAndYaw() } - override def writeToNBTForServer(nbt: NBTTagCompound) = { - super.writeToNBTForServer(nbt) - nbt.setInteger(PitchTag, pitch.ordinal) - nbt.setInteger(YawTag, yaw.ordinal) + override def saveForServer(nbt: CompoundNBT) = { + super.saveForServer(nbt) + nbt.putInt(PitchTag, pitch.ordinal) + nbt.putInt(YawTag, yaw.ordinal) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - pitch = EnumFacing.getFront(nbt.getInteger(PitchTag)) - yaw = EnumFacing.getFront(nbt.getInteger(YawTag)) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + pitch = Direction.from3DDataValue(nbt.getInt(PitchTag)) + yaw = Direction.from3DDataValue(nbt.getInt(YawTag)) validatePitchAndYaw() } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setInteger(PitchTag, pitch.ordinal) - nbt.setInteger(YawTag, yaw.ordinal) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + nbt.putInt(PitchTag, pitch.ordinal) + nbt.putInt(YawTag, yaw.ordinal) } private def validatePitchAndYaw() { if (!_pitch.getAxis.isVertical) { - _pitch = EnumFacing.NORTH + _pitch = Direction.NORTH } if (!_yaw.getAxis.isHorizontal) { - _yaw = EnumFacing.SOUTH + _yaw = Direction.SOUTH } updateTranslation() } @@ -78,7 +79,7 @@ trait RotatableTile extends Rotatable { // ----------------------------------------------------------------------- // /** Validates new values against the allowed rotations as set in our block. */ - override protected def trySetPitchYaw(pitch: EnumFacing, yaw: EnumFacing) = { + override protected def trySetPitchYaw(pitch: Direction, yaw: Direction) = { var changed = false if (pitch != _pitch) { changed = true diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RotationAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RotationAware.scala index 908f8d52d8..8c134796ab 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RotationAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RotationAware.scala @@ -1,9 +1,9 @@ package li.cil.oc.common.tileentity.traits -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction trait RotationAware extends TileEntity { - def toLocal(value: EnumFacing) = value + def toLocal(value: Direction) = value - def toGlobal(value: EnumFacing) = value + def toGlobal(value: Direction) = value } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala index bc0ae7798f..59212aba8f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala @@ -5,9 +5,9 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.internal import li.cil.oc.api.network.Node -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait TextBuffer extends Environment with Tickable { lazy val buffer: internal.TextBuffer = { @@ -32,24 +32,24 @@ trait TextBuffer extends Environment with Tickable { // ----------------------------------------------------------------------- // - override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { - super.readFromNBTForServer(nbt) - buffer.load(nbt) + override def loadForServer(nbt: CompoundNBT): Unit = { + super.loadForServer(nbt) + buffer.loadData(nbt) } - override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - super.writeToNBTForServer(nbt) - buffer.save(nbt) + override def saveForServer(nbt: CompoundNBT): Unit = { + super.saveForServer(nbt) + buffer.saveData(nbt) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - buffer.load(nbt) + @OnlyIn(Dist.CLIENT) + override def loadForClient(nbt: CompoundNBT) { + super.loadForClient(nbt) + buffer.loadData(nbt) } - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - buffer.save(nbt) + override def saveForClient(nbt: CompoundNBT) { + super.saveForClient(nbt) + buffer.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Tickable.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Tickable.scala index 9632cb67e9..b7e65db5b4 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Tickable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Tickable.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.tileentity.traits -import net.minecraft.util.ITickable +import net.minecraft.tileentity.ITickableTileEntity -trait Tickable extends TileEntity with ITickable { - override def update(): Unit = updateEntity() +trait Tickable extends TileEntity with ITickableTileEntity { + override def tick(): Unit = updateEntity() } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/TileEntity.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/TileEntity.scala index 4607894d4d..a4e96ff011 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/TileEntity.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/TileEntity.scala @@ -6,50 +6,50 @@ import li.cil.oc.client.Sound import li.cil.oc.common.SaveHandler import li.cil.oc.util.BlockPosition import li.cil.oc.util.SideTracker -import net.minecraft.block.state.IBlockState -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.block.BlockState +import net.minecraft.nbt.CompoundNBT import net.minecraft.network.NetworkManager -import net.minecraft.network.play.server.SPacketUpdateTileEntity +import net.minecraft.network.play.server.SUpdateTileEntityPacket import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait TileEntity extends net.minecraft.tileentity.TileEntity { private final val IsServerDataTag = Settings.namespace + "isServerData" - def x: Int = getPos.getX + def x: Int = getBlockPos.getX - def y: Int = getPos.getY + def y: Int = getBlockPos.getY - def z: Int = getPos.getZ + def z: Int = getBlockPos.getZ - def position = BlockPosition(x, y, z, getWorld) + def position = BlockPosition(x, y, z, getLevel) def isClient: Boolean = !isServer - def isServer: Boolean = if (getWorld != null) !getWorld.isRemote else SideTracker.isServer + def isServer: Boolean = if (getLevel != null) !getLevel.isClientSide else SideTracker.isServer // ----------------------------------------------------------------------- // def updateEntity() { - if (Settings.get.periodicallyForceLightUpdate && getWorld.getTotalWorldTime % 40 == 0 && getBlockType.getLightValue(getWorld.getBlockState(getPos), getWorld, getPos) > 0) { - getWorld.notifyBlockUpdate(getPos, getWorld.getBlockState(getPos), getWorld.getBlockState(getPos), 3) + if (Settings.get.periodicallyForceLightUpdate && getLevel.getGameTime % 40 == 0 && getBlockState.getBlock.getLightValue(getLevel.getBlockState(getBlockPos), getLevel, getBlockPos) > 0) { + getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) } } - override def validate() { - super.validate() + override def clearRemoved() { + super.clearRemoved() initialize() } - override def invalidate() { - super.invalidate() + override def setRemoved() { + super.setRemoved() dispose() } - override def onChunkUnload() { - super.onChunkUnload() + override def onChunkUnloaded() { + super.onChunkUnloaded() try dispose() catch { case t: Throwable => OpenComputers.log.error("Failed properly disposing a tile entity, things may leak and or break.", t) } @@ -67,53 +67,52 @@ trait TileEntity extends net.minecraft.tileentity.TileEntity { // ----------------------------------------------------------------------- // - override def shouldRefresh(world: World, pos: BlockPos, oldState: IBlockState, newSate: IBlockState): Boolean = oldState.getBlock != newSate.getBlock + def loadForServer(nbt: CompoundNBT) {} - def readFromNBTForServer(nbt: NBTTagCompound): Unit = super.readFromNBT(nbt) - - def writeToNBTForServer(nbt: NBTTagCompound): Unit = { - nbt.setBoolean(IsServerDataTag, true) - super.writeToNBT(nbt) + def saveForServer(nbt: CompoundNBT): Unit = { + nbt.putBoolean(IsServerDataTag, true) + super.save(nbt) } - @SideOnly(Side.CLIENT) - def readFromNBTForClient(nbt: NBTTagCompound) {} + @OnlyIn(Dist.CLIENT) + def loadForClient(nbt: CompoundNBT) {} - def writeToNBTForClient(nbt: NBTTagCompound): Unit = { - nbt.setBoolean(IsServerDataTag, false) + def saveForClient(nbt: CompoundNBT): Unit = { + nbt.putBoolean(IsServerDataTag, false) } // ----------------------------------------------------------------------- // - override def readFromNBT(nbt: NBTTagCompound): Unit = { + override def load(state: BlockState, nbt: CompoundNBT): Unit = { + super.load(state, nbt) if (isServer || nbt.getBoolean(IsServerDataTag)) { - readFromNBTForServer(nbt) + loadForServer(nbt) } else { - readFromNBTForClient(nbt) + loadForClient(nbt) } } - override def writeToNBT(nbt: NBTTagCompound): NBTTagCompound = { + override def save(nbt: CompoundNBT): CompoundNBT = { if (isServer) { - writeToNBTForServer(nbt) + saveForServer(nbt) } nbt } - override def getUpdatePacket: SPacketUpdateTileEntity = { + override def getUpdatePacket: SUpdateTileEntityPacket = { // Obfuscation workaround. If it works. val te = this.asInstanceOf[net.minecraft.tileentity.TileEntity] - new SPacketUpdateTileEntity(te.getPos, te.getBlockMetadata, te.getUpdateTag) + new SUpdateTileEntityPacket(te.getBlockPos, 0, te.getUpdateTag) } - override def getUpdateTag: NBTTagCompound = { + override def getUpdateTag: CompoundNBT = { val nbt = super.getUpdateTag // See comment on savingForClients variable. SaveHandler.savingForClients = true try { - try writeToNBTForClient(nbt) catch { + try saveForClient(nbt) catch { case e: Throwable => OpenComputers.log.warn("There was a problem writing a TileEntity description packet. Please report this if you see it!", e) } } finally { @@ -123,8 +122,8 @@ trait TileEntity extends net.minecraft.tileentity.TileEntity { nbt } - override def onDataPacket(manager: NetworkManager, packet: SPacketUpdateTileEntity) { - try readFromNBTForClient(packet.getNbtCompound) catch { + override def onDataPacket(manager: NetworkManager, packet: SUpdateTileEntityPacket) { + try loadForClient(packet.getTag) catch { case e: Throwable => OpenComputers.log.warn("There was a problem reading a TileEntity description packet. Please report this if you see it!", e) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala index 9d31151b8a..1d23e60f60 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.tileentity.traits.power import java.util -import appeng.api.AEApi +import appeng.api._ import appeng.api.config.Actionable import appeng.api.config.PowerMultiplier import appeng.api.networking._ @@ -10,10 +10,12 @@ import appeng.api.util.{AECableType, AEColor, AEPartLocation, DimensionalCoord} import li.cil.oc.Settings import li.cil.oc.common.EventHandler import li.cil.oc.integration.Mods +import li.cil.oc.integration.appeng.AEUtil import li.cil.oc.integration.util.Power import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraftforge.fml.common._ @@ -27,12 +29,11 @@ trait AppliedEnergistics2 extends Common with IGridHost { override def updateEntity() { super.updateEntity() - if (useAppliedEnergistics2Power && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (useAppliedEnergistics2Power && getLevel.getGameTime % Settings.get.tickFrequency == 0) { updateEnergy() } } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) private def updateEnergy() { tryAllSides((demand, _) => { val grid = getGridNode(AEPartLocation.INTERNAL).getGrid @@ -47,37 +48,36 @@ trait AppliedEnergistics2 extends Common with IGridHost { }, Power.fromAE, Power.toAE) } - override def validate() { - super.validate() + override def clearRemoved() { + super.clearRemoved() if (useAppliedEnergistics2Power) EventHandler.scheduleAE2Add(this) } - override def invalidate() { - super.invalidate() + override def setRemoved() { + super.setRemoved() if (useAppliedEnergistics2Power) securityBreak() } - override def onChunkUnload() { - super.onChunkUnload() + override def onChunkUnloaded() { + super.onChunkUnloaded() if (useAppliedEnergistics2Power) securityBreak() } // ----------------------------------------------------------------------- // - override def readFromNBTForServer(nbt: NBTTagCompound) { - super.readFromNBTForServer(nbt) + override def loadForServer(nbt: CompoundNBT) { + super.loadForServer(nbt) if (useAppliedEnergistics2Power) loadNode(nbt) } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) - private def loadNode(nbt: NBTTagCompound): Unit = { + private def loadNode(nbt: CompoundNBT): Unit = { getGridNode(AEPartLocation.INTERNAL).loadFromNBT(Settings.namespace + "ae2power", nbt) } - override def setWorld(worldIn: World): Unit = { - if (getWorld == worldIn) + override def setLevelAndPosition(worldIn: World, pos: BlockPos): Unit = { + if (getLevel == worldIn) return - super.setWorld(worldIn) + super.setLevelAndPosition(worldIn, pos) if (worldIn != null && isServer && useAppliedEnergistics2Power) { val gridNode = getGridNode(AEPartLocation.INTERNAL) if (gridNode != null) { @@ -86,32 +86,28 @@ trait AppliedEnergistics2 extends Common with IGridHost { } } - override def writeToNBTForServer(nbt: NBTTagCompound) { - super.writeToNBTForServer(nbt) + override def saveForServer(nbt: CompoundNBT) { + super.saveForServer(nbt) if (useAppliedEnergistics2Power) saveNode(nbt) } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) - private def saveNode(nbt: NBTTagCompound): Unit = { + private def saveNode(nbt: CompoundNBT): Unit = { getGridNode(AEPartLocation.INTERNAL).saveToNBT(Settings.namespace + "ae2power", nbt) } // ----------------------------------------------------------------------- // - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) def getGridNode(side: AEPartLocation): IGridNode = node match { case Some(gridNode: IGridNode) => gridNode case _ if isServer => - val gridNode = AEApi.instance.grid.createGridNode(new AppliedEnergistics2GridBlock(this)) + val gridNode = AEUtil.aeApi.get.grid.createGridNode(new AppliedEnergistics2GridBlock(this)) node = Option(gridNode) gridNode case _ => null } - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) def getCableConnectionType(side: AEPartLocation): AECableType = AECableType.SMART - @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) def securityBreak() { getGridNode(AEPartLocation.INTERNAL).destroy() } @@ -130,12 +126,10 @@ class AppliedEnergistics2GridBlock(val tileEntity: AppliedEnergistics2) extends override def onGridNotification(p1: GridNotification): Unit = {} - override def setNetworkStatus(p1: IGrid, p2: Int): Unit = {} - - override def getConnectableSides: util.EnumSet[EnumFacing] = { - val connectableSides = JavaConversions.asJavaCollection(EnumFacing.values.filter(tileEntity.canConnectPower)) + override def getConnectableSides: util.EnumSet[Direction] = { + val connectableSides = JavaConversions.asJavaCollection(Direction.values.filter(tileEntity.canConnectPower)) if (connectableSides.isEmpty) { - val s = util.EnumSet.copyOf(JavaConversions.asJavaCollection(EnumFacing.values)) + val s = util.EnumSet.copyOf(JavaConversions.asJavaCollection(Direction.values)) s.clear() s } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Common.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Common.scala index fc240c9d95..c25351b9b9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Common.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Common.scala @@ -3,25 +3,25 @@ package li.cil.oc.common.tileentity.traits.power import li.cil.oc.Settings import li.cil.oc.api.network.Connector import li.cil.oc.common.tileentity.traits.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn trait Common extends TileEntity { - @SideOnly(Side.CLIENT) - protected def hasConnector(side: EnumFacing) = false + @OnlyIn(Dist.CLIENT) + protected def hasConnector(side: Direction) = false - protected def connector(side: EnumFacing): Option[Connector] = None + protected def connector(side: Direction): Option[Connector] = None // ----------------------------------------------------------------------- // def energyThroughput: Double - protected def tryAllSides(provider: (Double, EnumFacing) => Double, fromOther: Double => Double, toOther: Double => Double) { + protected def tryAllSides(provider: (Double, Direction) => Double, fromOther: Double => Double, toOther: Double => Double) { // We make sure to only call this every `Settings.get.tickFrequency` ticks, // but our throughput is per tick, so multiply this up for actual budget. var budget = energyThroughput * Settings.get.tickFrequency - for (side <- EnumFacing.values) { + for (side <- Direction.values) { val demand = toOther(math.min(budget, globalDemand(side))) if (demand > 1) { val energy = fromOther(provider(demand, side)) @@ -34,7 +34,7 @@ trait Common extends TileEntity { // ----------------------------------------------------------------------- // - def canConnectPower(side: EnumFacing) = + def canConnectPower(side: Direction) = !Settings.get.ignorePower && (if (isClient) hasConnector(side) else connector(side).isDefined) /** @@ -45,7 +45,7 @@ trait Common extends TileEntity { * @param doReceive whether to actually inject energy or only simulate it. * @return the amount of energy that was actually injected. */ - def tryChangeBuffer(side: EnumFacing, amount: Double, doReceive: Boolean = true): Double = + def tryChangeBuffer(side: Direction, amount: Double, doReceive: Boolean = true): Double = if (isClient || Settings.get.ignorePower) 0 else connector(side) match { case Some(node) => @@ -55,19 +55,19 @@ trait Common extends TileEntity { case _ => 0 } - def globalBuffer(side: EnumFacing): Double = + def globalBuffer(side: Direction): Double = if (isClient) 0 else connector(side) match { case Some(node) => node.globalBuffer case _ => 0 } - def globalBufferSize(side: EnumFacing): Double = + def globalBufferSize(side: Direction): Double = if (isClient) 0 else connector(side) match { case Some(node) => node.globalBufferSize case _ => 0 } - def globalDemand(side: EnumFacing) = math.max(0, math.min(energyThroughput, globalBufferSize(side) - globalBuffer(side))) + def globalDemand(side: Direction) = math.max(0, math.min(energyThroughput, globalBufferSize(side) - globalBuffer(side))) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala deleted file mode 100644 index f41d755fe8..0000000000 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/Mekanism.scala +++ /dev/null @@ -1,29 +0,0 @@ -package li.cil.oc.common.tileentity.traits.power -/* TODO Mekanism - -import cpw.mods.fml.common.Optional -import li.cil.oc.common.asm.Injectable -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.Power -import net.minecraftforge.common.util.ForgeDirection - -@Injectable.Interface(value = "mekanism.api.energy.IStrictEnergyAcceptor", modid = Mods.IDs.Mekanism) -trait Mekanism extends Common { - @Optional.Method(modid = Mods.IDs.Mekanism) - def canReceiveEnergy(side: ForgeDirection) = Mods.Mekanism.isAvailable && canConnectPower(side) - - @Optional.Method(modid = Mods.IDs.Mekanism) - def transferEnergyToAcceptor(side: ForgeDirection, amount: Double) = - if (!Mods.Mekanism.isAvailable) 0 - else Power.toJoules(tryChangeBuffer(side, Power.fromJoules(amount))) - - @Optional.Method(modid = Mods.IDs.Mekanism) - def getMaxEnergy = Power.toJoules(ForgeDirection.VALID_DIRECTIONS.map(globalBufferSize).max) - - @Optional.Method(modid = Mods.IDs.Mekanism) - def getEnergy = Power.toJoules(ForgeDirection.VALID_DIRECTIONS.map(globalBuffer).max) - - @Optional.Method(modid = Mods.IDs.Mekanism) - def setEnergy(energy: Double) {} -} -*/ \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index 8f1a159c1e..3fea571c55 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -1,11 +1,13 @@ package li.cil.oc.integration +import java.util.Optional + import li.cil.oc.Settings import li.cil.oc.integration -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.ModAPIManager -import net.minecraftforge.fml.common.versioning.ArtifactVersion -import net.minecraftforge.fml.common.versioning.VersionParser +import net.minecraftforge.fml.ModList +import net.minecraftforge.fml.ModContainer +import net.minecraftforge.forgespi.language.MavenVersionAdapter +import org.apache.maven.artifact.versioning.ArtifactVersion import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -25,7 +27,7 @@ object Mods { val Mekanism = new SimpleMod(IDs.Mekanism) val Minecraft = new SimpleMod(IDs.Minecraft) val OpenComputers = new SimpleMod(IDs.OpenComputers) - val TIS3D = new SimpleMod(IDs.TIS3D, version = "@[0.9,)") + val TIS3D = new SimpleMod(IDs.TIS3D, version = "[0.9,)") val Waila = new SimpleMod(IDs.Waila) val ProjectRedTransmission = new SimpleMod((IDs.ProjectRedTransmission)) val DraconicEvolution = new SimpleMod(IDs.DraconicEvolution) @@ -86,6 +88,8 @@ object Mods { // ----------------------------------------------------------------------- // + private def optionToScala[T](opt: Optional[T]): Option[T] = if (opt.isPresent) Some(opt.get) else None + trait ModBase extends Mod { knownMods += this @@ -93,24 +97,22 @@ object Mods { def id: String - def container = Option(Loader.instance.getIndexedModList.get(id)) + def container: Option[ModContainer] = optionToScala(ModList.get.getModContainerById(id)) - def version: Option[ArtifactVersion] = container.map(_.getProcessedVersion) + def version: Option[ArtifactVersion] = container.map(_.getModInfo.getVersion) } class SimpleMod(val id: String, version: String = "") extends ModBase { - private lazy val isModAvailable_ = { - val version = VersionParser.parseVersionReference(id + this.version) - if (Loader.isModLoaded(version.getLabel)) - version.containsVersion(Loader.instance.getIndexedModList.get(version.getLabel).getProcessedVersion) - else ModAPIManager.INSTANCE.hasAPI(version.getLabel) + private lazy val isModAvailable_ = optionToScala(ModList.get.getModContainerById(id)) match { + case Some(container) => version.isEmpty || MavenVersionAdapter.createFromVersionSpec(version).containsVersion(container.getModInfo.getVersion) + case _ => false } def isModAvailable: Boolean = isModAvailable_ } class ClassBasedMod(val id: String, val classNames: String*) extends ModBase { - private lazy val isModAvailable_ = Loader.isModLoaded(id) && classNames.forall(className => try Class.forName(className) != null catch { + private lazy val isModAvailable_ = ModList.get.isLoaded(id) && classNames.forall(className => try Class.forName(className) != null catch { case _: Throwable => false }) diff --git a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala index a912105dcf..5eb260a9a3 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala @@ -1,8 +1,9 @@ package li.cil.oc.integration.appeng +import java.util.Optional import javax.annotation.Nonnull -import appeng.api.AEApi +import appeng.api._ import appeng.api.networking.IGrid import appeng.api.networking.crafting.ICraftingGrid import appeng.api.networking.energy.IEnergyGrid @@ -12,57 +13,57 @@ import appeng.api.storage.data.{IAEFluidStack, IAEItemStack} import appeng.api.storage.IStorageHelper import li.cil.oc.integration.Mods import net.minecraft.item.ItemStack -import net.minecraftforge.fml.common.versioning.VersionRange -import net.minecraftforge.fml.common.Loader +import net.minecraftforge.fml.ModList +import net.minecraftforge.forgespi.language.MavenVersionAdapter +import org.apache.maven.artifact.versioning.VersionRange -object AEUtil { - val versionsWithNewItemDefinitionAPI: VersionRange = VersionRange.createFromVersionSpec("[rv6-stable-5,)") +@AEAddon +object AEUtil extends IAEAddon { + var aeApi: Option[IAppEngApi] = None - val itemStorageChannel: IItemStorageChannel = AEApi.instance.storage.getStorageChannel[IAEItemStack, IItemStorageChannel](classOf[IItemStorageChannel]) - val fluidStorageChannel: IFluidStorageChannel = AEApi.instance.storage.getStorageChannel[IAEFluidStack, IFluidStorageChannel](classOf[IFluidStorageChannel]) + private def optionToScala[T](opt: Optional[T]): Option[T] = if (opt.isPresent) Some(opt.get) else None - def useNewItemDefinitionAPI: Boolean = versionsWithNewItemDefinitionAPI.containsVersion( - Loader.instance.getIndexedModList.get(Mods.AppliedEnergistics2.id).getProcessedVersion) + def onAPIAvailable(aeApi: IAppEngApi) { + AEUtil.aeApi = Some(aeApi) + itemStorageChannel = aeApi.storage.getStorageChannel[IAEItemStack, IItemStorageChannel](classOf[IItemStorageChannel]) + fluidStorageChannel = aeApi.storage.getStorageChannel[IAEFluidStack, IFluidStorageChannel](classOf[IFluidStorageChannel]) + } + + val versionsWithNewItemDefinitionAPI: VersionRange = MavenVersionAdapter.createFromVersionSpec("[rv6-stable-5,)") + + var itemStorageChannel: IItemStorageChannel = null + var fluidStorageChannel: IFluidStorageChannel = null + + def useNewItemDefinitionAPI: Boolean = optionToScala(ModList.get.getModContainerById(Mods.AppliedEnergistics2.id)). + filter(container => versionsWithNewItemDefinitionAPI.containsVersion(container.getModInfo.getVersion)).isDefined // ----------------------------------------------------------------------- // - def controllerClass: Class[_] = { - if (AEApi.instance != null) { - val maybe = AEApi.instance.definitions.blocks.controller.maybeEntity - if (maybe.isPresent) - maybe.get() - else - null: Class[_] - } - else null: Class[_] - } + def controllerClass: Class[_] = aeApi.flatMap(api => optionToScala(api.definitions.blocks.controller.maybeEntity)).orNull // ----------------------------------------------------------------------- // - def interfaceClass: Class[_] = - if (AEApi.instance != null) - AEApi.instance.definitions.blocks.iface.maybeEntity.get() - else null: Class[_] + def interfaceClass: Class[_] = aeApi.map(api => api.definitions.blocks.iface.maybeEntity.get).orNull // ----------------------------------------------------------------------- // - def isController(stack: ItemStack): Boolean = stack != null && AEApi.instance != null && AEApi.instance.definitions.blocks.controller.isSameAs(stack) + def isController(stack: ItemStack): Boolean = stack != null && aeApi.filter(_.definitions.blocks.controller.isSameAs(stack)).isDefined // ----------------------------------------------------------------------- // - def isExportBus(stack: ItemStack): Boolean = stack != null && AEApi.instance != null && AEApi.instance.definitions.parts.exportBus.isSameAs(stack) + def isExportBus(stack: ItemStack): Boolean = stack != null && aeApi.filter(_.definitions.parts.exportBus.isSameAs(stack)).isDefined // ----------------------------------------------------------------------- // - def isImportBus(stack: ItemStack): Boolean = stack != null && AEApi.instance != null && AEApi.instance.definitions.parts.importBus.isSameAs(stack) + def isImportBus(stack: ItemStack): Boolean = stack != null && aeApi.filter(_.definitions.parts.importBus.isSameAs(stack)).isDefined // ----------------------------------------------------------------------- // - def isBlockInterface(stack: ItemStack): Boolean = stack != null && AEApi.instance != null && AEApi.instance.definitions.blocks.iface.isSameAs(stack) + def isBlockInterface(stack: ItemStack): Boolean = stack != null && aeApi.filter(_.definitions.blocks.iface.isSameAs(stack)).isDefined // ----------------------------------------------------------------------- // - def isPartInterface(stack: ItemStack): Boolean = stack != null && AEApi.instance != null && AEApi.instance.definitions.parts.iface.isSameAs(stack) + def isPartInterface(stack: ItemStack): Boolean = stack != null && aeApi.filter(_.definitions.parts.iface.isSameAs(stack)).isDefined // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java index d3499f887e..573cea61c7 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java @@ -1,10 +1,8 @@ package li.cil.oc.integration.appeng; -import appeng.api.AEApi; import appeng.api.implementations.items.IStorageCell; -import appeng.api.storage.ICellInventory; -import appeng.api.storage.ICellInventoryHandler; -import appeng.api.storage.IMEInventoryHandler; +import appeng.api.storage.cells.ICellInventory; +import appeng.api.storage.cells.ICellInventoryHandler; import appeng.api.storage.channels.IItemStorageChannel; import li.cil.oc.api.driver.Converter; import net.minecraft.item.ItemStack; @@ -22,7 +20,7 @@ public void convert(final Object value, final Map output) { output.put("remainingItemTypes", cell.getRemainingItemTypes()); output.put("getTotalItemTypes", cell.getTotalItemTypes()); - output.put("getAvailableItems", cell.getAvailableItems(AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList())); + output.put("getAvailableItems", cell.getAvailableItems(AEUtil.aeApi().get().storage().getStorageChannel(IItemStorageChannel.class).createList())); output.put("totalBytes", cell.getTotalBytes()); output.put("freeBytes", cell.getFreeBytes()); @@ -34,9 +32,9 @@ public void convert(final Object value, final Map output) { output.put("fuzzyMode", cell.getFuzzyMode().toString()); output.put("name", cell.getItemStack().getDisplayName()); } else if (value instanceof ICellInventoryHandler) { - convert(((ICellInventoryHandler) value).getCellInv(), output); + convert(((ICellInventoryHandler) value).getCellInv(), output); } else if ((value instanceof ItemStack) && (((ItemStack)value).getItem() instanceof IStorageCell)) { - IMEInventoryHandler inventory = AEApi.instance().registries().cell().getCellInventory((ItemStack) value, null, AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)); + ICellInventoryHandler inventory = AEUtil.aeApi().get().registries().cell().getCellInventory((ItemStack) value, null, AEUtil.aeApi().get().storage().getStorageChannel(IItemStorageChannel.class)); if (inventory != null) convert(((ICellInventoryHandler) inventory).getCellInv(), output); } diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala index 8ff06a7148..1902a55e1a 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverBlockInterface.scala @@ -18,15 +18,15 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper._ import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverBlockInterface extends DriverSidedTileEntity { def getTileEntityClass: Class[_] = AEUtil.interfaceClass - def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntity with ISegmentedInventory with IActionHost with IGridHost]) + def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[TileEntity with ISegmentedInventory with IActionHost with IGridHost]) final class Environment(val tile: TileEntity with ISegmentedInventory with IActionHost with IGridHost) extends ManagedTileEntityEnvironment[TileEntity with ISegmentedInventory with IActionHost](tile, "me_interface") with NamedBlock with NetworkControl[TileEntity with ISegmentedInventory with IActionHost with IGridHost] { override def preferredName = "me_interface" diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala index 10a900d4ff..86673f8d7c 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala @@ -10,7 +10,7 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World @@ -21,8 +21,8 @@ object DriverController extends DriverSidedTileEntity { def getTileEntityClass = AEUtil.controllerClass - def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileController]) + def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[TileController]) final class Environment(val tile: TileController) extends ManagedTileEntityEnvironment[TileController](tile, "me_controller") with NamedBlock with NetworkControl[TileController] { override def preferredName = "me_controller" diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index 5f2f03696d..f7e3615518 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -1,6 +1,5 @@ package li.cil.oc.integration.appeng -import appeng.api.AEApi import appeng.api.config.{Actionable, FuzzyMode, Settings, Upgrades} import appeng.api.implementations.IUpgradeableHost import appeng.api.implementations.tiles.ISegmentedInventory @@ -22,19 +21,19 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ResultWrapper._ import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.world.World import net.minecraft.util.math.BlockPos import net.minecraftforge.items.IItemHandler object DriverExportBus extends driver.DriverBlock { - override def worksWith(world: World, pos: BlockPos, side: EnumFacing) = - world.getTileEntity(pos) match { - case container: IPartHost => EnumFacing.VALUES.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isExportBus) + override def worksWith(world: World, pos: BlockPos, side: Direction) = + world.getBlockEntity(pos) match { + case container: IPartHost => Direction.values.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isExportBus) case _ => false } - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing) = new Environment(world.getTileEntity(pos).asInstanceOf[IPartHost]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction) = new Environment(world.getBlockEntity(pos).asInstanceOf[IPartHost]) final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_exportbus") with NamedBlock with PartEnvironmentBase { override def preferredName = "me_exportbus" diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala index b18d15387f..ff57e10b24 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverImportBus.scala @@ -10,18 +10,18 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.integration.ManagedTileEntityEnvironment import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverImportBus extends driver.DriverBlock { - override def worksWith(world: World, pos: BlockPos, side: EnumFacing) = - world.getTileEntity(pos) match { - case container: IPartHost => EnumFacing.VALUES.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isImportBus) + override def worksWith(world: World, pos: BlockPos, side: Direction) = + world.getBlockEntity(pos) match { + case container: IPartHost => Direction.values.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isImportBus) case _ => false } - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing) = new Environment(world.getTileEntity(pos).asInstanceOf[IPartHost]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction) = new Environment(world.getBlockEntity(pos).asInstanceOf[IPartHost]) final class Environment(val host: IPartHost) extends ManagedTileEntityEnvironment[IPartHost](host, "me_importbus") with NamedBlock with PartEnvironmentBase { override def preferredName = "me_importbus" diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala index c004b96a06..a1e0070f16 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverPartInterface.scala @@ -12,29 +12,29 @@ import li.cil.oc.api.machine.Context import li.cil.oc.integration.ManagedTileEntityEnvironment import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverPartInterface extends driver.DriverBlock { - override def worksWith(world: World, pos: BlockPos, side: EnumFacing): Boolean = - world.getTileEntity(pos) match { + override def worksWith(world: World, pos: BlockPos, side: Direction): Boolean = + world.getBlockEntity(pos) match { case container: IPartHost => { - EnumFacing.VALUES.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isPartInterface) + Direction.values.map(container.getPart).filter(p => p != null).map(_.getItemStack(PartItemStack.PICK)).exists(AEUtil.isPartInterface) } case _ => false } - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): DriverPartInterface.Environment = { - val host: IPartHost = world.getTileEntity(pos).asInstanceOf[IPartHost] + override def createEnvironment(world: World, pos: BlockPos, side: Direction): DriverPartInterface.Environment = { + val host: IPartHost = world.getBlockEntity(pos).asInstanceOf[IPartHost] val tile = host.asInstanceOf[TileEntity with IPartHost with ISegmentedInventory with IActionHost with IGridHost] val aePos: AEPartLocation = side match { - case EnumFacing.EAST => AEPartLocation.WEST - case EnumFacing.WEST => AEPartLocation.EAST - case EnumFacing.NORTH => AEPartLocation.SOUTH - case EnumFacing.SOUTH => AEPartLocation.NORTH - case EnumFacing.UP => AEPartLocation.DOWN - case EnumFacing.DOWN => AEPartLocation.UP + case Direction.EAST => AEPartLocation.WEST + case Direction.WEST => AEPartLocation.EAST + case Direction.NORTH => AEPartLocation.SOUTH + case Direction.SOUTH => AEPartLocation.NORTH + case Direction.UP => AEPartLocation.DOWN + case Direction.DOWN => AEPartLocation.UP } new Environment(host, tile, aePos) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/EventHandlerAE2.scala b/src/main/scala/li/cil/oc/integration/appeng/EventHandlerAE2.scala index 893c8079dd..e34424c16b 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/EventHandlerAE2.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/EventHandlerAE2.scala @@ -1,15 +1,15 @@ package li.cil.oc.integration.appeng import appeng.api.implementations.items.IAEWrench -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.EnumHand +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos object EventHandlerAE2 { - def useWrench(player: EntityPlayer, pos: BlockPos, changeDurability: Boolean): Boolean = { - player.getHeldItem(EnumHand.MAIN_HAND).getItem match { - case wrench: IAEWrench => wrench.canWrench(player.getHeldItem(EnumHand.MAIN_HAND), player, pos) + def useWrench(player: PlayerEntity, pos: BlockPos, changeDurability: Boolean): Boolean = { + player.getItemInHand(Hand.MAIN_HAND).getItem match { + case wrench: IAEWrench => wrench.canWrench(player.getItemInHand(Hand.MAIN_HAND), player, pos) case _ => false } } diff --git a/src/main/scala/li/cil/oc/integration/appeng/MachineSource.scala b/src/main/scala/li/cil/oc/integration/appeng/MachineSource.scala index 57ca274ee3..cbc50e12cc 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/MachineSource.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/MachineSource.scala @@ -3,10 +3,10 @@ package li.cil.oc.integration.appeng import java.util.Optional import appeng.api.networking.security.{IActionHost, IActionSource} -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity class MachineSource(val via: IActionHost) extends IActionSource { - def player: Optional[EntityPlayer] = Optional.empty[EntityPlayer] + def player: Optional[PlayerEntity] = Optional.empty[PlayerEntity] def machine: Optional[IActionHost] = Optional.of(this.via) diff --git a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala index 9b973b4dce..cba9ed33ef 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/ModAppEng.scala @@ -1,6 +1,5 @@ package li.cil.oc.integration.appeng -import appeng.api.AEApi import li.cil.oc.api import li.cil.oc.api.Driver import li.cil.oc.common.tileentity.Print @@ -14,7 +13,7 @@ object ModAppEng extends ModProxy { api.IMC.registerWrenchTool("li.cil.oc.integration.appeng.EventHandlerAE2.useWrench") api.IMC.registerWrenchToolCheck("li.cil.oc.integration.appeng.EventHandlerAE2.isWrench") - AEApi.instance.registries.movable.whiteListTileEntity(classOf[Print]) + AEUtil.aeApi.get.registries.movable.whiteListTileEntity(classOf[Print]) Driver.add(DriverController) Driver.add(DriverExportBus) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index b131af8527..4b9e2aeef8 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -2,7 +2,6 @@ package li.cil.oc.integration.appeng import java.util -import appeng.api.AEApi import appeng.api.config.Actionable import appeng.api.networking.{IGridHost, IGridNode} import appeng.api.networking.crafting.{ICraftingJob, ICraftingLink, ICraftingRequester} @@ -23,11 +22,15 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ResultWrapper._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.util.RegistryKey +import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos -import net.minecraftforge.common.DimensionManager +import net.minecraft.util.registry.{Registry => VanillaRegistry} +import net.minecraft.world.World import net.minecraftforge.common.util.Constants.NBT +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -44,7 +47,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IActionHost with IGridHos def node: Node private def aeCraftItem(aeItem: IAEItemStack): IAEItemStack = { - val patterns = AEUtil.getGridCrafting(tile.getGridNode(pos).getGrid).getCraftingFor(aeItem, null, 0, tile.getWorld) + val patterns = AEUtil.getGridCrafting(tile.getGridNode(pos).getGrid).getCraftingFor(aeItem, null, 0, tile.getLevel) patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(aeItem))) match { case Some(pattern) => pattern.getOutputs.find(_.isSameType(aeItem)).get case _ => aeItem.copy.setStackSize(0) // Should not be possible, but hey... @@ -308,7 +311,7 @@ object NetworkControl { if (delayData != null) { result(Unit, "waiting for ae network to load") } else { - if (controller == null || controller.isInvalid) { + if (controller == null || controller.isRemoved) { result(Unit, "no controller") } else { f(controller) @@ -347,7 +350,7 @@ object NetworkControl { val craftingGrid = AEUtil.getGridCrafting(gridNode.getGrid) val source = new MachineSource(controller) - val future = craftingGrid.beginCraftingJob(controller.getWorld, gridNode.getGrid, source, request, null) + val future = craftingGrid.beginCraftingJob(controller.getLevel, gridNode.getGrid, source, request, null) val cpu = if (!cpuName.isEmpty) { craftingGrid.getCpus.collectFirst({ case c if cpuName.equals(c.getName) => c @@ -392,19 +395,19 @@ object NetworkControl { private val MAX_BACKOFF_TICKS = 20 * 5 // 5 seconds private val BACKOFF_SCALE = 2 // multiply by this factor on each failure - private class EphemeralDelayData(val dimension: Int, val x: Int, val y: Int, val z: Int) { + private class EphemeralDelayData(val dimension: RegistryKey[World], val x: Int, val y: Int, val z: Int) { var delay: Int = 1 } private var delayData: EphemeralDelayData = _ // null unless delay loading is active // return true when we do not want to try again, either because we completely failed or we succeeded // return false when things appears just not ready yet - private def tryLoadGrid(dimension: Int, x: Int, y: Int, z: Int): Boolean = { - val world = DimensionManager.getWorld(dimension) + private def tryLoadGrid(dimension: RegistryKey[World], x: Int, y: Int, z: Int): Boolean = { + val world = ServerLifecycleHooks.getCurrentServer.getLevel(dimension) if (world == null) { return false // maybe the dimension isn't loaded yet } - val tileEntity = world.getTileEntity(new BlockPos(x, y, z)) + val tileEntity = world.getBlockEntity(new BlockPos(x, y, z)) if (tileEntity == null) { return false // maybe the chunk isn't loaded yet } @@ -442,17 +445,17 @@ object NetworkControl { } } - override def load(nbt: NBTTagCompound) { - super.load(nbt) - stack = AEUtil.itemStorageChannel.createStack(new ItemStack(nbt)) - links ++= nbt.getTagList(LINKS_KEY, NBT.TAG_COMPOUND).map( - (nbt: NBTTagCompound) => LinkCache.store(AEApi.instance.storage.loadCraftingLink(nbt, this))) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + stack = AEUtil.itemStorageChannel.createStack(ItemStack.of(nbt)) + links ++= nbt.getList(LINKS_KEY, NBT.TAG_COMPOUND).map( + (nbt: CompoundNBT) => LinkCache.store(AEUtil.aeApi.get.storage.loadCraftingLink(nbt, this))) pos = AEPartLocation.fromOrdinal(NbtDataStream.getOptInt(nbt, POS_KEY, AEPartLocation.INTERNAL.ordinal)) - if (nbt.hasKey(DIMENSION_KEY)) { - val dimension = nbt.getInteger(DIMENSION_KEY) - val x = nbt.getInteger(X_KEY) - val y = nbt.getInteger(Y_KEY) - val z = nbt.getInteger(Z_KEY) + if (nbt.contains(DIMENSION_KEY)) { + val dimension = RegistryKey.create(VanillaRegistry.DIMENSION_REGISTRY, new ResourceLocation(nbt.getString(DIMENSION_KEY))) + val x = nbt.getInt(X_KEY) + val y = nbt.getInt(Y_KEY) + val z = nbt.getInt(Z_KEY) delayData = new EphemeralDelayData(dimension, x, y, z) // all of this delay load could have been done nested // but i don't want infinite lambda nesting in cases where the load is never ready @@ -460,21 +463,21 @@ object NetworkControl { } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - stack.createItemStack().writeToNBT(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + stack.createItemStack().save(nbt) nbt.setNewTagList(LINKS_KEY, links.map((link) => { - val comp = new NBTTagCompound() + val comp = new CompoundNBT() link.writeToNBT(comp) comp })) if (pos != null) - nbt.setInteger(POS_KEY, pos.ordinal) - if (controller != null && !controller.isInvalid) { - nbt.setInteger(DIMENSION_KEY, controller.getWorld.provider.getDimension) - nbt.setInteger(X_KEY, controller.getPos.getX) - nbt.setInteger(Y_KEY, controller.getPos.getY) - nbt.setInteger(Z_KEY, controller.getPos.getZ) + nbt.putInt(POS_KEY, pos.ordinal) + if (controller != null && !controller.isRemoved) { + nbt.putString(DIMENSION_KEY, controller.getLevel.dimension.location.toString) + nbt.putInt(X_KEY, controller.getBlockPos.getX) + nbt.putInt(Y_KEY, controller.getBlockPos.getY) + nbt.putInt(Z_KEY, controller.getBlockPos.getZ) } } } @@ -530,17 +533,17 @@ object NetworkControl { private val FAILED_KEY: String = "failed" private val REASON_KEY: String = "reason" - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setBoolean(COMPUTING_KEY, isComputing) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putBoolean(COMPUTING_KEY, isComputing) if (link.nonEmpty) - nbt.setString(LINK_ID_KEY, link.get.getCraftingID) - nbt.setBoolean(FAILED_KEY, failed) - nbt.setString(REASON_KEY, reason) + nbt.putString(LINK_ID_KEY, link.get.getCraftingID) + nbt.putBoolean(FAILED_KEY, failed) + nbt.putString(REASON_KEY, reason) } - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) isComputing = NbtDataStream.getOptBoolean(nbt, COMPUTING_KEY, isComputing) val id = NbtDataStream.getOptString(nbt, LINK_ID_KEY, "") diff --git a/src/main/scala/li/cil/oc/integration/computercraft/CallableHelper.java b/src/main/scala/li/cil/oc/integration/computercraft/CallableHelper.java index c37bdf1c26..f4cf70effb 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/CallableHelper.java +++ b/src/main/scala/li/cil/oc/integration/computercraft/CallableHelper.java @@ -22,7 +22,7 @@ public int methodIndex(final String method) throws NoSuchMethodException { return index; } - public Object[] convertArguments(final Arguments args) throws UnsupportedEncodingException { + public static Object[] convertArguments(final Arguments args) throws UnsupportedEncodingException { final Object[] argArray = Iterables.toArray(args, Object.class); for (int i = 0; i < argArray.length; ++i) { if (argArray[i] instanceof byte[]) { diff --git a/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftFileSystem.scala b/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftFileSystem.scala index 0a129d38a3..5a88989028 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftFileSystem.scala @@ -1,5 +1,7 @@ package li.cil.oc.integration.computercraft +import java.nio.channels.Channels + import dan200.computercraft.api.filesystem.IMount import li.cil.oc.server.fs.InputStreamFileSystem @@ -27,7 +29,7 @@ class ComputerCraftFileSystem(val mount: IMount) extends InputStreamFileSystem { // ----------------------------------------------------------------------- // protected def openInputChannel(path: String) = try { - Some(new InputStreamChannel(mount.openForRead(path))) + Some(new InputStreamChannel(Channels.newInputStream(mount.openForRead(path)))) } catch { case _: Throwable => None } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftWritableFileSystem.scala b/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftWritableFileSystem.scala index 12be1052ce..d58e359bce 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftWritableFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/ComputerCraftWritableFileSystem.scala @@ -2,6 +2,7 @@ package li.cil.oc.integration.computercraft import java.io.IOException import java.io.OutputStream +import java.nio.channels.Channels import dan200.computercraft.api.filesystem.IWritableMount import li.cil.oc.api.fs.Mode @@ -27,8 +28,8 @@ class ComputerCraftWritableFileSystem(override val mount: IWritableMount) override protected def openOutputHandle(id: Int, path: String, mode: Mode): Option[OutputHandle] = try { Some(new ComputerCraftOutputHandle(mount, mode match { - case Mode.Append => mount.openForAppend(path) - case Mode.Write => mount.openForWrite(path) + case Mode.Append => Channels.newOutputStream(mount.openForAppend(path)) + case Mode.Write => Channels.newOutputStream(mount.openForWrite(path)) case _ => throw new IllegalArgumentException() }, this, id, path)) } catch { diff --git a/src/main/scala/li/cil/oc/integration/computercraft/ConverterLuaObject.java b/src/main/scala/li/cil/oc/integration/computercraft/ConverterLuaObject.java index d75fe499e3..c5504a2e04 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/ConverterLuaObject.java +++ b/src/main/scala/li/cil/oc/integration/computercraft/ConverterLuaObject.java @@ -1,6 +1,7 @@ package li.cil.oc.integration.computercraft; -import dan200.computercraft.api.lua.ILuaObject; +import dan200.computercraft.api.lua.IDynamicLuaObject; +import dan200.computercraft.api.lua.ObjectArguments; import li.cil.oc.api.driver.Converter; import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Context; @@ -12,13 +13,13 @@ public final class ConverterLuaObject implements Converter { @Override public void convert(final Object value, final Map output) { - if (value instanceof ILuaObject) { - output.put("value", new LuaObjectValue((ILuaObject) value)); + if (value instanceof IDynamicLuaObject) { + output.put("value", new LuaObjectValue((IDynamicLuaObject) value)); } } public static final class LuaObjectValue extends AbstractValue implements ManagedPeripheral { - private final ILuaObject value; + private final IDynamicLuaObject value; protected final CallableHelper helper; @@ -28,7 +29,7 @@ public LuaObjectValue() { helper = null; } - public LuaObjectValue(final ILuaObject value) { + public LuaObjectValue(final IDynamicLuaObject value) { this.value = value; helper = new CallableHelper(value.getMethodNames()); } @@ -44,8 +45,8 @@ public String[] methods() { public Object[] invoke(final String method, final Context context, final Arguments args) throws Exception { if (value != null) { final int index = helper.methodIndex(method); - final Object[] argArray = helper.convertArguments(args); - return value.callMethod(DriverPeripheral.Environment.UnsupportedLuaContext.instance(), index, argArray); + final Object[] argArray = CallableHelper.convertArguments(args); + return value.callMethod(DriverPeripheral.Environment.UnsupportedLuaContext.instance(), index, new ObjectArguments(argArray)).getResult(); } return new Object[]{null, "ComputerCraft userdata cannot be persisted"}; } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/DriverComputerCraftMedia.scala b/src/main/scala/li/cil/oc/integration/computercraft/DriverComputerCraftMedia.scala index 13c1be5d23..56760e395d 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/DriverComputerCraftMedia.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/DriverComputerCraftMedia.scala @@ -11,12 +11,12 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Slot import li.cil.oc.integration.opencomputers.Item import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object DriverComputerCraftMedia extends Item { override def worksWith(stack: ItemStack) = stack.getItem.isInstanceOf[IMedia] - override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = if (!host.world.isRemote) { + override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = if (!host.world.isClientSide) { val address = addressFromTag(dataTag(stack)) val mount = fromComputerCraft(stack.getItem.asInstanceOf[IMedia].createDataMount(stack, host.world)) Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), host, Settings.resourceDomain + ":floppy_access")) match { @@ -36,9 +36,9 @@ object DriverComputerCraftMedia extends Item { case ro: IMount => new ComputerCraftFileSystem(ro) } - private def addressFromTag(tag: NBTTagCompound) = - if (tag.hasKey("node") && tag.getCompoundTag("node").hasKey("address")) { - tag.getCompoundTag("node").getString("address") + private def addressFromTag(tag: CompoundNBT) = + if (tag.contains("node") && tag.getCompound("node").contains("address")) { + tag.getCompound("node").getString("address") } else java.util.UUID.randomUUID().toString @@ -51,9 +51,9 @@ object DriverComputerCraftMedia extends Item { media.setLabel(stack, value) } - override def load(nbt: NBTTagCompound) {} + override def loadData(nbt: CompoundNBT) {} - override def save(nbt: NBTTagCompound) {} + override def saveData(nbt: CompoundNBT) {} } } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java b/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java index a4368d2193..7f69d2ec1c 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java +++ b/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java @@ -5,8 +5,13 @@ import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaTask; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.lua.ObjectArguments; import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IWorkMonitor; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.apis.PeripheralAPI; +import dan200.computercraft.core.asm.PeripheralMethod; import li.cil.oc.OpenComputers; import li.cil.oc.Settings; import li.cil.oc.api.FileSystem; @@ -21,9 +26,10 @@ import li.cil.oc.util.Reflection; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.world.World; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -56,9 +62,9 @@ private boolean isBlacklisted(final Object o) { return false; } - private IPeripheral findPeripheral(final World world, final BlockPos pos, final EnumFacing side) { + private IPeripheral findPeripheral(final World world, final BlockPos pos, final Direction side) { try { - final IPeripheral p = dan200.computercraft.ComputerCraft.getPeripheralAt(world, pos, side); + final IPeripheral p = dan200.computercraft.shared.Peripherals.getPeripheral(world, pos, side, cap -> {}); if (!isBlacklisted(p)) { return p; } @@ -69,8 +75,8 @@ private IPeripheral findPeripheral(final World world, final BlockPos pos, final } @Override - public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) { - final TileEntity tileEntity = world.getTileEntity(pos); + public boolean worksWith(final World world, final BlockPos pos, final Direction side) { + final TileEntity tileEntity = world.getBlockEntity(pos); return tileEntity != null // This ensures we don't get duplicate components, in case the // tile entity is natively compatible with OpenComputers. @@ -83,32 +89,35 @@ public boolean worksWith(final World world, final BlockPos pos, final EnumFacing } @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { + public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final Direction side) { return new Environment(findPeripheral(world, pos, side)); } public static class Environment extends li.cil.oc.api.prefab.AbstractManagedEnvironment implements li.cil.oc.api.network.ManagedPeripheral, NamedBlock { protected final IPeripheral peripheral; - protected final CallableHelper helper; + protected final Map methods; + protected final String[] methodNames; protected final Map accesses = new HashMap(); public Environment(final IPeripheral peripheral) { this.peripheral = peripheral; - helper = new CallableHelper(peripheral.getMethodNames()); + methods = PeripheralAPI.getMethods(peripheral); + methodNames = methods.keySet().toArray(new String[methods.size()]); setNode(Network.newNode(this, Visibility.Network).create()); } @Override public String[] methods() { - return peripheral.getMethodNames(); + return methodNames; } @Override - public Object[] invoke(final String method, final Context context, final Arguments args) throws Exception { - final int index = helper.methodIndex(method); - final Object[] argArray = helper.convertArguments(args); + public Object[] invoke(final String name, final Context context, final Arguments args) throws Exception { + final Object[] argArray = CallableHelper.convertArguments(args); + final PeripheralMethod method = methods.get(name); + if (method == null) throw new NoSuchMethodException(); final FakeComputerAccess access; if (accesses.containsKey(context.node().address())) { access = accesses.get(context.node().address()); @@ -117,7 +126,7 @@ public Object[] invoke(final String method, final Context context, final Argumen // an onConnect for it. Create a temporary access. access = new FakeComputerAccess(this, context); } - return peripheral.callMethod(access, UnsupportedLuaContext.instance(), index, argArray); + return method.apply(peripheral, UnsupportedLuaContext.instance(), access, new ObjectArguments(argArray)).getResult(); } @Override @@ -237,6 +246,21 @@ public void queueEvent(final String event, final Object[] arguments) { public String getAttachmentName() { return owner.node().address(); } + + @Override + public Map getAvailablePeripherals() { + return Collections.emptyMap(); + } + + @Override + public IPeripheral getAvailablePeripheral(String name) { + return null; + } + + @Override + public IWorkMonitor getMainThreadMonitor() { + throw new UnsupportedOperationException(); + } } /** @@ -259,22 +283,7 @@ public long issueMainThreadTask(ILuaTask task) throws LuaException { } @Override - public Object[] executeMainThreadTask(ILuaTask task) throws LuaException, InterruptedException { - throw new UnsupportedOperationException(); - } - - @Override - public Object[] pullEvent(final String filter) throws LuaException, InterruptedException { - throw new UnsupportedOperationException(); - } - - @Override - public Object[] pullEventRaw(final String filter) throws InterruptedException { - throw new UnsupportedOperationException(); - } - - @Override - public Object[] yield(final Object[] arguments) throws InterruptedException { + public MethodResult executeMainThreadTask(ILuaTask task) throws LuaException { throw new UnsupportedOperationException(); } } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala index e0aaacfdef..c5ae02b1d4 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala @@ -4,17 +4,21 @@ import dan200.computercraft.api.ComputerCraftAPI import dan200.computercraft.api.peripheral.IPeripheral import dan200.computercraft.api.peripheral.IPeripheralProvider import li.cil.oc.common.tileentity.Relay -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier object PeripheralProvider extends IPeripheralProvider { def init() { ComputerCraftAPI.registerPeripheralProvider(this) } - override def getPeripheral(world: World, blockPos: BlockPos, enumFacing: EnumFacing): IPeripheral = world.getTileEntity(blockPos) match { - case relay: Relay => new RelayPeripheral(relay) - case _ => null + override def getPeripheral(world: World, blockPos: BlockPos, side: Direction): LazyOptional[IPeripheral] = world.getBlockEntity(blockPos) match { + case relay: Relay => LazyOptional.of(new NonNullSupplier[IPeripheral] { + override def get = new RelayPeripheral(relay) + }) + case _ => LazyOptional.empty[IPeripheral] } } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala index c030b3b90b..dd60721ddd 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala @@ -1,8 +1,11 @@ package li.cil.oc.integration.computercraft +import dan200.computercraft.api.lua.IArguments import dan200.computercraft.api.lua.ILuaContext import dan200.computercraft.api.lua.LuaException +import dan200.computercraft.api.lua.MethodResult import dan200.computercraft.api.peripheral.IComputerAccess +import dan200.computercraft.api.peripheral.IDynamicPeripheral import dan200.computercraft.api.peripheral.IPeripheral import li.cil.oc.Settings import li.cil.oc.api @@ -10,13 +13,13 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Component import li.cil.oc.common.tileentity.Relay import li.cil.oc.util.ResultWrapper._ -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class RelayPeripheral(val relay: Relay) extends IPeripheral { +class RelayPeripheral(val relay: Relay) extends IDynamicPeripheral { private val methods = Map[String, (IComputerAccess, ILuaContext, Array[AnyRef]) => Array[AnyRef]]( // Generic modem methods. "open" -> ((computer, context, arguments) => { @@ -111,8 +114,8 @@ class RelayPeripheral(val relay: Relay) extends IPeripheral { override def getMethodNames = methodNames - override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: Array[AnyRef]) = - try methods(methodNames(method))(computer, context, arguments) catch { + override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: IArguments) = + try MethodResult.of(methods(methodNames(method))(computer, context, arguments.getAll)) catch { case e: LuaException => throw e case t: Throwable => t.printStackTrace() @@ -140,7 +143,7 @@ class RelayPeripheral(val relay: Relay) extends IPeripheral { } private def visibleComponents = { - EnumFacing.values().flatMap(side => { + Direction.values().flatMap(side => { val node = relay.sidedNode(side) node.reachableNodes.collect { case component: Component if component.canBeSeenFrom(node) => component diff --git a/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java b/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java index 13ae86bd78..fa0c63bc1f 100644 --- a/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java +++ b/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java @@ -12,7 +12,7 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity; import li.cil.oc.integration.ManagedTileEntityEnvironment; import net.minecraft.world.World; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import java.util.Map; @@ -24,8 +24,8 @@ public Class getTileEntityClass() { } @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((TileFrequencyOwner) world.getTileEntity(pos)); + public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final Direction side) { + return new Environment((TileFrequencyOwner) world.getBlockEntity(pos)); } public static final class Environment extends ManagedTileEntityEnvironment implements NamedBlock { @@ -46,7 +46,7 @@ public int priority() { @Callback(doc = "function():table -- Get the currently set frequency. {left, middle, right}") public Object[] getFrequency(final Context context, final Arguments args) { Object[] frequencies = new Object[3]; - Frequency frequency = tileEntity.frequency; + Frequency frequency = tileEntity.getFrequency(); frequencies[0] = frequency.getLeft().ordinal(); frequencies[1] = frequency.getMiddle().ordinal(); frequencies[2] = frequency.getRight().ordinal(); @@ -79,18 +79,19 @@ public Object[] setFrequency(final Context context, final Arguments args) { EnumColour.fromWoolMeta(left), EnumColour.fromWoolMeta(middle), EnumColour.fromWoolMeta(right), - tileEntity.frequency.owner)); + tileEntity.getFrequency().owner, + tileEntity.getFrequency().ownerName)); return null; } @Callback(doc = "function():string -- Get the name of the owner, which is usually a player's name or 'global'.") public Object[] getOwner(final Context context, final Arguments args) { - return new Object[]{tileEntity.frequency.owner}; + return new Object[]{tileEntity.getFrequency().ownerName.getString()}; } @Callback(doc = "function():table -- Get the currently set frequency as a table of color names.") public Object[] getFrequencyColors(final Context context, final Arguments args){ - return new Object[]{tileEntity.frequency.toArray()}; + return new Object[]{tileEntity.getFrequency().toArray()}; } @Callback(doc = "function():table -- Get a table with the mapping of colors (as Minecraft names) to Frequency numbers. NB: Frequencies are zero based!") diff --git a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala index 47c394485e..48c9820fbe 100644 --- a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala @@ -2,21 +2,26 @@ package li.cil.oc.integration.jei import java.util -import javax.annotation.Nonnull import com.google.common.base.Strings +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.server.machine.Callbacks -import mezz.jei.api.IGuiHelper -import mezz.jei.api.IModRegistry -import mezz.jei.api.gui.IDrawable +import mezz.jei.api.constants.VanillaTypes import mezz.jei.api.gui.IRecipeLayout +import mezz.jei.api.gui.drawable.IDrawable +import mezz.jei.api.gui.drawable.IDrawableAnimated.StartDirection +import mezz.jei.api.helpers.IGuiHelper import mezz.jei.api.ingredients.IIngredients -import mezz.jei.api.recipe._ +import mezz.jei.api.recipe.category.IRecipeCategory +import mezz.jei.api.registration.IRecipeRegistration import net.minecraft.client.Minecraft import net.minecraft.item.ItemStack +import net.minecraft.util.ICharacterConsumer import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.CharacterManager.ISliceAcceptor +import net.minecraft.util.text.Style import net.minecraft.util.text.TextFormatting import scala.collection.convert.WrapAsJava._ @@ -29,7 +34,7 @@ object CallbackDocHandler { private val VexPattern = """(?s)^function(\(.*?\).*?); (.*)$""".r - def getRecipes(registry: IModRegistry): util.List[CallbackDocRecipe] = registry.getIngredientRegistry.getIngredients(classOf[ItemStack]).collect { + def getRecipes(registration: IRecipeRegistration): util.List[CallbackDocRecipe] = registration.getIngredientManager.getAllIngredients(VanillaTypes.ITEM).collect { case stack: ItemStack => val callbacks = api.Driver.environmentsFor(stack).flatMap(getCallbacks).toBuffer @@ -73,22 +78,15 @@ object CallbackDocHandler { } else Seq.empty - protected def wrap(line: String, width: Int): util.List[String] = Minecraft.getMinecraft.fontRenderer.listFormattedStringToWidth(line, width) - - object CallbackDocRecipeHandler extends IRecipeWrapperFactory[CallbackDocRecipe] { - override def getRecipeWrapper(recipe: CallbackDocRecipe): CallbackDocRecipe = recipe + protected def wrap(line: String, width: Int): util.List[String] = { + val list = new util.ArrayList[String] + Minecraft.getInstance.font.getSplitter.splitLines(line, width, Style.EMPTY, true, new ISliceAcceptor { + override def accept(style: Style, start: Int, end: Int) = list.add(line.substring(start, end)) + }) + list } - class CallbackDocRecipe(val stack: ItemStack, val page: String) extends BlankRecipeWrapper { - - override def getIngredients(ingredients: IIngredients): Unit = ingredients.setInputs(classOf[ItemStack], List(stack)) - - override def drawInfo(@Nonnull minecraft: Minecraft, recipeWidth: Int, recipeHeight: Int, mouseX: Int, mouseY: Int): Unit = { - for ((text, line) <- page.lines.zipWithIndex) { - minecraft.fontRenderer.drawString(text, 4, 4 + line * (minecraft.fontRenderer.FONT_HEIGHT + 1), 0x333333, false) - } - } - } + class CallbackDocRecipe(val stack: ItemStack, val page: String) object CallbackDocRecipeCategory extends IRecipeCategory[CallbackDocRecipe] { val recipeWidth: Int = 160 @@ -102,18 +100,30 @@ object CallbackDocHandler { guiHelper.createTickTimer(20, 1, true), 0, 16) } + override def getRecipeClass = classOf[CallbackDocRecipe] + override def getIcon: IDrawable = icon override def getBackground: IDrawable = background + override def setIngredients(recipeWrapper: CallbackDocRecipe, ingredients: IIngredients) { + ingredients.setInput(VanillaTypes.ITEM, recipeWrapper.stack) + } + override def setRecipe(recipeLayout: IRecipeLayout, recipeWrapper: CallbackDocRecipe, ingredients: IIngredients) { } - override def getTitle = "OpenComputers API" + override def draw(recipeWrapper: CallbackDocRecipe, stack: MatrixStack, mouseX: Double, mouseY: Double): Unit = { + val minecraft = Minecraft.getInstance + for ((text, line) <- recipeWrapper.page.lines.zipWithIndex) { + minecraft.font.drawShadow(stack, text, 4, 4 + line * (minecraft.font.lineHeight + 1), 0x333333, false) + } + } - override def getUid = "oc.api" + @Deprecated + override def getTitle = "OpenComputers API" - override def getModName: String = OpenComputers.Name + override def getUid = new ResourceLocation(OpenComputers.ID, "api") } } diff --git a/src/main/scala/li/cil/oc/integration/jei/DrawableAnimatedIcon.scala b/src/main/scala/li/cil/oc/integration/jei/DrawableAnimatedIcon.scala index 3d5c170d10..4efb5ce3f3 100644 --- a/src/main/scala/li/cil/oc/integration/jei/DrawableAnimatedIcon.scala +++ b/src/main/scala/li/cil/oc/integration/jei/DrawableAnimatedIcon.scala @@ -1,12 +1,13 @@ package li.cil.oc.integration.jei -import mezz.jei.api.gui.IDrawableAnimated +import com.mojang.blaze3d.matrix.MatrixStack import mezz.jei.api.gui.ITickTimer +import mezz.jei.api.gui.drawable.IDrawableAnimated import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.AbstractGui import net.minecraft.util.ResourceLocation -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn /** * Used to simulate an animated texture. @@ -21,21 +22,18 @@ class DrawableAnimatedIcon(resourceLocation: ResourceLocation, u: Int, v: Int, w override def getHeight: Int = height + paddingTop + paddingBottom - @SideOnly(Side.CLIENT) - override def draw(minecraft: Minecraft): Unit = draw(minecraft, 0, 0) - - @SideOnly(Side.CLIENT) - override def draw(minecraft: Minecraft, xOffset: Int, yOffset: Int) { + @OnlyIn(Dist.CLIENT) + override def draw(stack: MatrixStack, xOffset: Int, yOffset: Int) { val animationValue = tickTimer.getValue val uOffsetTotal = uOffset * animationValue val vOffsetTotal = vOffset * animationValue - minecraft.getTextureManager.bindTexture(resourceLocation) + Minecraft.getInstance.getTextureManager.bind(resourceLocation) val x = xOffset + this.paddingLeft val y = yOffset + this.paddingTop val u = this.u + uOffsetTotal val v = this.v + vOffsetTotal - Gui.drawModalRectWithCustomSizedTexture(x, y, u, v, width, height, textureWidth, textureHeight) + AbstractGui.blit(stack, x, y, u, v, width, height, textureWidth, textureHeight) } } diff --git a/src/main/scala/li/cil/oc/integration/jei/LootDiskCyclingRecipeHandler.scala b/src/main/scala/li/cil/oc/integration/jei/LootDiskCyclingRecipeHandler.scala deleted file mode 100644 index a35885eee0..0000000000 --- a/src/main/scala/li/cil/oc/integration/jei/LootDiskCyclingRecipeHandler.scala +++ /dev/null @@ -1,33 +0,0 @@ -package li.cil.oc.integration.jei - -import java.util - -import li.cil.oc.Constants -import li.cil.oc.api -import li.cil.oc.common.Loot -import li.cil.oc.common.recipe.LootDiskCyclingRecipe -import mezz.jei.api.ingredients.IIngredients -import mezz.jei.api.recipe._ -import net.minecraft.item.ItemStack - -import scala.collection.convert.WrapAsJava._ - -object LootDiskCyclingRecipeHandler extends IRecipeWrapperFactory[LootDiskCyclingRecipe] { - override def getRecipeWrapper(recipe: LootDiskCyclingRecipe): IRecipeWrapper = new LootDiskCyclingRecipeWrapper(recipe) - - class LootDiskCyclingRecipeWrapper(val recipe: LootDiskCyclingRecipe) extends BlankRecipeWrapper { - - def getInputs: util.List[util.List[ItemStack]] = List(seqAsJavaList(Loot.disksForCycling), seqAsJavaList(List(api.Items.get(Constants.ItemName.Wrench).createItemStack(1)))) - - def getOutputs: util.List[ItemStack] = Loot.disksForCycling.toList - - override def getIngredients(ingredients: IIngredients): Unit = { - ingredients.setInputLists(classOf[ItemStack], getInputs) - ingredients.setOutputs(classOf[ItemStack], getOutputs) - } - } - -} - - - diff --git a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala index 9e419587df..49683119cf 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala @@ -2,84 +2,84 @@ package li.cil.oc.integration.jei import java.util -import javax.annotation.Nonnull +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api -import mezz.jei.api.IGuiHelper -import mezz.jei.api.IModRegistry -import mezz.jei.api.gui.IDrawable +import mezz.jei.api.constants.VanillaTypes import mezz.jei.api.gui.IRecipeLayout +import mezz.jei.api.gui.drawable.IDrawable +import mezz.jei.api.helpers.IGuiHelper import mezz.jei.api.ingredients.IIngredients -import mezz.jei.api.recipe._ +import mezz.jei.api.recipe.category.IRecipeCategory +import mezz.jei.api.registration.IRecipeRegistration import net.minecraft.client.Minecraft import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation -import net.minecraftforge.fml.client.config.GuiButtonExt +import net.minecraft.client.gui.widget.button.Button +import org.lwjgl.glfw.GLFW import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ object ManualUsageHandler { - def getRecipes(registry: IModRegistry): util.List[ManualUsageRecipe] = registry.getIngredientRegistry.getIngredients(classOf[ItemStack]).collect { + def getRecipes(registration: IRecipeRegistration): util.List[ManualUsageRecipe] = registration.getIngredientManager.getAllIngredients(VanillaTypes.ITEM).collect { case stack: ItemStack => api.Manual.pathFor(stack) match { case s: String => Option(new ManualUsageRecipe(stack, s)) case _ => None } }.flatten.toList - object ManualUsageRecipeHandler extends IRecipeWrapperFactory[ManualUsageRecipe] { - override def getRecipeWrapper(recipe: ManualUsageRecipe): ManualUsageRecipe = recipe - } - - class ManualUsageRecipe(val stack: ItemStack, val path: String) extends BlankRecipeWrapper { - lazy val button = new GuiButtonExt(0, (160 - 100) / 2, 10, 100, 20, Localization.localizeImmediately("nei.usage.oc.Manual")) - - override def getIngredients(ingredients: IIngredients): Unit = ingredients.setInputs(classOf[ItemStack], List(stack)) - - override def drawInfo(@Nonnull minecraft: Minecraft, recipeWidth: Int, recipeHeight: Int, mouseX: Int, mouseY: Int): Unit = { - button.displayString = Localization.localizeImmediately("nei.usage.oc.Manual") - button.x = (recipeWidth - button.width) / 2 - button.y = button.height / 2 - button.drawButton(minecraft, mouseX, mouseY, 1) - } - - override def handleClick(@Nonnull minecraft: Minecraft, mouseX: Int, mouseY: Int, mouseButton: Int): Boolean = { - if (button.mousePressed(minecraft, mouseX, mouseY)) { - minecraft.player.closeScreen() - api.Manual.openFor(minecraft.player) - api.Manual.navigate(path) - true - } - else false - } - } + class ManualUsageRecipe(val stack: ItemStack, val path: String) object ManualUsageRecipeCategory extends IRecipeCategory[ManualUsageRecipe] { val recipeWidth: Int = 160 val recipeHeight: Int = 125 private var background: IDrawable = _ private var icon: IDrawable = _ + private val button = new Button((160 - 100) / 2, 10, 100, 20, Localization.localizeLater("nei.usage.oc.Manual"), new Button.IPressable { + override def onPress(b: Button) = Unit + }) def initialize(guiHelper: IGuiHelper) { background = guiHelper.createBlankDrawable(recipeWidth, recipeHeight) - icon = guiHelper.createDrawable(new ResourceLocation(Settings.resourceDomain, "textures/items/manual.png"), 0, 0, 16, 16, 16, 16) + icon = guiHelper.drawableBuilder(new ResourceLocation(Settings.resourceDomain, "textures/items/manual.png"), 0, 0, 16, 16).setTextureSize(16, 16).build() } + override def getRecipeClass = classOf[ManualUsageRecipe] + override def getBackground: IDrawable = background override def getIcon: IDrawable = icon + override def setIngredients(recipeWrapper: ManualUsageRecipe, ingredients: IIngredients) { + ingredients.setInput(VanillaTypes.ITEM, recipeWrapper.stack) + } + override def setRecipe(recipeLayout: IRecipeLayout, recipeWrapper: ManualUsageRecipe, ingredients: IIngredients) { } - override def getTitle = "OpenComputers Manual" + override def draw(recipeWrapper: ManualUsageRecipe, stack: MatrixStack, mouseX: Double, mouseY: Double) { + button.renderButton(stack, mouseX.toInt, mouseY.toInt, 1) + } + + override def handleClick(recipeWrapper: ManualUsageRecipe, mouseX: Double, mouseY: Double, mouseButton: Int): Boolean = { + if (mouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT || button.isMouseOver(mouseX, mouseY)) { + val minecraft = Minecraft.getInstance + minecraft.player.closeContainer() + api.Manual.openFor(minecraft.player) + api.Manual.navigate(recipeWrapper.path) + true + } + else false + } - override def getUid = "oc.manual" + @Deprecated + override def getTitle = "OpenComputers Manual" - override def getModName: String = OpenComputers.Name + override def getUid = new ResourceLocation(OpenComputers.ID, "manual") } } diff --git a/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala b/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala index 36955dff9e..0c86caa22b 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala @@ -1,8 +1,9 @@ package li.cil.oc.integration.jei import li.cil.oc.common.EventHandler -import mezz.jei.api.IJeiRuntime -import mezz.jei.api.ingredients.IIngredientRegistry +import mezz.jei.api.constants.VanillaTypes +import mezz.jei.api.runtime.IIngredientManager +import mezz.jei.api.runtime.IJeiRuntime import net.minecraft.item.ItemStack import scala.collection.convert.WrapAsJava.seqAsJavaList @@ -13,18 +14,18 @@ import scala.collection.convert.WrapAsScala._ object ModJEI { var runtime: Option[IJeiRuntime] = None - var ingredientRegistry: Option[IIngredientRegistry] = None + var ingredientRegistry: Option[IIngredientManager] = None private val disksForRuntime: ArrayBuffer[ItemStack] = mutable.ArrayBuffer.empty private var scheduled: Boolean = false def addDiskAtRuntime(stack: ItemStack): Unit = ingredientRegistry.foreach { registry => - if (!registry.getIngredients(classOf[ItemStack]).exists(ItemStack.areItemStacksEqual(_, stack))) { + if (!registry.getAllIngredients(VanillaTypes.ITEM).exists(ItemStack.matches(_, stack))) { disksForRuntime += stack if (!scheduled) { EventHandler.scheduleClient { () => - ingredientRegistry.foreach(_.addIngredientsAtRuntime(classOf[ItemStack], seqAsJavaList(disksForRuntime))) + ingredientRegistry.foreach(_.addIngredientsAtRuntime(VanillaTypes.ITEM, seqAsJavaList(disksForRuntime))) disksForRuntime.clear() scheduled = false } diff --git a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala index 8e968b42a8..dae2ed75de 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala @@ -1,75 +1,78 @@ package li.cil.oc.integration.jei import li.cil.oc.Constants +import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api.Items -import li.cil.oc.common.recipe.LootDiskCyclingRecipe +import li.cil.oc.client.gui.Relay import li.cil.oc.integration.jei.CallbackDocHandler.CallbackDocRecipe import li.cil.oc.integration.jei.ManualUsageHandler.ManualUsageRecipe import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.integration.util.ItemSearch import li.cil.oc.util.StackOption -import mezz.jei.api.IJeiRuntime import mezz.jei.api.IModPlugin -import mezz.jei.api.IModRegistry -import mezz.jei.api.ISubtypeRegistry -import mezz.jei.api.ISubtypeRegistry.ISubtypeInterpreter -import mezz.jei.api.JEIPlugin -import mezz.jei.api.ingredients.IModIngredientRegistration -import mezz.jei.api.recipe.{IRecipeCategoryRegistration, VanillaRecipeCategoryUid} -import net.minecraft.client.gui.inventory.GuiContainer +import mezz.jei.api.JeiPlugin +import mezz.jei.api.constants.VanillaTypes +import mezz.jei.api.ingredients.subtypes.IIngredientSubtypeInterpreter +import mezz.jei.api.ingredients.subtypes.UidContext +import mezz.jei.api.registration.IAdvancedRegistration +import mezz.jei.api.registration.IGuiHandlerRegistration +import mezz.jei.api.registration.IRecipeCategoryRegistration +import mezz.jei.api.registration.IRecipeRegistration +import mezz.jei.api.registration.ISubtypeRegistration +import mezz.jei.api.runtime.IIngredientManager +import mezz.jei.api.runtime.IJeiRuntime +import net.minecraft.client.gui.screen.inventory.ContainerScreen import net.minecraft.item.Item import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ResourceLocation -@JEIPlugin +import scala.collection.JavaConverters._ + +@JeiPlugin class ModPluginOpenComputers extends IModPlugin { + override def getPluginUid = new ResourceLocation(OpenComputers.ID, "jei_plugin") + override def registerCategories(registry: IRecipeCategoryRegistration): Unit = { registry.addRecipeCategories(ManualUsageHandler.ManualUsageRecipeCategory) registry.addRecipeCategories(CallbackDocHandler.CallbackDocRecipeCategory) } - override def register(registry: IModRegistry) { - if (Settings.get.lootRecrafting) { - registry.handleRecipes(classOf[LootDiskCyclingRecipe], LootDiskCyclingRecipeHandler, VanillaRecipeCategoryUid.CRAFTING) - } + override def registerRecipes(registration: IRecipeRegistration) { + registration.addRecipes(ManualUsageHandler.getRecipes(registration), ManualUsageHandler.ManualUsageRecipeCategory.getUid) + registration.addRecipes(CallbackDocHandler.getRecipes(registration), CallbackDocHandler.CallbackDocRecipeCategory.getUid) + } - ItemBlacklist.hiddenItems.foreach(getter => registry.getJeiHelpers.getIngredientBlacklist.addIngredientToBlacklist(getter())) + override def registerGuiHandlers(registration: IGuiHandlerRegistration) = { + registration.addGuiContainerHandler(classOf[Relay], RelayGuiHandler) + } + override def registerAdvanced(registration: IAdvancedRegistration) = { // This could go into the Description category, but Manual should always be in front of the Callback doc. - ManualUsageHandler.ManualUsageRecipeCategory.initialize(registry.getJeiHelpers.getGuiHelper) - CallbackDocHandler.CallbackDocRecipeCategory.initialize(registry.getJeiHelpers.getGuiHelper) - - registry.handleRecipes(classOf[ManualUsageRecipe], ManualUsageHandler.ManualUsageRecipeHandler, ManualUsageHandler.ManualUsageRecipeCategory.getUid) - registry.handleRecipes(classOf[CallbackDocRecipe], CallbackDocHandler.CallbackDocRecipeHandler, CallbackDocHandler.CallbackDocRecipeCategory.getUid) - - registry.addRecipes(ManualUsageHandler.getRecipes(registry), ManualUsageHandler.ManualUsageRecipeCategory.getUid) - registry.addRecipes(CallbackDocHandler.getRecipes(registry), CallbackDocHandler.CallbackDocRecipeCategory.getUid) - - registry.addAdvancedGuiHandlers(RelayGuiHandler) - - ModJEI.ingredientRegistry = Option(registry.getIngredientRegistry) + ManualUsageHandler.ManualUsageRecipeCategory.initialize(registration.getJeiHelpers.getGuiHelper) + CallbackDocHandler.CallbackDocRecipeCategory.initialize(registration.getJeiHelpers.getGuiHelper) } - private var stackUnderMouse: (GuiContainer, Int, Int) => StackOption = _ + private var stackUnderMouse: (ContainerScreen[_], Int, Int) => StackOption = _ override def onRuntimeAvailable(jeiRuntime: IJeiRuntime) { if (stackUnderMouse == null) { ItemSearch.stackFocusing += ((container, mouseX, mouseY) => stackUnderMouse(container, mouseX, mouseY)) } - stackUnderMouse = (container, mouseX, mouseY) => StackOption(jeiRuntime.getItemListOverlay.getStackUnderMouse) + stackUnderMouse = (container, mouseX, mouseY) => StackOption(jeiRuntime.getIngredientListOverlay.getIngredientUnderMouse(VanillaTypes.ITEM)) - ModJEI.runtime = Option(jeiRuntime) - } + jeiRuntime.getIngredientManager.removeIngredientsAtRuntime(VanillaTypes.ITEM, ItemBlacklist.hiddenItems.map(getter => getter()).asJavaCollection) - override def registerIngredients(registry: IModIngredientRegistration) { + ModJEI.runtime = Option(jeiRuntime) + ModJEI.ingredientRegistry = Option(jeiRuntime.getIngredientManager) } - override def registerItemSubtypes(subtypeRegistry: ISubtypeRegistry) { + override def registerItemSubtypes(subtypeRegistry: ISubtypeRegistration) { def useNBT(names: String*) = names.map(name => { val info = Items.get(name) - Option(info.item).getOrElse(Item.getItemFromBlock(info.block)) + Option(info.item).getOrElse(info.block.asItem()) }).filter(_ != null).distinct.foreach(subtypeRegistry.useNbtForSubtypes(_)) // Only the preconfigured blocks and items have to be here. @@ -81,16 +84,16 @@ class ModPluginOpenComputers extends IModPlugin { Constants.ItemName.Tablet ) - subtypeRegistry.registerSubtypeInterpreter(Items.get(Constants.ItemName.Floppy).item(), new ISubtypeInterpreter { - override def apply(stack: ItemStack): String = { - if (!stack.hasTagCompound) return null - val compound: NBTTagCompound = stack.getTagCompound - val data = new NBTTagCompound + subtypeRegistry.registerSubtypeInterpreter(Items.get(Constants.ItemName.Floppy).item(), new IIngredientSubtypeInterpreter[ItemStack] { + override def apply(stack: ItemStack, ctx: UidContext): String = { + if (!stack.hasTag) return IIngredientSubtypeInterpreter.NONE + val compound: CompoundNBT = stack.getTag + val data = new CompoundNBT // Separate loot disks from normal floppies - if (compound.hasKey(Settings.namespace + "lootFactory")) { - data.setTag(Settings.namespace + "lootFactory", compound.getTag(Settings.namespace + "lootFactory")) + if (compound.contains(Settings.namespace + "lootFactory")) { + data.put(Settings.namespace + "lootFactory", compound.get(Settings.namespace + "lootFactory")) } - if (data.hasNoTags) null else data.toString + if (data.isEmpty) IIngredientSubtypeInterpreter.NONE else data.toString } }) } diff --git a/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala b/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala index 860c4ee6db..6abd7d21f1 100644 --- a/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala @@ -1,20 +1,18 @@ package li.cil.oc.integration.jei -import java.awt.Rectangle import java.util import li.cil.oc.client.gui.Relay -import mezz.jei.api.gui.IAdvancedGuiHandler +import mezz.jei.api.gui.handlers.IGuiContainerHandler +import net.minecraft.client.renderer.Rectangle2d import scala.collection.convert.WrapAsJava._ -object RelayGuiHandler extends IAdvancedGuiHandler[Relay] { +object RelayGuiHandler extends IGuiContainerHandler[Relay] { - override def getGuiContainerClass: Class[Relay] = classOf[Relay] - - override def getGuiExtraAreas(gui: Relay): util.List[Rectangle] = List( - new Rectangle(gui.windowX + gui.tabPosition.getX, gui.windowY + gui.tabPosition.getY, gui.tabPosition.getWidth, gui.tabPosition.getHeight) + override def getGuiExtraAreas(gui: Relay): util.List[Rectangle2d] = List( + new Rectangle2d(gui.windowX + gui.tabPosition.getX, gui.windowY + gui.tabPosition.getY, gui.tabPosition.getWidth, gui.tabPosition.getHeight) ) - override def getIngredientUnderMouse(guiContainer: Relay, mouseX: Int, mouseY: Int) = null + override def getIngredientUnderMouse(guiContainer: Relay, mouseX: Double, mouseY: Double) = null } diff --git a/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala index a2a78094ec..57e94bd486 100644 --- a/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala +++ b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala @@ -4,21 +4,25 @@ import java.util import li.cil.oc.Settings import li.cil.oc.api +import mekanism.api.MekanismAPI +import mekanism.api.chemical.gas.Gas +import mekanism.api.chemical.gas.GasStack +import net.minecraftforge.registries.ForgeRegistry import scala.collection.convert.WrapAsScala._ object ConverterGasStack extends api.driver.Converter { override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = value match { - case stack: mekanism.api.gas.GasStack => + case stack: GasStack => if (Settings.get.insertIdsInConverters) { - output += "id" -> Int.box(stack.getGas.getID) + output += "id" -> Int.box(MekanismAPI.gasRegistry().asInstanceOf[ForgeRegistry[Gas]].getID(stack.getType)) } - output += "amount" -> Int.box(stack.amount) - val gas = stack.getGas + output += "amount" -> Long.box(stack.getAmount) + val gas = stack.getType if (gas != null) { - output += "name" -> gas.getName - output += "label" -> gas.getLocalizedName + output += "name" -> gas.getRegistryName.toString + output += "label" -> gas.getTextComponent.getString } case _ => } diff --git a/src/main/scala/li/cil/oc/integration/mekanism/EventHandlerMekanism.scala b/src/main/scala/li/cil/oc/integration/mekanism/EventHandlerMekanism.scala deleted file mode 100644 index b15ec865fd..0000000000 --- a/src/main/scala/li/cil/oc/integration/mekanism/EventHandlerMekanism.scala +++ /dev/null @@ -1,18 +0,0 @@ -package li.cil.oc.integration.mekanism - -import mekanism.api.IMekWrench -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.ItemStack -import net.minecraft.util.EnumHand -import net.minecraft.util.math.BlockPos - -object EventHandlerMekanism { - def useWrench(player: EntityPlayer, pos: BlockPos, changeDurability: Boolean): Boolean = { - player.getHeldItem(EnumHand.MAIN_HAND).getItem match { - case wrench: IMekWrench => wrench.canUseWrench(player.getHeldItem(EnumHand.MAIN_HAND), player, pos) - case _ => false - } - } - - def isWrench(stack: ItemStack): Boolean = stack.getItem.isInstanceOf[IMekWrench] -} diff --git a/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala b/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala index 35b9ddc816..1da9310e9e 100644 --- a/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala +++ b/src/main/scala/li/cil/oc/integration/mekanism/ModMekanism.scala @@ -8,8 +8,6 @@ object ModMekanism extends ModProxy { override def getMod = Mods.Mekanism override def initialize(): Unit = { - api.IMC.registerWrenchTool("li.cil.oc.integration.mekanism.EventHandlerMekanism.useWrench") - api.IMC.registerWrenchToolCheck("li.cil.oc.integration.mekanism.EventHandlerMekanism.isWrench") api.Driver.add(ConverterGasStack) } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala index f46fa0cb1e..c200b730ed 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala @@ -10,13 +10,11 @@ object ConverterFluidStack extends api.driver.Converter { override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = value match { case stack: net.minecraftforge.fluids.FluidStack => - output += "amount" -> Int.box(stack.amount) - output += "hasTag" -> Boolean.box(stack.tag != null) + output += "amount" -> Int.box(stack.getAmount) + output += "hasTag" -> Boolean.box(stack.hasTag) val fluid = stack.getFluid - if (fluid != null) { - output += "name" -> fluid.getName - output += "label" -> fluid.getLocalizedName(stack) - } + output += "name" -> fluid.getRegistryName.toString + output += "label" -> fluid.getAttributes.getDisplayName(stack) case _ => } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala index 7e36d439a6..cb677efb17 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala @@ -10,10 +10,10 @@ import scala.collection.convert.WrapAsScala._ object ConverterFluidTankInfo extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { - case tankInfo: fluids.FluidTankInfo => - output += "capacity" -> Int.box(tankInfo.capacity) - if (tankInfo.fluid != null) { - ConverterFluidStack.convert(tankInfo.fluid, output) + case tankInfo: fluids.IFluidTank => + output += "capacity" -> Int.box(tankInfo.getCapacity) + if (!tankInfo.getFluid.isEmpty) { + ConverterFluidStack.convert(tankInfo.getFluid, output) } else output += "amount" -> Int.box(0) case _ => diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala index 80c99189df..a3b2ad3fd7 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala @@ -3,16 +3,16 @@ package li.cil.oc.integration.minecraft import java.util import li.cil.oc.api -import net.minecraftforge.fluids +import li.cil.oc.util.ExtendedArguments.TankProperties import scala.collection.convert.WrapAsScala._ object ConverterFluidTankProperties extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { - case properties: fluids.capability.IFluidTankProperties => - output += "capacity" -> Int.box(properties.getCapacity) - val fluid = properties.getContents + case properties: TankProperties => + output += "capacity" -> Int.box(properties.capacity) + val fluid = properties.contents if (fluid != null) { ConverterFluidStack.convert(fluid, output) } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala index e7a1efeba3..7ed795a572 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala @@ -5,31 +5,30 @@ import java.util import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.integration.Mods -import net.minecraftforge.fml.common.Loader import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.enchantment.EnchantmentHelper import li.cil.oc.util.ItemUtils +import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.item import net.minecraft.item.Item -import net.minecraft.nbt.{NBTTagCompound, NBTTagList, NBTTagString} +import net.minecraft.nbt.{CompoundNBT, ListNBT, StringNBT} +import net.minecraft.tags.ItemTags import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.oredict.OreDictionary import scala.collection.convert.WrapAsScala._ import scala.collection.mutable object ConverterItemStack extends api.driver.Converter { - def getTagValue(tag: NBTTagCompound, key: String): AnyRef = tag.getTagId(key) match { - case NBT.TAG_INT => Int.box(tag.getInteger(key)) + def getTagValue(tag: CompoundNBT, key: String): AnyRef = tag.getTagType(key) match { + case NBT.TAG_INT => Int.box(tag.getInt(key)) case NBT.TAG_STRING => tag.getString(key) case NBT.TAG_BYTE => Byte.box(tag.getByte(key)) - case NBT.TAG_COMPOUND => tag.getCompoundTag(key) - case NBT.TAG_LIST => tag.getTagList(key, NBT.TAG_STRING) + case NBT.TAG_COMPOUND => tag.getCompound(key) + case NBT.TAG_LIST => tag.getList(key, NBT.TAG_STRING) case _ => null } - def withTag(tag: NBTTagCompound, key: String, tagId: Int, f: AnyRef => AnyRef): AnyRef = { - if (tag.hasKey(key, tagId)) { + def withTag(tag: CompoundNBT, key: String, tagId: Int, f: AnyRef => AnyRef): AnyRef = { + if (tag.contains(key, tagId)) { Option(getTagValue(tag, key)) match { case Some(value) => f(value) case _ => null @@ -37,60 +36,43 @@ object ConverterItemStack extends api.driver.Converter { } else null } - def withCompound(tag: NBTTagCompound, key: String, f: NBTTagCompound => AnyRef): AnyRef = { - withTag(tag, key, NBT.TAG_COMPOUND, { case value: NBTTagCompound => f(value)}) + def withCompound(tag: CompoundNBT, key: String, f: CompoundNBT => AnyRef): AnyRef = { + withTag(tag, key, NBT.TAG_COMPOUND, { case value: CompoundNBT => f(value)}) } - def withList(tag: NBTTagCompound, key: String, f: NBTTagList => AnyRef): AnyRef = { - withTag(tag, key, NBT.TAG_STRING, { case value: NBTTagList => f(value)}) + def withList(tag: CompoundNBT, key: String, f: ListNBT => AnyRef): AnyRef = { + withTag(tag, key, NBT.TAG_STRING, { case value: ListNBT => f(value)}) } override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { case stack: item.ItemStack => if (Settings.get.insertIdsInConverters) { - output += "id" -> Int.box(Item.getIdFromItem(stack.getItem)) - output += "oreNames" -> OreDictionary.getOreIDs(stack).map(OreDictionary.getOreName) + output += "id" -> Int.box(Item.getId(stack.getItem)) + output += "oreNames" -> stack.getItem.getTags.map(_.toString).toArray } - output += "damage" -> Int.box(stack.getItemDamage) + output += "damage" -> Int.box(stack.getDamageValue) output += "maxDamage" -> Int.box(stack.getMaxDamage) output += "size" -> Int.box(stack.getCount) output += "maxSize" -> Int.box(stack.getMaxStackSize) - output += "hasTag" -> Boolean.box(stack.hasTagCompound) - output += "name" -> Item.REGISTRY.getNameForObject(stack.getItem) + output += "hasTag" -> Boolean.box(stack.hasTag) + output += "name" -> stack.getItem.getRegistryName output += "label" -> stack.getDisplayName // custom mod tags - if (stack.hasTagCompound) { - val tags = stack.getTagCompound + if (stack.hasTag) { + val tags = stack.getTag //Lore tags withCompound(tags, "display", withList(_, "Lore", { - output += "lore" -> _.map((tag: NBTTagString) => tag.getString).mkString("\n") + output += "lore" -> _.map((tag: StringNBT) => tag.getAsString).mkString("\n") }) ) - // IC2 reactor items custom damage - withTag(tags, "advDmg", NBT.TAG_INT, dmg => output += "customDamage" -> dmg) - - // draconic upgrades - if (Mods.DraconicEvolution.isModAvailable) { - withCompound(tags, "DEUpgrades", de => { - output += "DEUpgrades" -> de - }) - (0 until 15).foreach(n => { - val profileName: String = s"Profile_$n" - Option(getTagValue(tags, profileName)) match { - case Some(profile: NBTTagCompound) => output += profileName -> profile - case _ => - } - }) - } - withTag(tags, "Energy", NBT.TAG_INT, value => output += "Energy" -> value) if (Settings.get.allowItemStackNBTTags) { - output += "tag" -> ItemUtils.saveTag(stack.getTagCompound) + output += "tag" -> ItemUtils.saveTag(stack.getTag) } } @@ -98,8 +80,8 @@ object ConverterItemStack extends api.driver.Converter { EnchantmentHelper.getEnchantments(stack).collect { case (enchantment, level) => val map = mutable.Map[String, Any]( - "name" -> enchantment.getName, - "label" -> enchantment.getTranslatedName(level), + "name" -> enchantment.getRegistryName, + "label" -> enchantment.getFullname(level), "level" -> level ) enchantments += map diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala index d664f4932c..d7cf411718 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala @@ -10,26 +10,26 @@ import scala.collection.convert.WrapAsScala._ object ConverterNBT extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { - case nbt: NBTTagCompound => output += "oc:flatten" -> convert(nbt) + case nbt: CompoundNBT => output += "oc:flatten" -> convert(nbt) case _ => } - private def convert(nbt: NBTBase): AnyRef = nbt match { - case tag: NBTTagByte => Byte.box(tag.getByte) - case tag: NBTTagShort => Short.box(tag.getShort) - case tag: NBTTagInt => Int.box(tag.getInt) - case tag: NBTTagLong => Long.box(tag.getLong) - case tag: NBTTagFloat => Float.box(tag.getFloat) - case tag: NBTTagDouble => Double.box(tag.getDouble) - case tag: NBTTagByteArray => tag.getByteArray - case tag: NBTTagString => tag.getString - case tag: NBTTagList => - val copy = tag.copy(): NBTTagList - (0 until copy.tagCount).map(_ => convert(copy.removeTag(0))).toArray - case tag: NBTTagCompound => - tag.getKeySet.collect { - case key: String => key -> convert(tag.getTag(key)) + private def convert(nbt: INBT): AnyRef = nbt match { + case tag: ByteNBT => Byte.box(tag.getAsByte) + case tag: ShortNBT => Short.box(tag.getAsShort) + case tag: IntNBT => Int.box(tag.getAsInt) + case tag: LongNBT => Long.box(tag.getAsLong) + case tag: FloatNBT => Float.box(tag.getAsFloat) + case tag: DoubleNBT => Double.box(tag.getAsDouble) + case tag: ByteArrayNBT => tag.getAsByteArray + case tag: StringNBT => tag.getAsString + case tag: ListNBT => + val copy = tag.copy(): ListNBT + (0 until copy.size).map(_ => convert(copy.remove(0))).toArray + case tag: CompoundNBT => + tag.getAllKeys.collect { + case key: String => key -> convert(tag.get(key)) }.toMap - case tag: NBTTagIntArray => tag.getIntArray + case tag: IntArrayNBT => tag.getAsIntArray } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala index c8d6d889ec..0305e560de 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala @@ -1,17 +1,31 @@ package li.cil.oc.integration.minecraft +import java.nio.charset.StandardCharsets import java.util +import java.util.UUID +import com.google.common.hash.Hashing import li.cil.oc.api -import net.minecraft.world +import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld import scala.collection.convert.WrapAsScala._ object ConverterWorld extends api.driver.Converter { - override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = + override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = { value match { - case world: world.World => - output += "oc:flatten" -> world.provider + case world: ServerWorld => + output += "id" -> UUID.nameUUIDFromBytes(Hashing.md5().newHasher(). + putLong(world.getSeed). + putString(world.dimension.location.toString, StandardCharsets.UTF_8). + hash().asBytes()).toString case _ => } + + value match { + case world: World => + output += "name" -> world.dimension.location.toString + case _ => + } + } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorldProvider.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorldProvider.scala deleted file mode 100644 index 93ee7c9b64..0000000000 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorldProvider.scala +++ /dev/null @@ -1,23 +0,0 @@ -package li.cil.oc.integration.minecraft - -import java.util -import java.util.UUID - -import com.google.common.hash.Hashing -import li.cil.oc.api -import net.minecraft.world - -import scala.collection.convert.WrapAsScala._ - -object ConverterWorldProvider extends api.driver.Converter { - override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = - value match { - case provider: world.WorldProvider => - output += "id" -> UUID.nameUUIDFromBytes(Hashing.md5().newHasher(). - putLong(provider.getSeed). - putInt(provider.getDimension). - hash().asBytes()).toString - output += "name" -> provider.getDimensionType.getName - case _ => - } -} diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverBeacon.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverBeacon.scala index 5f8ae6edc5..5d3a1bd735 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverBeacon.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverBeacon.scala @@ -10,51 +10,48 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks import net.minecraft.item.ItemStack -import net.minecraft.potion.Potion -import net.minecraft.tileentity.TileEntityBeacon -import net.minecraft.util.EnumFacing +import net.minecraft.potion.Effect +import net.minecraft.tileentity.BeaconTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverBeacon extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityBeacon] + override def getTileEntityClass: Class[_] = classOf[BeaconTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityBeacon]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[BeaconTileEntity]) - final class Environment(tileEntity: TileEntityBeacon) extends ManagedTileEntityEnvironment[TileEntityBeacon](tileEntity, "beacon") with NamedBlock { + final class Environment(tileEntity: BeaconTileEntity) extends ManagedTileEntityEnvironment[BeaconTileEntity](tileEntity, "beacon") with NamedBlock { override def preferredName = "beacon" override def priority = 0 @Callback(doc = "function():number -- Get the number of levels for this beacon.") def getLevels(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(0)) + result(tileEntity.getLevels) } @Callback(doc = "function():string -- Get the name of the active primary effect.") def getPrimaryEffect(context: Context, args: Arguments): Array[AnyRef] = { - result(getEffectName(tileEntity.getField(1))) + result(getEffectName(tileEntity.primaryPower)) } @Callback(doc = "function():string -- Get the name of the active secondary effect.") def getSecondaryEffect(context: Context, args: Arguments): Array[AnyRef] = { - result(getEffectName(tileEntity.getField(2))) + result(getEffectName(tileEntity.secondaryPower)) } - private def getEffectName(id: Int): String = { - val potion = Potion.getPotionById(id) - if (potion != null) - Potion.getPotionById(id).getName - else null + private def getEffectName(effect: Effect): String = { + if (effect != null) effect.getRegistryName.toString else null } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.BEACON) + if (!stack.isEmpty && Block.byItem(stack.getItem) == Blocks.BEACON) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverBrewingStand.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverBrewingStand.scala index f46a7456bf..3d80e521f6 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverBrewingStand.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverBrewingStand.scala @@ -9,27 +9,27 @@ import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result -import net.minecraft.init.Items +import net.minecraft.item.Items import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityBrewingStand -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.BrewingStandTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverBrewingStand extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityBrewingStand] + override def getTileEntityClass: Class[_] = classOf[BrewingStandTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityBrewingStand]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[BrewingStandTileEntity]) - final class Environment(tileEntity: TileEntityBrewingStand) extends ManagedTileEntityEnvironment[TileEntityBrewingStand](tileEntity, "brewing_stand") with NamedBlock { + final class Environment(tileEntity: BrewingStandTileEntity) extends ManagedTileEntityEnvironment[BrewingStandTileEntity](tileEntity, "brewing_stand") with NamedBlock { override def preferredName = "brewing_stand" override def priority = 0 @Callback(doc = "function():number -- Get the number of ticks remaining of the current brewing operation.") def getBrewTime(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(0)) + result(tileEntity.brewTime) } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverCommandBlock.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverCommandBlock.scala index 7c6f55fb92..b68de56a1d 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverCommandBlock.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverCommandBlock.scala @@ -10,53 +10,53 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityCommandBlock -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.CommandBlockTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import net.minecraftforge.fml.common.FMLCommonHandler +import net.minecraftforge.fml.server.ServerLifecycleHooks object DriverCommandBlock extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityCommandBlock] + override def getTileEntityClass: Class[_] = classOf[CommandBlockTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityCommandBlock]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[CommandBlockTileEntity]) - final class Environment(tileEntity: TileEntityCommandBlock) extends ManagedTileEntityEnvironment[TileEntityCommandBlock](tileEntity, "command_block") with NamedBlock { + final class Environment(tileEntity: CommandBlockTileEntity) extends ManagedTileEntityEnvironment[CommandBlockTileEntity](tileEntity, "command_block") with NamedBlock { override def preferredName = "command_block" override def priority = 0 @Callback(direct = true, doc = "function():string -- Get the command currently set in this command block.") def getCommand(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getCommandBlockLogic.getCommand) + result(tileEntity.getCommandBlock.getCommand) } @Callback(doc = "function(value:string) -- Set the specified command for the command block.") def setCommand(context: Context, args: Arguments): Array[AnyRef] = { - tileEntity.getCommandBlockLogic.setCommand(args.checkString(0)) - tileEntity.getWorld.notifyBlockUpdate(tileEntity.getPos, tileEntity.getWorld.getBlockState(tileEntity.getPos), tileEntity.getWorld.getBlockState(tileEntity.getPos), 3) + tileEntity.getCommandBlock.setCommand(args.checkString(0)) + tileEntity.getLevel.sendBlockUpdated(tileEntity.getBlockPos, tileEntity.getLevel.getBlockState(tileEntity.getBlockPos), tileEntity.getLevel.getBlockState(tileEntity.getBlockPos), 3) result(true) } @Callback(doc = "function():number -- Execute the currently set command. This has a slight delay to allow the command block to properly update.") def executeCommand(context: Context, args: Arguments): Array[AnyRef] = { context.pause(0.1) - if (!FMLCommonHandler.instance.getMinecraftServerInstance.isCommandBlockEnabled) { + if (!ServerLifecycleHooks.getCurrentServer.isCommandBlockEnabled) { result(null, "command blocks are disabled") } else { - val commandSender = tileEntity.getCommandBlockLogic - commandSender.trigger(tileEntity.getWorld) - result(commandSender.getSuccessCount, commandSender.getLastOutput.getUnformattedText) + val commandSender = tileEntity.getCommandBlock + commandSender.performCommand(tileEntity.getLevel) + result(commandSender.getSuccessCount, commandSender.getLastOutput.getString) } } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.COMMAND_BLOCK) + if (!stack.isEmpty && Block.byItem(stack.getItem) == Blocks.COMMAND_BLOCK) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverComparator.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverComparator.scala index a7ea2143ed..6e89d9f7fd 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverComparator.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverComparator.scala @@ -9,20 +9,20 @@ import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result -import net.minecraft.init.Items +import net.minecraft.item.Items import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityComparator -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.ComparatorTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverComparator extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityComparator] + override def getTileEntityClass: Class[_] = classOf[ComparatorTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityComparator]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[ComparatorTileEntity]) - final class Environment(tileEntity: TileEntityComparator) extends ManagedTileEntityEnvironment[TileEntityComparator](tileEntity, "comparator") with NamedBlock { + final class Environment(tileEntity: ComparatorTileEntity) extends ManagedTileEntityEnvironment[ComparatorTileEntity](tileEntity, "comparator") with NamedBlock { override def preferredName = "comparator" override def priority = 0 diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidHandler.java b/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidHandler.java index 505ac64a5c..5f2777106d 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidHandler.java +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidHandler.java @@ -6,8 +6,9 @@ import li.cil.oc.api.machine.Context; import li.cil.oc.api.network.ManagedEnvironment; import li.cil.oc.integration.ManagedTileEntityEnvironment; +import li.cil.oc.util.ExtendedArguments.TankProperties; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -15,18 +16,17 @@ public final class DriverFluidHandler implements DriverBlock { @Override - public boolean worksWith(final World world, final BlockPos pos, final EnumFacing side) { - final TileEntity tileEntity = world.getTileEntity(pos); + public boolean worksWith(final World world, final BlockPos pos, final Direction side) { + final TileEntity tileEntity = world.getBlockEntity(pos); if (tileEntity == null) { return false; } - return tileEntity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side) && - tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side) != null; + return tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side).isPresent(); } @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment(world.getTileEntity(pos).getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side)); + public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final Direction side) { + return new Environment(world.getBlockEntity(pos).getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side).orElse(null)); } public static final class Environment extends ManagedTileEntityEnvironment { @@ -36,7 +36,11 @@ public Environment(final IFluidHandler tileEntity) { @Callback(doc = "function():table -- Get some information about the tank accessible from the specified side.") public Object[] getTankInfo(final Context context, final Arguments args) { - return tileEntity.getTankProperties(); + TankProperties[] props = new TankProperties[tileEntity.getTanks()]; + for (int i = 0; i < props.length; i++) { + props[i] = new TankProperties(tileEntity.getTankCapacity(i), tileEntity.getFluidInTank(i)); + } + return props; } } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidTank.java b/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidTank.java index d319492e1d..3fefc5fc70 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidTank.java +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverFluidTank.java @@ -6,8 +6,9 @@ import li.cil.oc.api.network.ManagedEnvironment; import li.cil.oc.api.prefab.DriverSidedTileEntity; import li.cil.oc.integration.ManagedTileEntityEnvironment; +import li.cil.oc.util.ExtendedArguments.TankProperties; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; +import net.minecraft.util.Direction; import net.minecraft.world.World; import net.minecraftforge.fluids.IFluidTank; @@ -18,8 +19,8 @@ public Class getTileEntityClass() { } @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment((IFluidTank) world.getTileEntity(pos)); + public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final Direction side) { + return new Environment((IFluidTank) world.getBlockEntity(pos)); } public static final class Environment extends ManagedTileEntityEnvironment { @@ -29,7 +30,7 @@ public Environment(final IFluidTank tileEntity) { @Callback(doc = "function():table -- Get some information about this tank.") public Object[] getInfo(final Context context, final Arguments args) { - return new Object[]{tileEntity.getInfo()}; + return new Object[]{new TankProperties(tileEntity.getCapacity(), tileEntity.getFluid())}; } } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverFurnace.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverFurnace.scala index 90b7e51e1a..e7f8cba296 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverFurnace.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverFurnace.scala @@ -10,53 +10,53 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityFurnace -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.FurnaceTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverFurnace extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityFurnace] + override def getTileEntityClass: Class[_] = classOf[FurnaceTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityFurnace]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[FurnaceTileEntity]) - final class Environment(tileEntity: TileEntityFurnace) extends ManagedTileEntityEnvironment[TileEntityFurnace](tileEntity, "furnace") with NamedBlock { + final class Environment(tileEntity: FurnaceTileEntity) extends ManagedTileEntityEnvironment[FurnaceTileEntity](tileEntity, "furnace") with NamedBlock { override def preferredName = "furnace" override def priority = 0 @Callback(doc = "function():number -- The number of ticks that the furnace will keep burning from the last consumed fuel.") def getBurnTime(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(0)) + result(tileEntity.litTime) } @Callback(doc = "function():number -- The number of ticks that the currently burning fuel lasts in total.") def getCurrentItemBurnTime(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(1)) + result(tileEntity.litDuration) } @Callback(doc = "function():number -- The number of ticks that the current item has been cooking for.") def getCookTime(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(2)) + result(tileEntity.cookingProgress) } @Callback(doc = "function():number -- The number of ticks that the current item needs to cook.") def getTotalCookTime(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getField(3)) + result(tileEntity.cookingTotalTime) } @Callback(doc = "function():boolean -- Get whether the furnace is currently active.") def isBurning(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.isBurning) + result(tileEntity.litTime > 0) } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.FURNACE) + if (!stack.isEmpty && Block.byItem(stack.getItem) == Blocks.FURNACE) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverInventory.java b/src/main/scala/li/cil/oc/integration/minecraft/DriverInventory.java index 566f2a9c85..677dddeaf9 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverInventory.java +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverInventory.java @@ -8,19 +8,21 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity; import li.cil.oc.integration.ManagedTileEntityEnvironment; import li.cil.oc.util.BlockPosition; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; +import net.minecraft.util.Direction; +import net.minecraft.util.Hand; +import net.minecraft.util.INameable; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.World; -import net.minecraft.world.WorldServer; +import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayerFactory; import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.eventbus.api.Event; public final class DriverInventory extends DriverSidedTileEntity { @Override @@ -29,37 +31,38 @@ public Class getTileEntityClass() { } @Override - public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final EnumFacing side) { - return new Environment(world.getTileEntity(pos), world); + public ManagedEnvironment createEnvironment(final World world, final BlockPos pos, final Direction side) { + return new Environment(world.getBlockEntity(pos), world); } public static final class Environment extends ManagedTileEntityEnvironment { - private final EntityPlayer fakePlayer; + private final PlayerEntity fakePlayer; private final BlockPosition position; public Environment(final TileEntity tileEntity, final World world) { super((IInventory) tileEntity, "inventory"); - fakePlayer = FakePlayerFactory.get((WorldServer) world, Settings.get().fakePlayerProfile()); - position = BlockPosition.apply(tileEntity.getPos(), world); + fakePlayer = FakePlayerFactory.get((ServerWorld) world, Settings.get().fakePlayerProfile()); + position = BlockPosition.apply(tileEntity.getBlockPos(), world); } @Callback(doc = "function():string -- Get the name of this inventory.") public Object[] getInventoryName(final Context context, final Arguments args) { if (notPermitted()) return new Object[]{null, "permission denied"}; - return new Object[]{tileEntity.getName()}; + if (tileEntity instanceof INameable) return new Object[]{((INameable) tileEntity).getName().getString()}; + return new Object[]{null, "inventory is unnamed"}; } @Callback(doc = "function():number -- Get the number of slots in this inventory.") public Object[] getInventorySize(final Context context, final Arguments args) { if (notPermitted()) return new Object[]{null, "permission denied"}; - return new Object[]{tileEntity.getSizeInventory()}; + return new Object[]{tileEntity.getContainerSize()}; } @Callback(doc = "function(slot:number):number -- Get the stack size of the item stack in the specified slot.") public Object[] getSlotStackSize(final Context context, final Arguments args) { if (notPermitted()) return new Object[]{null, "permission denied"}; final int slot = checkSlot(args, 0); - final ItemStack stack = tileEntity.getStackInSlot(slot); + final ItemStack stack = tileEntity.getItem(slot); if (!stack.isEmpty()) { return new Object[]{stack.getCount()}; } else { @@ -71,11 +74,11 @@ public Object[] getSlotStackSize(final Context context, final Arguments args) { public Object[] getSlotMaxStackSize(final Context context, final Arguments args) { if (notPermitted()) return new Object[]{null, "permission denied"}; final int slot = checkSlot(args, 0); - final ItemStack stack = tileEntity.getStackInSlot(slot); + final ItemStack stack = tileEntity.getItem(slot); if (!stack.isEmpty()) { - return new Object[]{Math.min(tileEntity.getInventoryStackLimit(), stack.getMaxStackSize())}; + return new Object[]{Math.min(tileEntity.getMaxStackSize(), stack.getMaxStackSize())}; } else { - return new Object[]{tileEntity.getInventoryStackLimit()}; + return new Object[]{tileEntity.getMaxStackSize()}; } } @@ -87,8 +90,8 @@ public Object[] compareStacks(final Context context, final Arguments args) { if (slotA == slotB) { return new Object[]{true}; } - final ItemStack stackA = tileEntity.getStackInSlot(slotA); - final ItemStack stackB = tileEntity.getStackInSlot(slotB); + final ItemStack stackA = tileEntity.getItem(slotA); + final ItemStack stackB = tileEntity.getItem(slotB); if (stackA.isEmpty() && stackB.isEmpty()) { return new Object[]{true}; } else if (!stackA.isEmpty() && !stackB.isEmpty()) { @@ -103,37 +106,37 @@ public Object[] transferStack(final Context context, final Arguments args) { if (notPermitted()) return new Object[]{null, "permission denied"}; final int slotA = checkSlot(args, 0); final int slotB = checkSlot(args, 1); - final int count = Math.max(0, Math.min(args.count() > 2 && args.checkAny(2) != null ? args.checkInteger(2) : 64, tileEntity.getInventoryStackLimit())); + final int count = Math.max(0, Math.min(args.count() > 2 && args.checkAny(2) != null ? args.checkInteger(2) : 64, tileEntity.getMaxStackSize())); if (slotA == slotB || count == 0) { return new Object[]{true}; } - final ItemStack stackA = tileEntity.getStackInSlot(slotA); - final ItemStack stackB = tileEntity.getStackInSlot(slotB); + final ItemStack stackA = tileEntity.getItem(slotA); + final ItemStack stackB = tileEntity.getItem(slotB); if (stackA.isEmpty()) { // Empty. return new Object[]{false}; } else if (stackB.isEmpty()) { // Move. - tileEntity.setInventorySlotContents(slotB, tileEntity.decrStackSize(slotA, count)); + tileEntity.setItem(slotB, tileEntity.removeItem(slotA, count)); return new Object[]{true}; } else if (itemEquals(stackA, stackB)) { // Pile. - final int space = Math.min(tileEntity.getInventoryStackLimit(), stackB.getMaxStackSize()) - stackB.getCount(); + final int space = Math.min(tileEntity.getMaxStackSize(), stackB.getMaxStackSize()) - stackB.getCount(); final int amount = Math.min(count, Math.min(space, stackA.getCount())); if (amount > 0) { // Some. stackA.setCount(stackA.getCount() - amount); stackB.setCount(stackB.getCount() + amount); if (stackA.getCount() == 0) { - tileEntity.setInventorySlotContents(slotA, ItemStack.EMPTY); + tileEntity.setItem(slotA, ItemStack.EMPTY); } - tileEntity.markDirty(); + tileEntity.setChanged(); return new Object[]{true}; } } else if (count >= stackA.getCount()) { // Swap. - tileEntity.setInventorySlotContents(slotB, stackA); - tileEntity.setInventorySlotContents(slotA, stackB); + tileEntity.setItem(slotB, stackA); + tileEntity.setItem(slotA, stackB); return new Object[]{true}; } // Fail. @@ -141,10 +144,10 @@ public Object[] transferStack(final Context context, final Arguments args) { } @Callback(doc = "function(slot:number):table -- Get a description of the item stack in the specified slot.") - public Object[] getStackInSlot(final Context context, final Arguments args) { + public Object[] getItem(final Context context, final Arguments args) { if (Settings.get().allowItemStackInspection()) { if (notPermitted()) return new Object[]{null, "permission denied"}; - return new Object[]{tileEntity.getStackInSlot(checkSlot(args, 0))}; + return new Object[]{tileEntity.getItem(checkSlot(args, 0))}; } else { return new Object[]{null, "not enabled in config"}; } @@ -154,9 +157,9 @@ public Object[] getStackInSlot(final Context context, final Arguments args) { public Object[] getAllStacks(final Context context, final Arguments args) { if (Settings.get().allowItemStackInspection()) { if (notPermitted()) return new Object[]{null, "permission denied"}; - ItemStack[] allStacks = new ItemStack[tileEntity.getSizeInventory()]; - for (int i = 0; i < tileEntity.getSizeInventory(); i++) { - allStacks[i] = tileEntity.getStackInSlot(i); + ItemStack[] allStacks = new ItemStack[tileEntity.getContainerSize()]; + for (int i = 0; i < tileEntity.getContainerSize(); i++) { + allStacks[i] = tileEntity.getItem(i); } return new Object[]{allStacks}; } else { @@ -166,22 +169,23 @@ public Object[] getAllStacks(final Context context, final Arguments args) { private int checkSlot(final Arguments args, final int number) { final int slot = args.checkInteger(number) - 1; - if (slot < 0 || slot >= tileEntity.getSizeInventory()) { + if (slot < 0 || slot >= tileEntity.getContainerSize()) { throw new IllegalArgumentException("slot index out of bounds"); } return slot; } private boolean itemEquals(final ItemStack stackA, final ItemStack stackB) { - return stackA.getItem().equals(stackB.getItem()) && !stackA.getHasSubtypes() || stackA.getItemDamage() == stackB.getItemDamage(); + return stackA.getItem().equals(stackB.getItem()) && stackA.getDamageValue() == stackB.getDamageValue(); } private boolean notPermitted() { synchronized (fakePlayer) { - fakePlayer.setPosition(position.toVec3().x, position.toVec3().y, position.toVec3().z); - final PlayerInteractEvent.RightClickBlock event = new PlayerInteractEvent.RightClickBlock(fakePlayer, EnumHand.MAIN_HAND, position.toBlockPos(), EnumFacing.DOWN, null); + fakePlayer.setPos(position.toVec3().x, position.toVec3().y, position.toVec3().z); + final BlockRayTraceResult trace = new BlockRayTraceResult(fakePlayer.position(), Direction.DOWN, position.toBlockPos(), false); + final PlayerInteractEvent.RightClickBlock event = new PlayerInteractEvent.RightClickBlock(fakePlayer, Hand.MAIN_HAND, position.toBlockPos(), trace); MinecraftForge.EVENT_BUS.post(event); - return !event.isCanceled() && event.getUseBlock() != Event.Result.DENY && !tileEntity.isUsableByPlayer(fakePlayer); + return !event.isCanceled() && event.getUseBlock() != Event.Result.DENY && !tileEntity.stillValid(fakePlayer); } } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverMobSpawner.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverMobSpawner.scala index 17fe50ff17..a4cec231f1 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverMobSpawner.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverMobSpawner.scala @@ -10,33 +10,33 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityMobSpawner -import net.minecraft.util.EnumFacing +import net.minecraft.tileentity.MobSpawnerTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World object DriverMobSpawner extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityMobSpawner] + override def getTileEntityClass: Class[_] = classOf[MobSpawnerTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityMobSpawner]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[MobSpawnerTileEntity]) - final class Environment(tileEntity: TileEntityMobSpawner) extends ManagedTileEntityEnvironment[TileEntityMobSpawner](tileEntity, "mob_spawner") with NamedBlock { + final class Environment(tileEntity: MobSpawnerTileEntity) extends ManagedTileEntityEnvironment[MobSpawnerTileEntity](tileEntity, "mob_spawner") with NamedBlock { override def preferredName = "mob_spawner" override def priority = 0 @Callback(doc = "function():string -- Get the name of the entity that is being spawned by this spawner.") def getSpawningMobName(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.getSpawnerBaseLogic.getEntityId) + result(tileEntity.getSpawner.getEntityId) } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.MOB_SPAWNER) + if (!stack.isEmpty && Block.byItem(stack.getItem) == Blocks.SPAWNER) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverNoteBlock.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverNoteBlock.scala index 756cb0c50a..705c2d2ebb 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverNoteBlock.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverNoteBlock.scala @@ -1,37 +1,47 @@ package li.cil.oc.integration.minecraft +import li.cil.oc.api.Network +import li.cil.oc.api.driver.DriverBlock import li.cil.oc.api.driver.EnvironmentProvider import li.cil.oc.api.driver.NamedBlock import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network.ManagedEnvironment -import li.cil.oc.api.prefab.DriverSidedTileEntity -import li.cil.oc.integration.ManagedTileEntityEnvironment +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.block.material.Material -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks +import net.minecraft.block.NoteBlock import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntityNote -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World -object DriverNoteBlock extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[TileEntityNote] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[TileEntityNote]) +object DriverNoteBlock extends DriverBlock { + override def worksWith(world: World, pos: BlockPos, side: Direction) = world.getBlockState(pos).is(Blocks.NOTE_BLOCK) + + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world, pos) + + final class Environment(val world: World, val pos: BlockPos) extends AbstractManagedEnvironment with NamedBlock { + setNode(Network.newNode(this, Visibility.Network). + withComponent(preferredName). + create()) - final class Environment(tileEntity: TileEntityNote) extends ManagedTileEntityEnvironment[TileEntityNote](tileEntity, "note_block") with NamedBlock { override def preferredName = "note_block" override def priority = 0 @Callback(direct = true, doc = "function():number -- Get the currently set pitch on this note block.") def getPitch(context: Context, args: Arguments): Array[AnyRef] = { - result(tileEntity.note + 1) + val state = world.getBlockState(pos) + if (!state.is(Blocks.NOTE_BLOCK)) { + throw new IllegalArgumentException("block removed") + } + result(state.getValue(NoteBlock.NOTE).intValue + 1) } @Callback(doc = "function(value:number) -- Set the pitch for this note block. Must be in the interval [1, 25].") @@ -45,26 +55,34 @@ object DriverNoteBlock extends DriverSidedTileEntity { if (args.count > 0 && args.checkAny(0) != null) { setPitch(args.checkInteger(0)) } - val world = tileEntity.getWorld - val pos = tileEntity.getPos - val material = world.getBlockState(pos.add(0, 1, 0)).getMaterial - val canTrigger = material == Material.AIR - tileEntity.triggerNote(world, pos) + else { + val state = world.getBlockState(pos) + if (!state.is(Blocks.NOTE_BLOCK)) { + throw new IllegalArgumentException("block removed") + } + } + val canTrigger = world.isEmptyBlock(pos.above) + if (canTrigger) world.blockEvent(pos, Blocks.NOTE_BLOCK, 0, 0) result(canTrigger) } private def setPitch(value: Int): Unit = { - if (value < 1 || value > 25) { + val pitch = Int.box(value - 1) + if (!NoteBlock.NOTE.getPossibleValues.contains(pitch)) { throw new IllegalArgumentException("invalid pitch") } - tileEntity.note = (value - 1).toByte - tileEntity.markDirty() + val state = world.getBlockState(pos) + if (!state.is(Blocks.NOTE_BLOCK)) { + throw new IllegalArgumentException("block removed") + } + val newState = state.setValue(NoteBlock.NOTE, pitch) + if (newState != state) world.setBlock(pos, newState, 3) } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.NOTEBLOCK) + if (!stack.isEmpty && Block.byItem(stack.getItem) == Blocks.NOTE_BLOCK) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/DriverRecordPlayer.scala b/src/main/scala/li/cil/oc/integration/minecraft/DriverRecordPlayer.scala index ccb4fd9e8c..ef2d95ff95 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/DriverRecordPlayer.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/DriverRecordPlayer.scala @@ -10,22 +10,23 @@ import li.cil.oc.api.prefab.DriverSidedTileEntity import li.cil.oc.integration.ManagedTileEntityEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.block.Block -import net.minecraft.block.BlockJukebox -import net.minecraft.init.Blocks +import net.minecraft.block.Blocks import net.minecraft.item.Item -import net.minecraft.item.ItemRecord import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.item.MusicDiscItem +import net.minecraft.tileentity.JukeboxTileEntity +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.LanguageMap import net.minecraft.world.World object DriverRecordPlayer extends DriverSidedTileEntity { - override def getTileEntityClass: Class[_] = classOf[BlockJukebox.TileEntityJukebox] + override def getTileEntityClass: Class[_] = classOf[JukeboxTileEntity] - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = - new Environment(world.getTileEntity(pos).asInstanceOf[BlockJukebox.TileEntityJukebox]) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = + new Environment(world.getBlockEntity(pos).asInstanceOf[JukeboxTileEntity]) - final class Environment(tileEntity: BlockJukebox.TileEntityJukebox) extends ManagedTileEntityEnvironment[BlockJukebox.TileEntityJukebox](tileEntity, "jukebox") with NamedBlock { + final class Environment(tileEntity: JukeboxTileEntity) extends ManagedTileEntityEnvironment[JukeboxTileEntity](tileEntity, "jukebox") with NamedBlock { override def preferredName = "jukebox" override def priority = 0 @@ -33,8 +34,8 @@ object DriverRecordPlayer extends DriverSidedTileEntity { @Callback(doc = "function():string -- Get the title of the record currently in the jukebox.") def getRecord(context: Context, args: Arguments): Array[AnyRef] = { val record = tileEntity.getRecord - if (record != null && record.getItem.isInstanceOf[ItemRecord]) { - result(record.getItem.asInstanceOf[ItemRecord].getRecordNameLocal) + if (!record.isEmpty && record.getItem.isInstanceOf[MusicDiscItem]) { + result(LanguageMap.getInstance.getOrDefault(record.getItem.asInstanceOf[MusicDiscItem].getDescriptionId)) } else null } @@ -42,8 +43,8 @@ object DriverRecordPlayer extends DriverSidedTileEntity { @Callback(doc = "function() -- Start playing the record currently in the jukebox.") def play(context: Context, args: Arguments): Array[AnyRef] = { val record = tileEntity.getRecord - if (record != null && record.getItem.isInstanceOf[ItemRecord]) { - tileEntity.getWorld.playEvent(null, 1005, tileEntity.getPos, Item.getIdFromItem(record.getItem)) + if (!record.isEmpty && record.getItem.isInstanceOf[MusicDiscItem]) { + tileEntity.getLevel.levelEvent(null, 1010, tileEntity.getBlockPos, Item.getId(record.getItem)) result(true) } else null @@ -51,15 +52,14 @@ object DriverRecordPlayer extends DriverSidedTileEntity { @Callback(doc = "function() -- Stop playing the record currently in the jukebox.") def stop(context: Context, args: Arguments): Array[AnyRef] = { - tileEntity.getWorld.playEvent(1005, tileEntity.getPos, 0) - tileEntity.getWorld.playRecord(tileEntity.getPos, null) + tileEntity.getLevel.levelEvent(1010, tileEntity.getBlockPos, 0) null } } object Provider extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = { - if (!stack.isEmpty && Block.getBlockFromItem(stack.getItem) == Blocks.JUKEBOX) + if (stack.getItem == Blocks.JUKEBOX.asItem) classOf[Environment] else null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala b/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala index f20b626cb5..f93b196d53 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala @@ -5,13 +5,13 @@ import li.cil.oc.api.event.GeolyzerEvent import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block -import net.minecraft.block.BlockCrops -import net.minecraft.block.BlockStem -import net.minecraft.block.properties.PropertyInteger -import net.minecraft.block.state.IBlockState -import net.minecraft.init.Blocks -import net.minecraftforge.fluids.FluidRegistry -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraft.block.CropsBlock +import net.minecraft.block.StemBlock +import net.minecraft.state.IntegerProperty +import net.minecraft.block.BlockState +import net.minecraft.block.Blocks +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fluids.IFluidBlock import scala.collection.convert.WrapAsScala._ @@ -26,24 +26,20 @@ object EventHandlerVanilla { } val noise = new Array[Byte](e.data.length) - world.rand.nextBytes(noise) + world.random.nextBytes(noise) // Map to [-1, 1). The additional /33f is for normalization below. noise.map(_ / 128f / 33f).copyToArray(e.data) val w = e.maxX - e.minX + 1 val d = e.maxZ - e.minZ + 1 for (ry <- e.minY to e.maxY; rz <- e.minZ to e.maxZ; rx <- e.minX to e.maxX) { - val pos = blockPos.toBlockPos.add(rx, ry, rz) - val x = blockPos.x + rx - val y = blockPos.y + ry - val z = blockPos.z + rz + val pos = blockPos.toBlockPos.offset(rx, ry, rz) val index = (rx - e.minX) + ((rz - e.minZ) + (ry - e.minY) * d) * w - if (world.isBlockLoaded(pos) && !world.isAirBlock(pos)) { + if (world.isLoaded(pos) && !world.isEmptyBlock(pos)) { val blockState = world.getBlockState(pos) - val block = blockState.getBlock - if (block != Blocks.AIR && (includeReplaceable || isFluid(block) || !block.isReplaceable(world, blockPos.toBlockPos))) { + if (!blockState.getBlock.isAir(blockState, world, pos) && (includeReplaceable || blockState.getBlock.isInstanceOf[IFluidBlock] || !blockState.getMaterial.isReplaceable)) { val distance = math.sqrt(rx * rx + ry * ry + rz * rz).toFloat - e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + blockState.getBlockHardness(world, pos) + e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + blockState.getDestroySpeed(world, pos) } else e.data(index) = 0 } @@ -51,13 +47,11 @@ object EventHandlerVanilla { } } - private def isFluid(block: Block) = FluidRegistry.lookupFluidForBlock(block) != null - - private def getGrowth(blockState: IBlockState) = { - blockState.getPropertyKeys().find(prop => {prop.isInstanceOf[PropertyInteger] && prop.getName() == "age"}) match { + private def getGrowth(blockState: BlockState) = { + blockState.getProperties().find(prop => {prop.isInstanceOf[IntegerProperty] && prop.getName() == "age"}) match { case Some(prop) => { - val propAge = prop.asInstanceOf[PropertyInteger] - Some((blockState.getValue(propAge).toFloat / propAge.getAllowedValues().max) max 0 min 1) + val propAge = prop.asInstanceOf[IntegerProperty] + Some((blockState.getValue(propAge).toFloat / propAge.getPossibleValues().max) max 0 min 1) } case None => None } @@ -66,40 +60,34 @@ object EventHandlerVanilla { @SubscribeEvent def onGeolyzerAnalyze(e: GeolyzerEvent.Analyze) { val world = e.host.world - val blockState = world.getBlockState(e.pos).getActualState(world, e.pos) + val blockState = world.getBlockState(e.pos) val block = blockState.getBlock - e.data += "name" -> Block.REGISTRY.getNameForObject(block) - e.data += "hardness" -> Float.box(blockState.getBlockHardness(world, e.pos)) + e.data += "name" -> block.getRegistryName + e.data += "hardness" -> Float.box(blockState.getDestroySpeed(world, e.pos)) e.data += "harvestLevel" -> Int.box(block.getHarvestLevel(blockState)) - e.data += "harvestTool" -> block.getHarvestTool(blockState) - e.data += "color" -> Int.box(blockState.getMapColor(world, e.pos).colorValue) + e.data += "harvestTool" -> Option(block.getHarvestTool(blockState)).map(_.getName).orNull + e.data += "color" -> Int.box(blockState.getMapColor(world, e.pos).col) // backward compatibility - e.data += "metadata" -> Int.box({ - try { - block.getMetaFromState(blockState) - } catch { - case _ :IllegalArgumentException => 0 - } - }) + e.data += "metadata" -> Int.box(0) e.data += "properties" -> { var props:Map[String, Any] = Map(); - for (prop <- blockState.getProperties().keySet()) { + for (prop <- blockState.getProperties()) { props += prop.getName() -> blockState.getValue(prop) } props } if (Settings.get.insertIdsInConverters) { - e.data += "id" -> Int.box(Block.getIdFromBlock(block)) + e.data += "id" -> Int.box(Block.getId(blockState)) } { - if (block.isInstanceOf[BlockCrops] || block.isInstanceOf[BlockStem] || block == Blocks.COCOA || block == Blocks.NETHER_WART || block == Blocks.CHORUS_FLOWER) { + if (block.isInstanceOf[CropsBlock] || block.isInstanceOf[StemBlock] || block == Blocks.COCOA || block == Blocks.NETHER_WART || block == Blocks.CHORUS_FLOWER) { getGrowth(blockState) - } else if (block == Blocks.MELON_BLOCK || block == Blocks.PUMPKIN || block == Blocks.CACTUS || block == Blocks.REEDS || block == Blocks.CHORUS_PLANT) { + } else if (block == Blocks.MELON || block == Blocks.PUMPKIN || block == Blocks.CACTUS || block == Blocks.SUGAR_CANE || block == Blocks.CHORUS_PLANT) { Some(1f) } else { None diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala b/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala index 0c7672e33a..88e1e39c1a 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala @@ -8,9 +8,9 @@ import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.block.BlockRedstoneWire -import net.minecraft.init.Blocks -import net.minecraft.util.EnumFacing +import net.minecraft.block.Blocks +import net.minecraft.block.RedstoneWireBlock +import net.minecraft.util.Direction import net.minecraftforge.common.MinecraftForge object ModMinecraft extends ModProxy with RedstoneProvider { @@ -50,20 +50,17 @@ object ModMinecraft extends ModProxy with RedstoneProvider { Driver.add(ConverterItemStack) Driver.add(ConverterNBT) Driver.add(ConverterWorld) - Driver.add(ConverterWorldProvider) - - RecipeHandler.init() BundledRedstone.addProvider(this) MinecraftForge.EVENT_BUS.register(EventHandlerVanilla) } - override def computeInput(pos: BlockPosition, side: EnumFacing): Int = { + override def computeInput(pos: BlockPosition, side: Direction): Int = { val world = pos.world.get math.max(world.computeRedstoneSignal(pos, side), - if (world.getBlock(pos.offset(side)) == Blocks.REDSTONE_WIRE) world.getBlockMetadata(pos.offset(side)).getValue(BlockRedstoneWire.POWER).intValue() else 0) + if (world.getBlock(pos.offset(side)) == Blocks.REDSTONE_WIRE) world.getBlockState(pos.offset(side).toBlockPos).getValue(RedstoneWireBlock.POWER).intValue() else 0) } - override def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = null + override def computeBundledInput(pos: BlockPosition, side: Direction): Array[Int] = null } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/RecipeHandler.scala b/src/main/scala/li/cil/oc/integration/minecraft/RecipeHandler.scala deleted file mode 100644 index d4436ccc97..0000000000 --- a/src/main/scala/li/cil/oc/integration/minecraft/RecipeHandler.scala +++ /dev/null @@ -1,77 +0,0 @@ -package li.cil.oc.integration.minecraft - -import com.typesafe.config.Config -import li.cil.oc.common.recipe.ExtendedShapedOreRecipe -import li.cil.oc.common.recipe.ExtendedShapelessOreRecipe -import li.cil.oc.common.recipe.Recipes -import li.cil.oc.common.recipe.Recipes.RecipeException -import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.FurnaceRecipes -import net.minecraftforge.fml.common.registry.GameRegistry -import net.minecraftforge.oredict.OreDictionary - -import scala.collection.convert.WrapAsScala._ -import scala.collection.mutable - -object RecipeHandler { - def init(): Unit = { - Recipes.registerRecipeHandler("shaped", addShapedRecipe) - Recipes.registerRecipeHandler("shapeless", addShapelessRecipe) - Recipes.registerRecipeHandler("furnace", addFurnaceRecipe) - } - - def addShapedRecipe(output: ItemStack, recipe: Config) { - val rows = recipe.getList("input").unwrapped().map { - case row: java.util.List[AnyRef]@unchecked => row.map(Recipes.parseIngredient) - case other => throw new RecipeException(s"Invalid row entry for shaped recipe (not a list: $other).") - } - output.setCount(Recipes.tryGetCount(recipe)) - - var number = -1 - var shape = mutable.ArrayBuffer.empty[String] - val input = mutable.ArrayBuffer.empty[AnyRef] - for (row <- rows) { - val (pattern, ingredients) = row.foldLeft((new StringBuilder, Iterable.empty[AnyRef]))((acc, ingredient) => { - val (pattern, ingredients) = acc - ingredient match { - case _@(_: ItemStack | _: String) => - number += 1 - (pattern.append(('a' + number).toChar), ingredients ++ Iterable(Char.box(('a' + number).toChar), ingredient)) - case _ => (pattern.append(' '), ingredients) - } - }) - shape += pattern.toString - input ++= ingredients - } - if (input.nonEmpty && output.getCount > 0) { - Recipes.addRecipe(new ExtendedShapedOreRecipe(output, shape ++ input: _*)) - } - } - - def addShapelessRecipe(output: ItemStack, recipe: Config) { - val input = recipe.getValue("input").unwrapped() match { - case list: java.util.List[AnyRef]@unchecked => list.map(Recipes.parseIngredient) - case other => Seq(Recipes.parseIngredient(other)) - } - output.setCount(Recipes.tryGetCount(recipe)) - - if (input.nonEmpty && output.getCount > 0) { - Recipes.addRecipe(new ExtendedShapelessOreRecipe(output, input: _*)) - } - } - - def addFurnaceRecipe(output: ItemStack, recipe: Config) { - val input = Recipes.parseIngredient(recipe.getValue("input").unwrapped()) - output.setCount(Recipes.tryGetCount(recipe)) - - input match { - case stack: ItemStack => - FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) - case name: String => - for (stack <- OreDictionary.getOres(name)) { - FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) - } - case _ => - } - } -} diff --git a/src/main/scala/li/cil/oc/integration/minecraftforge/DriverEnergyStorage.scala b/src/main/scala/li/cil/oc/integration/minecraftforge/DriverEnergyStorage.scala index f241427100..33c45e95a9 100644 --- a/src/main/scala/li/cil/oc/integration/minecraftforge/DriverEnergyStorage.scala +++ b/src/main/scala/li/cil/oc/integration/minecraftforge/DriverEnergyStorage.scala @@ -11,7 +11,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.ResultWrapper.result import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraftforge.energy.CapabilityEnergy @@ -22,13 +22,13 @@ import net.minecraftforge.energy.IEnergyStorage */ object DriverEnergyStorage extends DriverBlock { - override def worksWith(world: World, pos: BlockPos, side: EnumFacing): Boolean = world.getTileEntity(pos) match { - case tile: TileEntity if tile.hasCapability(CapabilityEnergy.ENERGY, side) => true + override def worksWith(world: World, pos: BlockPos, side: Direction): Boolean = world.getBlockEntity(pos) match { + case tile: TileEntity if tile.getCapability(CapabilityEnergy.ENERGY, side).isPresent => true case _ => false } - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): ManagedEnvironment = world.getTileEntity(pos) match { - case tile: TileEntity if tile.hasCapability(CapabilityEnergy.ENERGY, side) => new Environment(tile.getCapability(CapabilityEnergy.ENERGY, side)) + override def createEnvironment(world: World, pos: BlockPos, side: Direction): ManagedEnvironment = world.getBlockEntity(pos) match { + case tile: TileEntity if tile.getCapability(CapabilityEnergy.ENERGY, side).isPresent => new Environment(tile.getCapability(CapabilityEnergy.ENERGY, side).orElse(null)) case _ => null } diff --git a/src/main/scala/li/cil/oc/integration/minecraftforge/EventHandlerMinecraftForge.scala b/src/main/scala/li/cil/oc/integration/minecraftforge/EventHandlerMinecraftForge.scala index 3f7e530c04..2650260f26 100644 --- a/src/main/scala/li/cil/oc/integration/minecraftforge/EventHandlerMinecraftForge.scala +++ b/src/main/scala/li/cil/oc/integration/minecraftforge/EventHandlerMinecraftForge.scala @@ -5,14 +5,16 @@ import li.cil.oc.common.tileentity.traits.PowerAcceptor import li.cil.oc.integration.util.Power import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.event.AttachCapabilitiesEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent object EventHandlerMinecraftForge { @@ -20,39 +22,50 @@ object EventHandlerMinecraftForge { def onAttachCapabilities(event: AttachCapabilitiesEvent[TileEntity]): Unit = { event.getObject match { case tileEntity: PowerAcceptor => - event.addCapability(ProviderEnergy, new Provider(tileEntity)) + val provider = new Provider(tileEntity) + event.addCapability(ProviderEnergy, provider) + event.addListener(new Runnable { + override def run = provider.invalidate + }) case _ => } } def canCharge(stack: ItemStack): Boolean = - if (stack.hasCapability(CapabilityEnergy.ENERGY, null)) stack.getCapability(CapabilityEnergy.ENERGY, null) match { + stack.getCapability(CapabilityEnergy.ENERGY, null).orElse(null) match { case storage: IEnergyStorage => storage.canReceive case _ => false - } else false + } def charge(stack: ItemStack, amount: Double, simulate: Boolean): Double = - if (stack.hasCapability(CapabilityEnergy.ENERGY, null)) stack.getCapability(CapabilityEnergy.ENERGY, null) match { + stack.getCapability(CapabilityEnergy.ENERGY, null).orElse(null) match { case storage: IEnergyStorage => amount - Power.fromRF(storage.receiveEnergy(Power.toRF(amount), simulate)) case _ => amount - } else amount + } val ProviderEnergy: ResourceLocation = new ResourceLocation(OpenComputers.ID, "forgeenergy") class Provider(tile: PowerAcceptor) extends ICapabilityProvider { - private val providers = EnumFacing.VALUES.map(side => new EnergyStorageImpl(tile, side)) - private val nullProvider = new EnergyStorageImpl(tile, null) + private val providers = Direction.values.map(side => LazyOptional.of(new NonNullSupplier[EnergyStorageImpl] { + override def get = new EnergyStorageImpl(tile, side) + })) + private val nullProvider = LazyOptional.of(new NonNullSupplier[EnergyStorageImpl] { + override def get = new EnergyStorageImpl(tile, null) + }) - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = capability == CapabilityEnergy.ENERGY + def invalidate(): Unit = { + for (provider <- providers) provider.invalidate + nullProvider.invalidate + } - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = { + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { if (capability == CapabilityEnergy.ENERGY) { - (if (facing == null) nullProvider else providers(facing.getIndex)).asInstanceOf[T] - } else null.asInstanceOf[T] + (if (facing == null) nullProvider.cast[T] else providers(facing.get3DDataValue)).cast[T] + } else LazyOptional.empty[T] } - class EnergyStorageImpl(val tile: PowerAcceptor, val side: EnumFacing) extends IEnergyStorage { + class EnergyStorageImpl(val tile: PowerAcceptor, val side: Direction) extends IEnergyStorage { override def getEnergyStored: Int = Power.toRF(tile.globalBuffer(side)) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala index 4072a146ff..752ffed160 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala @@ -18,7 +18,7 @@ object DriverAPU extends DriverCPU with HostAware { api.Items.get(Constants.ItemName.APUCreative)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else gpuTier(stack) match { case Tier.One => new component.APU(Tier.One) case Tier.Two => new component.APU(Tier.Two) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala index ee5a35adca..21c70b7d7b 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala @@ -11,7 +11,7 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import li.cil.oc.server.machine.luac.NativeLuaArchitecture import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -41,8 +41,8 @@ abstract class DriverCPU extends Item with api.driver.item.MutableProcessor with override def allArchitectures = api.Machine.architectures.toList override def architecture(stack: ItemStack): Class[_ <: api.machine.Architecture] = { - if (stack.hasTagCompound) { - val archClass = stack.getTagCompound.getString(Settings.namespace + "archClass") match { + if (stack.hasTag) { + val archClass = stack.getTag.getString(Settings.namespace + "archClass") match { case clazz if clazz == classOf[NativeLuaArchitecture].getName => // Migrate old saved CPUs to new versions (since the class they refer still // exists, but is abstract, which would lead to issues). @@ -52,8 +52,8 @@ abstract class DriverCPU extends Item with api.driver.item.MutableProcessor with if (!archClass.isEmpty) try return Class.forName(archClass).asSubclass(classOf[api.machine.Architecture]) catch { case t: Throwable => OpenComputers.log.warn("Failed getting class for CPU architecture. Resetting CPU to use the default.", t) - stack.getTagCompound.removeTag(Settings.namespace + "archClass") - stack.getTagCompound.removeTag(Settings.namespace + "archName") + stack.getTag.remove(Settings.namespace + "archClass") + stack.getTag.remove(Settings.namespace + "archName") } } api.Machine.architectures.headOption.orNull @@ -61,9 +61,9 @@ abstract class DriverCPU extends Item with api.driver.item.MutableProcessor with override def setArchitecture(stack: ItemStack, architecture: Class[_ <: api.machine.Architecture]): Unit = { if (!worksWith(stack)) throw new IllegalArgumentException("Unsupported processor type.") - if (!stack.hasTagCompound) stack.setTagCompound(new NBTTagCompound()) - stack.getTagCompound.setString(Settings.namespace + "archClass", architecture.getName) - stack.getTagCompound.setString(Settings.namespace + "archName", api.Machine.getArchitectureName(architecture)) + val data = stack.getOrCreateTag + data.putString(Settings.namespace + "archClass", architecture.getName) + data.putString(Settings.namespace + "archName", api.Machine.getArchitectureName(architecture)) } override def getCallBudget(stack: ItemStack): Double = Settings.get.callBudgets(tier(stack) max Tier.One min Tier.Three) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala index 75410f4227..6b467a7f3c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala @@ -18,7 +18,7 @@ object DriverDataCard extends Item { api.Items.get(Constants.ItemName.DataCardTier3)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else tier(stack) match { case Tier.One => new component.DataCard.Tier1() case Tier.Two => new component.DataCard.Tier2() diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDebugCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDebugCard.scala index c469c7be96..94150fc1cc 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDebugCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDebugCard.scala @@ -13,7 +13,7 @@ object DriverDebugCard extends Item { api.Items.get(Constants.ItemName.DebugCard)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.DebugCard(host) override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDiskDriveMountable.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDiskDriveMountable.scala index 8a275d1f98..b4d5d52f28 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDiskDriveMountable.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.Slot import li.cil.oc.server.component import li.cil.oc.util.ExtendedInventory._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object DriverDiskDriveMountable extends Item with HostAware { override def worksWith(stack: ItemStack): Boolean = isOneOf(stack, @@ -22,10 +22,5 @@ object DriverDiskDriveMountable extends Item with HostAware { override def slot(stack: ItemStack): String = Slot.RackMountable - override def dataTag(stack: ItemStack): NBTTagCompound = { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - stack.getTagCompound - } + override def dataTag(stack: ItemStack): CompoundNBT = stack.getOrCreateTag } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala index fa309c537b..30cf0689b6 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverEEPROM.scala @@ -14,7 +14,7 @@ object DriverEEPROM extends Item { api.Items.get(Constants.ItemName.EEPROM)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.EEPROM() override def slot(stack: ItemStack) = Slot.EEPROM diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala index ab8177978a..d8b1625843 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -15,8 +15,8 @@ import li.cil.oc.common.item.data.DriveData import li.cil.oc.server.component.Drive import li.cil.oc.server.fs.FileSystem.{ItemLabel, ReadOnlyLabel} import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.common.DimensionManager +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.fml.server.ServerLifecycleHooks object DriverFileSystem extends Item { val UUIDVerifier = """^([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})$""".r @@ -26,10 +26,10 @@ object DriverFileSystem extends Item { api.Items.get(Constants.ItemName.HDDTier2), api.Items.get(Constants.ItemName.HDDTier3), api.Items.get(Constants.ItemName.Floppy)) && - (!stack.hasTagCompound || !stack.getTagCompound.hasKey(Settings.namespace + "lootPath")) + (!stack.hasTag || !stack.getTag.contains(Settings.namespace + "lootPath")) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else Delegator.subItem(stack) match { case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, hdd.platterCount, host, hdd.tier + 2) case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, 1, host, 1) @@ -49,13 +49,13 @@ object DriverFileSystem extends Item { case _ => 0 } - private def createEnvironment(stack: ItemStack, capacity: Int, platterCount: Int, host: EnvironmentHost, speed: Int) = if (DimensionManager.getWorld(0) != null) { - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "lootFactory")) { + private def createEnvironment(stack: ItemStack, capacity: Int, platterCount: Int, host: EnvironmentHost, speed: Int) = if (ServerLifecycleHooks.getCurrentServer != null) { + if (stack.hasTag && stack.getTag.contains(Settings.namespace + "lootFactory")) { // Loot disk, create file system using factory callback. - Loot.factories.get(stack.getTagCompound.getString(Settings.namespace + "lootFactory")) match { + Loot.factories.get(stack.getTag.getString(Settings.namespace + "lootFactory")) match { case Some(factory) => val label = - if (dataTag(stack).hasKey(Settings.namespace + "fs.label")) + if (dataTag(stack).contains(Settings.namespace + "fs.label")) dataTag(stack).getString(Settings.namespace + "fs.label") else null api.FileSystem.asManagedEnvironment(factory.call(), label, host, Settings.resourceDomain + ":floppy_access") @@ -90,13 +90,13 @@ object DriverFileSystem extends Item { } else null - private def addressFromTag(tag: NBTTagCompound) = - if (tag.hasKey("node") && tag.getCompoundTag("node").hasKey("address")) { - tag.getCompoundTag("node").getString("address") match { + private def addressFromTag(tag: CompoundNBT) = + if (tag.contains("node") && tag.getCompound("node").contains("address")) { + tag.getCompound("node").getString("address") match { case UUIDVerifier(address) => address case _ => // Invalid disk address. val newAddress = java.util.UUID.randomUUID().toString - tag.getCompoundTag("node").setString("address", newAddress) + tag.getCompound("node").putString("address", newAddress) OpenComputers.log.warn(s"Generated new address for disk '${newAddress}'.") newAddress } @@ -114,15 +114,15 @@ object DriverFileSystem extends Item { private final val LabelTag = Settings.namespace + "fs.label" - override def load(nbt: NBTTagCompound) { - if (nbt.hasKey(LabelTag)) { + override def loadData(nbt: CompoundNBT) { + if (nbt.contains(LabelTag)) { label = Option(nbt.getString(LabelTag)) } } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { label match { - case Some(value) => nbt.setString(LabelTag, value) + case Some(value) => nbt.putString(LabelTag, value) case _ => } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGeolyzer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGeolyzer.scala index afa811ee13..1982c76947 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGeolyzer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGeolyzer.scala @@ -14,7 +14,7 @@ object DriverGeolyzer extends Item with HostAware { api.Items.get(Constants.BlockName.Geolyzer)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.Geolyzer(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala index bc9f3a477f..a98b61a2c8 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala @@ -19,7 +19,7 @@ object DriverGraphicsCard extends Item with HostAware { api.Items.get(Constants.ItemName.GraphicsCardTier3)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else tier(stack) match { case Tier.One => new component.GraphicsCard(Tier.One) case Tier.Two => new component.GraphicsCard(Tier.Two) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverInternetCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverInternetCard.scala index e4a718bb1c..a82ab1c97e 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverInternetCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverInternetCard.scala @@ -14,7 +14,7 @@ object DriverInternetCard extends Item { api.Items.get(Constants.ItemName.InternetCard)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.InternetCard() override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLinkedCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLinkedCard.scala index e83a08d5c9..0691608573 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLinkedCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLinkedCard.scala @@ -14,7 +14,7 @@ object DriverLinkedCard extends Item { api.Items.get(Constants.ItemName.LinkedCard)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.LinkedCard() override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala index 08e6f3db3d..b0f98c534e 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala @@ -9,7 +9,8 @@ import li.cil.oc.api import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Slot import net.minecraft.item.ItemStack -import net.minecraftforge.common.DimensionManager +import net.minecraft.world.storage.FolderName +import net.minecraftforge.fml.server.ServerLifecycleHooks // This is deprecated and kept for compatibility with old saves. // As of OC 1.5.10, loot disks are generated using normal floppies, and using @@ -17,12 +18,12 @@ import net.minecraftforge.common.DimensionManager object DriverLootDisk extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get(Constants.ItemName.Floppy)) && - (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "lootPath")) + (stack.hasTag && stack.getTag.contains(Settings.namespace + "lootPath")) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (!host.world.isRemote && stack.hasTagCompound && DimensionManager.getWorld(0) != null) { - val lootPath = "loot/" + stack.getTagCompound.getString(Settings.namespace + "lootPath") - val savePath = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + lootPath) + if (!host.world.isClientSide && stack.hasTag && ServerLifecycleHooks.getCurrentServer != null) { + val lootPath = Settings.savePath + "loot/" + stack.getTag.getString(Settings.namespace + "lootPath") + val savePath = ServerLifecycleHooks.getCurrentServer.getWorldPath(new FolderName(lootPath)).toFile val fs = if (savePath.exists && savePath.isDirectory) { api.FileSystem.fromSaveDirectory(lootPath, 0, false) @@ -31,7 +32,7 @@ object DriverLootDisk extends Item { api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, lootPath) } val label = - if (dataTag(stack).hasKey(Settings.namespace + "fs.label")) { + if (dataTag(stack).contains(Settings.namespace + "fs.label")) { dataTag(stack).getString(Settings.namespace + "fs.label") } else null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverMotionSensor.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverMotionSensor.scala index a0416e4d69..d4e4ffba1d 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverMotionSensor.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverMotionSensor.scala @@ -15,7 +15,7 @@ object DriverMotionSensor extends Item with HostAware { api.Items.get(Constants.BlockName.MotionSensor)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.MotionSensor(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverNetworkCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverNetworkCard.scala index 70fd0ca611..fa588a42c6 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverNetworkCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverNetworkCard.scala @@ -14,7 +14,7 @@ object DriverNetworkCard extends Item with HostAware { api.Items.get(Constants.ItemName.NetworkCard)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.NetworkCard(host) override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala index c856b60e35..4e902344bb 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala @@ -22,7 +22,7 @@ object DriverRedstoneCard extends Item with HostAware { api.Items.get(Constants.ItemName.RedstoneCardTier2)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else { val isAdvanced = tier(stack) == Tier.Two val hasBundled = BundledRedstone.isAvailable && isAdvanced diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverServer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverServer.scala index b3033da84a..12f4f7e56c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverServer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverServer.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.Slot import li.cil.oc.server.component import li.cil.oc.util.ExtendedInventory._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object DriverServer extends Item with HostAware { override def worksWith(stack: ItemStack): Boolean = isOneOf(stack, @@ -25,10 +25,5 @@ object DriverServer extends Item with HostAware { override def slot(stack: ItemStack): String = Slot.RackMountable - override def dataTag(stack: ItemStack): NBTTagCompound = { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - stack.getTagCompound - } + override def dataTag(stack: ItemStack): CompoundNBT = stack.getOrCreateTag } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala index 8507051806..6f21c60c04 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTablet.scala @@ -10,7 +10,7 @@ import li.cil.oc.common.Slot import li.cil.oc.common.item.Tablet import li.cil.oc.common.item.data.TabletData import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.common.util.Constants.NBT object DriverTablet extends Item { @@ -18,7 +18,7 @@ object DriverTablet extends Item { api.Items.get(Constants.ItemName.Tablet)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else { Tablet.Server.cache.invalidate(Tablet.getId(stack)) val data = new TabletData(stack) @@ -28,7 +28,7 @@ object DriverTablet extends Item { case Some(environment) => environment.node match { case component: Component => component.setVisibility(Visibility.Network) - environment.save(dataTag(stack)) + environment.saveData(dataTag(stack)) environment case _ => null } @@ -44,21 +44,21 @@ object DriverTablet extends Item { case fs if !fs.isEmpty => DriverFileSystem.worksWith(fs) case _ => false } - if (index >= 0 && stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "items")) { - val baseTag = stack.getTagCompound.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND).getCompoundTagAt(index) - if (!baseTag.hasKey("item")) { - baseTag.setTag("item", new NBTTagCompound()) + if (index >= 0 && stack.hasTag && stack.getTag.contains(Settings.namespace + "items")) { + val baseTag = stack.getTag.getList(Settings.namespace + "items", NBT.TAG_COMPOUND).getCompound(index) + if (!baseTag.contains("item")) { + baseTag.put("item", new CompoundNBT()) } - val itemTag = baseTag.getCompoundTag("item") - if (!itemTag.hasKey("tag")) { - itemTag.setTag("tag", new NBTTagCompound()) + val itemTag = baseTag.getCompound("item") + if (!itemTag.contains("tag")) { + itemTag.put("tag", new CompoundNBT()) } - val stackTag = itemTag.getCompoundTag("tag") - if (!stackTag.hasKey(Settings.namespace + "data")) { - stackTag.setTag(Settings.namespace + "data", new NBTTagCompound()) + val stackTag = itemTag.getCompound("tag") + if (!stackTag.contains(Settings.namespace + "data")) { + stackTag.put(Settings.namespace + "data", new CompoundNBT()) } - stackTag.getCompoundTag(Settings.namespace + "data") + stackTag.getCompound(Settings.namespace + "data") } - else new NBTTagCompound() + else new CompoundNBT() } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTransposer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTransposer.scala index a2637d1048..79d54c22a9 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverTransposer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverTransposer.scala @@ -14,7 +14,7 @@ object DriverTransposer extends Item with HostAware { api.Items.get(Constants.BlockName.Transposer)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.Transposer.Upgrade(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeAngel.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeAngel.scala index 3f7a1013e6..0949f2e021 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeAngel.scala @@ -14,7 +14,7 @@ object DriverUpgradeAngel extends Item with HostAware { api.Items.get(Constants.ItemName.AngelUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeAngel() override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala index ce2ba4f6fb..956648ea26 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala @@ -18,7 +18,7 @@ object DriverUpgradeBattery extends Item with HostAware { api.Items.get(Constants.ItemName.BatteryUpgradeTier3)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeBattery(tier(stack)) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeChunkloader.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeChunkloader.scala index 36532b63b6..8f89e69f84 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeChunkloader.scala @@ -15,7 +15,7 @@ object DriverUpgradeChunkloader extends Item with HostAware { api.Items.get(Constants.ItemName.ChunkloaderUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeChunkloader(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeCrafting.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeCrafting.scala index 6fab0a16ff..e71c32b2a4 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeCrafting.scala @@ -16,7 +16,7 @@ object DriverUpgradeCrafting extends Item with HostAware { api.Items.get(Constants.ItemName.CraftingUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case robot: EnvironmentHost with Robot => new component.UpgradeCrafting(robot) case _ => null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala index c9fdc5fe45..53dfbe20cd 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.inventory.DatabaseInventory import li.cil.oc.common.item import li.cil.oc.common.item.Delegator import li.cil.oc.server.component -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack object DriverUpgradeDatabase extends Item with api.driver.item.HostAware { @@ -19,11 +19,11 @@ object DriverUpgradeDatabase extends Item with api.driver.item.HostAware { api.Items.get(Constants.ItemName.DatabaseUpgradeTier3)) override def createEnvironment(stack: ItemStack, host: api.network.EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeDatabase(new DatabaseInventory { override def container = stack - override def isUsableByPlayer(player: EntityPlayer) = false + override def stillValid(player: PlayerEntity) = false }) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeGenerator.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeGenerator.scala index dae2f6999b..2caf6bf0d9 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeGenerator.scala @@ -16,7 +16,7 @@ object DriverUpgradeGenerator extends Item with HostAware { api.Items.get(Constants.ItemName.GeneratorUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case host: internal.Agent => new component.UpgradeGenerator(host) case _ => null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeInventoryController.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeInventoryController.scala index 85897d72ad..f8b429d25e 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeInventoryController.scala @@ -18,7 +18,7 @@ object DriverUpgradeInventoryController extends Item with HostAware { api.Items.get(Constants.ItemName.InventoryControllerUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case host: EnvironmentHost with Adapter => new component.UpgradeInventoryController.Adapter(host) case host: EnvironmentHost with Drone => new component.UpgradeInventoryController.Drone(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala index 0af34be1c8..e765b16513 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala @@ -16,7 +16,7 @@ object DriverUpgradeLeash extends Item with HostAware { api.Items.get(Constants.ItemName.LeashUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case entity: Entity => new component.UpgradeLeash(entity) case _ => null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala index fbd3bf62b1..47227398ba 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala @@ -6,10 +6,15 @@ import li.cil.oc.api.network.{EnvironmentHost, ManagedEnvironment} import li.cil.oc.common.{Slot, Tier} import li.cil.oc.server.component import li.cil.oc.util.BlockPosition +import li.cil.oc.util.RotationHelper import li.cil.oc.{Constants, Settings, api} import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing -import net.minecraftforge.common.DimensionManager +import net.minecraft.util.Direction +import net.minecraft.util.RegistryKey +import net.minecraft.util.ResourceLocation +import net.minecraft.util.registry.Registry +import net.minecraft.world.server.ServerWorld +import net.minecraftforge.fml.server.ServerLifecycleHooks /** * @author Vexatos @@ -26,14 +31,16 @@ object DriverUpgradeMF extends Item with HostAware { override def tier(stack: ItemStack) = Tier.Three override def createEnvironment(stack: ItemStack, host: EnvironmentHost): ManagedEnvironment = { - if (host.world != null && !host.world.isRemote) { - if (stack.hasTagCompound) { - stack.getTagCompound.getIntArray(Settings.namespace + "coord") match { - case Array(x, y, z, dim, side) => - Option(DimensionManager.getWorld(dim)) match { - case Some(world) => return new component.UpgradeMF(host, BlockPosition(x, y, z, world), EnumFacing.getFront(side)) + if (host.world != null && !host.world.isClientSide) { + if (stack.hasTag) { + stack.getTag.getIntArray(Settings.namespace + "coord") match { + case Array(x, y, z, side) => { + val dimension = new ResourceLocation(stack.getTag.getString(Settings.namespace + "dimension")) + ServerLifecycleHooks.getCurrentServer.getLevel(RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension)) match { + case world: ServerWorld => return new component.UpgradeMF(host, BlockPosition(x, y, z, world), Direction.from3DDataValue(side)) case _ => // Invalid dimension ID } + } case _ => // Invalid tag } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala index 150155f82d..1f18af0885 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeNavigation.scala @@ -16,7 +16,7 @@ object DriverUpgradeNavigation extends Item with HostAware { api.Items.get(Constants.ItemName.NavigationUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case rotatable: EnvironmentHost with Rotatable => new component.UpgradeNavigation(rotatable) case _ => null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala index 64b40bd11b..5e9eb7e311 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala @@ -15,7 +15,7 @@ object DriverUpgradePiston extends Item with HostAware { api.Items.get(Constants.ItemName.PistonUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case host: internal.Drone => new component.UpgradePiston.Drone(host) case host: internal.Tablet => new component.UpgradePiston.Tablet(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala index 58638827d9..7d5ba624d0 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSign.scala @@ -18,7 +18,7 @@ object DriverUpgradeSign extends Item with HostAware { api.Items.get(Constants.ItemName.SignUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case rotatable: EnvironmentHost with Rotatable => new UpgradeSignInRotatable(rotatable) case adapter: EnvironmentHost with Adapter => new UpgradeSignInAdapter(adapter) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala index f91a21c30f..f6d729953e 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeSolarGenerator.scala @@ -14,7 +14,7 @@ object DriverUpgradeSolarGenerator extends Item with HostAware { api.Items.get(Constants.ItemName.SolarGeneratorUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeSolarGenerator(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeStickyPiston.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeStickyPiston.scala index 0c1bb66615..7690ccde73 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeStickyPiston.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeStickyPiston.scala @@ -15,7 +15,7 @@ object DriverUpgradeStickyPiston extends Item with HostAware { api.Items.get(Constants.ItemName.StickyPistonUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost): ManagedEnvironment = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case host: internal.Drone => new component.UpgradeStickyPiston.Drone(host) case host: internal.Tablet => new component.UpgradeStickyPiston.Tablet(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTank.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTank.scala index 618875afcf..02612d6ecd 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTank.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTank.scala @@ -13,7 +13,7 @@ object DriverUpgradeTank extends Item with HostAware { api.Items.get(Constants.ItemName.TankUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else new component.UpgradeTank(host, 16000) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTankController.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTankController.scala index b66090ffe7..e225714152 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTankController.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTankController.scala @@ -18,7 +18,7 @@ object DriverUpgradeTankController extends Item with HostAware { api.Items.get(Constants.ItemName.TankControllerUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case host: EnvironmentHost with Adapter => new component.UpgradeTankController.Adapter(host) case host: EnvironmentHost with Drone => new component.UpgradeTankController.Drone(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTractorBeam.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTractorBeam.scala index 63306e940c..8f6f388457 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTractorBeam.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTractorBeam.scala @@ -19,7 +19,7 @@ object DriverUpgradeTractorBeam extends Item with HostAware { api.Items.get(Constants.ItemName.TractorBeamUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else host match { case drone: Drone => new UpgradeTractorBeam.Drone(drone) case robot: Robot => new component.UpgradeTractorBeam.Player(host, robot.player) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTrading.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTrading.scala index db66594eb2..603500d8a2 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeTrading.scala @@ -16,7 +16,7 @@ object DriverUpgradeTrading extends Item with HostAware { api.Items.get(Constants.ItemName.TradingUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world.isRemote) null + if (host.world.isClientSide) null else new UpgradeTrading(host) override def slot(stack: ItemStack) = Slot.Upgrade diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala index 485e1de094..b00f775a47 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala @@ -17,7 +17,7 @@ object DriverWirelessNetworkCard extends Item { api.Items.get(Constants.ItemName.WirelessNetworkCardTier2)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = - if (host.world != null && host.world.isRemote) null + if (host.world != null && host.world.isClientSide) null else tier(stack) match { case Tier.One => new component.WirelessNetworkCard.Tier1(host) case Tier.Two => new component.WirelessNetworkCard.Tier2(host) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/EnvironmentProviderBlocks.scala b/src/main/scala/li/cil/oc/integration/opencomputers/EnvironmentProviderBlocks.scala index 74513be7ba..aa43318302 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/EnvironmentProviderBlocks.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/EnvironmentProviderBlocks.scala @@ -10,7 +10,7 @@ import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.server.component import li.cil.oc.server.machine.Machine import net.minecraft.block.Block -import net.minecraft.item.ItemBlock +import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack /** @@ -21,7 +21,7 @@ import net.minecraft.item.ItemStack */ object EnvironmentProviderBlocks extends EnvironmentProvider { override def getEnvironment(stack: ItemStack): Class[_] = stack.getItem match { - case block: ItemBlock if block.getBlock != null => + case block: BlockItem if block.getBlock != null => if (isOneOf(block.getBlock, Constants.BlockName.Assembler)) classOf[tileentity.Assembler] else if (isOneOf(block.getBlock, Constants.BlockName.CaseTier1, Constants.BlockName.CaseTier2, Constants.BlockName.CaseTier3, Constants.BlockName.CaseCreative, Constants.BlockName.Microcontroller)) classOf[Machine] else if (isOneOf(block.getBlock, Constants.BlockName.HologramTier1, Constants.BlockName.HologramTier2)) classOf[tileentity.Hologram] diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala index d5216aec00..e8265b661a 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala @@ -2,16 +2,16 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.DatabaseInventory -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack object InventoryProviderDatabase extends InventoryProvider { - override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverUpgradeDatabase.worksWith(stack) + override def worksWith(stack: ItemStack, player: PlayerEntity): Boolean = DriverUpgradeDatabase.worksWith(stack) - override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new DatabaseInventory { + override def getInventory(stack: ItemStack, player: PlayerEntity): IInventory = new DatabaseInventory { override def container: ItemStack = stack - override def isUsableByPlayer(player: EntityPlayer): Boolean = player == player + override def stillValid(player: PlayerEntity): Boolean = player == player } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala index d7e30f1cd0..46bac1b1b8 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala @@ -2,14 +2,14 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.ServerInventory -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack object InventoryProviderServer extends InventoryProvider { - override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverServer.worksWith(stack) + override def worksWith(stack: ItemStack, player: PlayerEntity): Boolean = DriverServer.worksWith(stack) - override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new ServerInventory { + override def getInventory(stack: ItemStack, player: PlayerEntity): IInventory = new ServerInventory { override def container: ItemStack = stack } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala b/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala index 0b63a40f97..4cb60a563b 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/Item.scala @@ -10,7 +10,7 @@ import li.cil.oc.api.internal import li.cil.oc.common.Tier import li.cil.oc.server.driver.Registry import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.annotation.tailrec @@ -18,13 +18,13 @@ trait Item extends DriverItem { def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]): Boolean = worksWith(stack) && !Registry.blacklist.exists { case (blacklistedStack, blacklistedHost) => - stack.isItemEqual(blacklistedStack) && + stack.sameItem(blacklistedStack) && blacklistedHost.exists(_.isAssignableFrom(host)) } override def tier(stack: ItemStack) = Tier.One - override def dataTag(stack: ItemStack): NBTTagCompound = Item.dataTag(stack) + override def dataTag(stack: ItemStack): CompoundNBT = Item.dataTag(stack) protected def isOneOf(stack: ItemStack, items: api.detail.ItemInfo*): Boolean = items.filter(_ != null).contains(api.Items.get(stack)) @@ -46,34 +46,31 @@ trait Item extends DriverItem { } object Item { - def dataTag(stack: ItemStack): NBTTagCompound = { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) + def dataTag(stack: ItemStack): CompoundNBT = { + val nbt = stack.getOrCreateTag + if (!nbt.contains(Settings.namespace + "data")) { + nbt.put(Settings.namespace + "data", new CompoundNBT()) } - val nbt = stack.getTagCompound - if (!nbt.hasKey(Settings.namespace + "data")) { - nbt.setTag(Settings.namespace + "data", new NBTTagCompound()) - } - nbt.getCompoundTag(Settings.namespace + "data") + nbt.getCompound(Settings.namespace + "data") } @tailrec - private def getTag(tagCompound: NBTTagCompound, keys: Array[String]): Option[NBTTagCompound] = { + private def getTag(tagCompound: CompoundNBT, keys: Array[String]): Option[CompoundNBT] = { if (keys.length == 0) Option(tagCompound) - else if (!tagCompound.hasKey(keys(0))) None - else getTag(tagCompound.getCompoundTag(keys(0)), keys.drop(1)) + else if (!tagCompound.contains(keys(0))) None + else getTag(tagCompound.getCompound(keys(0)), keys.drop(1)) } - private def getTag(stack: ItemStack, keys: Array[String]): Option[NBTTagCompound] = { + private def getTag(stack: ItemStack, keys: Array[String]): Option[CompoundNBT] = { if (stack == null || stack.getCount == 0 || stack == ItemStack.EMPTY) None - else if (!stack.hasTagCompound) None - else getTag(stack.getTagCompound, keys) + else if (!stack.hasTag) None + else getTag(stack.getTag, keys) } def address(stack: ItemStack): Option[String] = { val addressKey = "address" getTag(stack, Array(Settings.namespace + "data", "node")) match { - case Some(tag) if tag.hasKey(addressKey) => Option(tag.getString(addressKey)) + case Some(tag) if tag.contains(addressKey) => Option(tag.getString(addressKey)) case _ => None } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index a36e0d2d63..fef966011c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -42,12 +42,13 @@ import li.cil.oc.server.machine.luac.NativeLua53Architecture import li.cil.oc.server.network.Waypoints import li.cil.oc.server.network.WirelessNetwork import li.cil.oc.util.Color -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import net.minecraftforge.common.ForgeChunkManager import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.common.world.ForgeChunkManager object ModOpenComputers extends ModProxy { override def getMod = Mods.OpenComputers @@ -91,7 +92,7 @@ object ModOpenComputers extends ModProxy { api.IMC.registerProgramDiskLabel("opl-flash", "openloader", "Lua 5.2", "Lua 5.3", "LuaJ") api.IMC.registerProgramDiskLabel("oppm", "oppm", "Lua 5.2", "Lua 5.3", "LuaJ") - ForgeChunkManager.setForcedChunkLoadingCallback(OpenComputers, ChunkloaderUpgradeHandler) + ForgeChunkManager.setForcedChunkLoadingCallback(OpenComputers.ID, ChunkloaderUpgradeHandler) MinecraftForge.EVENT_BUS.register(EventHandler) MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Common) @@ -100,7 +101,6 @@ object ModOpenComputers extends ModProxy { MinecraftForge.EVENT_BUS.register(Analyzer) MinecraftForge.EVENT_BUS.register(AngelUpgradeHandler) - MinecraftForge.EVENT_BUS.register(BlockChangeHandler) MinecraftForge.EVENT_BUS.register(ChunkloaderUpgradeHandler) MinecraftForge.EVENT_BUS.register(EventHandler) MinecraftForge.EVENT_BUS.register(ExperienceUpgradeHandler) @@ -337,9 +337,9 @@ object ModOpenComputers extends ModProxy { api.Nanomachines.addProvider(MagnetProvider) } - def useWrench(player: EntityPlayer, pos: BlockPos, changeDurability: Boolean): Boolean = { - player.getHeldItemMainhand.getItem match { - case wrench: Wrench => wrench.useWrenchOnBlock(player, player.getEntityWorld, pos, !changeDurability) + def useWrench(player: PlayerEntity, pos: BlockPos, changeDurability: Boolean): Boolean = { + player.getItemInHand(Hand.MAIN_HAND).getItem match { + case wrench: Wrench => wrench.useWrenchOnBlock(player, player.level, pos, !changeDurability) case _ => false } } diff --git a/src/main/scala/li/cil/oc/integration/projectred/EventHandlerProjectRed.scala b/src/main/scala/li/cil/oc/integration/projectred/EventHandlerProjectRed.scala index 63ec0c5165..2707370e7e 100644 --- a/src/main/scala/li/cil/oc/integration/projectred/EventHandlerProjectRed.scala +++ b/src/main/scala/li/cil/oc/integration/projectred/EventHandlerProjectRed.scala @@ -1,13 +1,14 @@ package li.cil.oc.integration.projectred import mrtjp.projectred.api.IScrewdriver -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos object EventHandlerProjectRed { - def useWrench(player: EntityPlayer, pos: BlockPos, changeDurability: Boolean): Boolean = { - val stack = player.getHeldItemMainhand + def useWrench(player: PlayerEntity, pos: BlockPos, changeDurability: Boolean): Boolean = { + val stack = player.getItemInHand(Hand.MAIN_HAND) stack.getItem match { case wrench: IScrewdriver => if (changeDurability) { diff --git a/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala index d5a92c7cc8..714d87ced1 100644 --- a/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala +++ b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala @@ -7,7 +7,7 @@ import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider import li.cil.oc.util.BlockPosition import mrtjp.projectred.api.ProjectRedAPI -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction object ModProjectRed extends ModProxy with RedstoneProvider { override def getMod = Mods.ProjectRedTransmission @@ -19,9 +19,9 @@ object ModProjectRed extends ModProxy with RedstoneProvider { BundledRedstone.addProvider(this) } - override def computeInput(pos: BlockPosition, side: EnumFacing): Int = 0 + override def computeInput(pos: BlockPosition, side: Direction): Int = 0 - def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = { + def computeBundledInput(pos: BlockPosition, side: Direction): Array[Int] = { Option(ProjectRedAPI.transmissionAPI.getBundledInput(pos.world.get, pos.toBlockPos, side)). fold(null: Array[Int])(_.map(_ & 0xFF)) } diff --git a/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala b/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala index 74737fdd61..97f1e82cfe 100644 --- a/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala +++ b/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala @@ -1,5 +1,8 @@ package li.cil.oc.integration.tis3d +import java.util.Optional + +import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.internal.Adapter @@ -11,33 +14,35 @@ import li.cil.oc.api.network.Message import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility import li.cil.oc.util.ResultWrapper.result -import li.cil.tis3d.api.ManualAPI -import li.cil.tis3d.api.SerialAPI -import li.cil.tis3d.api.prefab.manual.ResourceContentProvider import li.cil.tis3d.api.serial.SerialInterface import li.cil.tis3d.api.serial.SerialInterfaceProvider import li.cil.tis3d.api.serial.SerialProtocolDocumentationReference -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import li.cil.tis3d.common.provider.SerialInterfaceProviders +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.registries.ForgeRegistryEntry import scala.collection.mutable -object SerialInterfaceProviderAdapter extends SerialInterfaceProvider { +object SerialInterfaceProviderAdapter extends ForgeRegistryEntry[SerialInterfaceProvider] with SerialInterfaceProvider { + setRegistryName(OpenComputers.ID, "serial_port") + def init(): Unit = { - ManualAPI.addProvider(new ResourceContentProvider(Settings.resourceDomain, "doc/tis3d/")) - SerialAPI.addProvider(this) + //ManualAPI.addProvider(new ResourceContentProvider(Settings.resourceDomain, "doc/tis3d/")) + SerialInterfaceProviders.MODULE_PROVIDER_REGISTRY.get.register(this) } - override def getDocumentationReference = new SerialProtocolDocumentationReference("OpenComputers Adapter", "protocols/opencomputersadapter.md") + override def getDocumentationReference = Optional.of(new SerialProtocolDocumentationReference(new StringTextComponent("OpenComputers Adapter"), "protocols/opencomputersadapter.md")) - override def worksWith(world: World, pos: BlockPos, side: EnumFacing): Boolean = world.getTileEntity(pos).isInstanceOf[Adapter] + override def matches(world: World, pos: BlockPos, side: Direction): Boolean = world.getBlockEntity(pos).isInstanceOf[Adapter] - override def interfaceFor(world: World, pos: BlockPos, side: EnumFacing): SerialInterface = new SerialInterfaceAdapter(world.getTileEntity(pos).asInstanceOf[Adapter]) + override def getInterface(world: World, pos: BlockPos, side: Direction): Optional[SerialInterface] = Optional.of(new SerialInterfaceAdapter(world.getBlockEntity(pos).asInstanceOf[Adapter])) - override def isValid(world: World, pos: BlockPos, side: EnumFacing, serialInterface: SerialInterface): Boolean = serialInterface match { - case adapter: SerialInterfaceAdapter => adapter.tileEntity == world.getTileEntity(pos) + override def stillValid(world: World, pos: BlockPos, side: Direction, serialInterface: SerialInterface): Boolean = serialInterface match { + case adapter: SerialInterfaceAdapter => adapter.tileEntity == world.getBlockEntity(pos) case _ => false } @@ -107,8 +112,8 @@ object SerialInterfaceProviderAdapter extends SerialInterfaceProvider { }) } - override def readFromNBT(nbt: NBTTagCompound): Unit = { - node.load(nbt) + override def readFromNBT(nbt: CompoundNBT): Unit = { + node.loadData(nbt) writeBuffer.clear() writeBuffer ++= nbt.getIntArray("writeBuffer").map(_.toShort) @@ -117,12 +122,12 @@ object SerialInterfaceProviderAdapter extends SerialInterfaceProvider { isReading = nbt.getBoolean("isReading") } - override def writeToNBT(nbt: NBTTagCompound): Unit = { - node.save(nbt) + override def writeToNBT(nbt: CompoundNBT): Unit = { + node.saveData(nbt) - nbt.setIntArray("writeBuffer", writeBuffer.toArray.map(_.toInt)) - nbt.setIntArray("readBuffer", readBuffer.toArray.map(_.toInt)) - nbt.setBoolean("isReading", isReading) + nbt.putIntArray("writeBuffer", writeBuffer.toArray.map(_.toInt)) + nbt.putIntArray("readBuffer", readBuffer.toArray.map(_.toInt)) + nbt.putBoolean("isReading", isReading) } private def ensureConnected(): Unit = { diff --git a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala index d133bd8f5a..5724bd4c3d 100644 --- a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala +++ b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala @@ -3,7 +3,7 @@ package li.cil.oc.integration.util import li.cil.oc.integration.Mods import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.collection.mutable @@ -14,13 +14,13 @@ object BundledRedstone { def isAvailable = providers.nonEmpty - def computeInput(pos: BlockPosition, side: EnumFacing): Int = { + def computeInput(pos: BlockPosition, side: Direction): Int = { if (pos.world.get.blockExists(pos.offset(side))) providers.map(_.computeInput(pos, side)).padTo(1, 0).max else 0 } - def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = { + def computeBundledInput(pos: BlockPosition, side: Direction): Array[Int] = { if (pos.world.get.blockExists(pos.offset(side))) { val inputs = providers.map(_.computeBundledInput(pos, side)).filter(_ != null) if (inputs.isEmpty) null @@ -30,9 +30,9 @@ object BundledRedstone { } trait RedstoneProvider { - def computeInput(pos: BlockPosition, side: EnumFacing): Int + def computeInput(pos: BlockPosition, side: Direction): Int - def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] + def computeBundledInput(pos: BlockPosition, side: Direction): Array[Int] } } diff --git a/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala b/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala index 4afb8ff14f..9128f7b4be 100644 --- a/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala +++ b/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala @@ -1,19 +1,19 @@ package li.cil.oc.integration.util import net.minecraft.client.resources.I18n -import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.LivingEntity import net.minecraft.util.DamageSource import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.util.text.TranslationTextComponent class DamageSourceWithRandomCause(name: String, numCauses: Int) extends DamageSource(name) { - override def getDeathMessage(damagee: EntityLivingBase): ITextComponent = { - val damager = damagee.getAttackingEntity - val format = "death.attack." + damageType + "." + (damagee.world.rand.nextInt(numCauses) + 1) + override def getLocalizedDeathMessage(damagee: LivingEntity): ITextComponent = { + val damager = damagee.getKillCredit + val format = "death.attack." + msgId + "." + (damagee.level.random.nextInt(numCauses) + 1) val withCauseFormat = format + ".player" - if (damager != null && I18n.hasKey(withCauseFormat)) - new TextComponentTranslation(withCauseFormat, damagee.getDisplayName, damager.getDisplayName) + if (damager != null && I18n.exists(withCauseFormat)) + new TranslationTextComponent(withCauseFormat, damagee.getDisplayName, damager.getDisplayName) else - new TextComponentTranslation(format, damagee.getDisplayName) + new TranslationTextComponent(format, damagee.getDisplayName) } } diff --git a/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala b/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala index 8980cf2627..7326080ffb 100644 --- a/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala +++ b/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala @@ -2,14 +2,14 @@ package li.cil.oc.integration.util import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.client.gui.screen.inventory.ContainerScreen import scala.collection.mutable object ItemSearch { val focusedInput = mutable.Set.empty[() => Boolean] - val stackFocusing = mutable.Set.empty[(GuiContainer, Int, Int) => StackOption] + val stackFocusing = mutable.Set.empty[(ContainerScreen[_], Int, Int) => StackOption] def isInputFocused: Boolean = { for (f <- focusedInput) { @@ -18,7 +18,7 @@ object ItemSearch { false } - def hoveredStack(container: GuiContainer, mouseX: Int, mouseY: Int): StackOption = { + def hoveredStack(container: ContainerScreen[_], mouseX: Int, mouseY: Int): StackOption = { for (f <- stackFocusing) { f(container, mouseX, mouseY).foreach(stack => return StackOption(stack)) } diff --git a/src/main/scala/li/cil/oc/integration/util/Wrench.scala b/src/main/scala/li/cil/oc/integration/util/Wrench.scala index d3f50a107c..b0adc1b285 100644 --- a/src/main/scala/li/cil/oc/integration/util/Wrench.scala +++ b/src/main/scala/li/cil/oc/integration/util/Wrench.scala @@ -3,8 +3,9 @@ package li.cil.oc.integration.util import java.lang.reflect.Method import li.cil.oc.common.IMC -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import scala.collection.mutable @@ -19,9 +20,9 @@ object Wrench { def isWrench(stack: ItemStack): Boolean = !stack.isEmpty && checks.exists(IMC.tryInvokeStatic(_, stack)(false)) - def holdsApplicableWrench(player: EntityPlayer, position: BlockPos): Boolean = - !player.getHeldItemMainhand.isEmpty && usages.exists(IMC.tryInvokeStatic(_, player, position, java.lang.Boolean.FALSE)(false)) + def holdsApplicableWrench(player: PlayerEntity, position: BlockPos): Boolean = + !player.getItemInHand(Hand.MAIN_HAND).isEmpty && usages.exists(IMC.tryInvokeStatic(_, player, position, java.lang.Boolean.FALSE)(false)) - def wrenchUsed(player: EntityPlayer, position: BlockPos): Unit = - if (!player.getHeldItemMainhand.isEmpty) usages.foreach(IMC.tryInvokeStaticVoid(_, player, position, java.lang.Boolean.TRUE)) + def wrenchUsed(player: PlayerEntity, position: BlockPos): Unit = + if (!player.getItemInHand(Hand.MAIN_HAND).isEmpty) usages.foreach(IMC.tryInvokeStaticVoid(_, player, position, java.lang.Boolean.TRUE)) } diff --git a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala index bd2b7bc252..9934cd430a 100644 --- a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala +++ b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala @@ -14,47 +14,50 @@ import li.cil.oc.common.tileentity.traits.NotAnalyzable import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption.SomeStack import mcp.mobius.waila.api._ -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagString +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.StringNBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.BlockPos +import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.common.util.Constants.NBT -object BlockDataProvider extends IWailaDataProvider { - val ConfigAddress = "oc.address" - val ConfigEnergy = "oc.energy" - val ConfigComponentName = "oc.componentName" +@WailaPlugin +object BlockDataProvider extends IWailaPlugin with IServerDataProvider[TileEntity] with IComponentProvider { + val ConfigAddress = new ResourceLocation(OpenComputers.ID, "oc.address") + val ConfigEnergy = new ResourceLocation(OpenComputers.ID, "oc.energy") + val ConfigComponentName = new ResourceLocation(OpenComputers.ID, "oc.componentName") - def init(registrar: IWailaRegistrar) { - registrar.registerBodyProvider(BlockDataProvider, classOf[SimpleBlock]) + def register(registrar: IRegistrar) { + registrar.registerComponentProvider(this, TooltipPosition.BODY, classOf[SimpleBlock]) - registrar.registerNBTProvider(this, classOf[li.cil.oc.api.network.Environment]) - registrar.registerNBTProvider(this, classOf[li.cil.oc.api.network.SidedEnvironment]) + registrar.registerBlockDataProvider(this, classOf[li.cil.oc.api.network.Environment]) + registrar.registerBlockDataProvider(this, classOf[li.cil.oc.api.network.SidedEnvironment]) - registrar.addConfig(OpenComputers.Name, ConfigAddress) - registrar.addConfig(OpenComputers.Name, ConfigEnergy) - registrar.addConfig(OpenComputers.Name, ConfigComponentName) + registrar.addConfig(ConfigAddress, true) + registrar.addConfig(ConfigEnergy, true) + registrar.addConfig(ConfigComponentName, true) } - override def getNBTData(player: EntityPlayerMP, tileEntity: TileEntity, tag: NBTTagCompound, world: World, pos: BlockPos): NBTTagCompound = { - def writeNode(node: Node, tag: NBTTagCompound) = { + override def appendServerData(tag: CompoundNBT, player: ServerPlayerEntity, world: World, tileEntity: TileEntity): Unit = { + def writeNode(node: Node, tag: CompoundNBT) = { if (node != null && node.reachability != Visibility.None && !tileEntity.isInstanceOf[NotAnalyzable]) { if (node.address != null) { - tag.setString("address", node.address) + tag.putString("address", node.address) } node match { case connector: Connector => - tag.setInteger("buffer", connector.localBuffer.toInt) - tag.setInteger("bufferSize", connector.localBufferSize.toInt) + tag.putInt("buffer", connector.localBuffer.toInt) + tag.putInt("bufferSize", connector.localBufferSize.toInt) case _ => } node match { case component: Component => - tag.setString("componentName", component.name) + tag.putString("componentName", component.name) case _ => } } @@ -63,9 +66,9 @@ object BlockDataProvider extends IWailaDataProvider { tileEntity match { case te: li.cil.oc.api.network.SidedEnvironment => - tag.setNewTagList("nodes", EnumFacing.values. + tag.setNewTagList("nodes", Direction.values. map(te.sidedNode). - map(writeNode(_, new NBTTagCompound()))) + map(writeNode(_, new CompoundNBT()))) case te: li.cil.oc.api.network.Environment => writeNode(te.node, tag) case _ => @@ -73,14 +76,14 @@ object BlockDataProvider extends IWailaDataProvider { // Override sided info (show info on all sides). def ignoreSidedness(node: Node): Unit = { - tag.removeTag("nodes") - val nodeTag = writeNode(node, new NBTTagCompound()) - tag.setNewTagList("nodes", EnumFacing.values.map(_ => nodeTag)) + tag.remove("nodes") + val nodeTag = writeNode(node, new CompoundNBT()) + tag.setNewTagList("nodes", Direction.values.map(_ => nodeTag)) } tileEntity match { case te: tileentity.Relay => - tag.setDouble("signalStrength", te.strength) + tag.putDouble("signalStrength", te.strength) // this might be called by waila before the components have finished loading, thus the addresses may be null tag.setNewTagList("addresses", stringIterableToNbt(te.componentNodes.collect { case n if n.address != null => n @@ -88,110 +91,102 @@ object BlockDataProvider extends IWailaDataProvider { case te: tileentity.Assembler => ignoreSidedness(te.node) if (te.isAssembling) { - tag.setDouble("progress", te.progress) - tag.setInteger("timeRemaining", te.timeRemaining) + tag.putDouble("progress", te.progress) + tag.putInt("timeRemaining", te.timeRemaining) te.output match { - case SomeStack(output) => tag.setString("output", output.getUnlocalizedName) + case SomeStack(output) => tag.putString("output", output.getDescriptionId) case _ => // Huh... } } case te: tileentity.Charger => - tag.setDouble("chargeSpeed", te.chargeSpeed) + tag.putDouble("chargeSpeed", te.chargeSpeed) case te: tileentity.DiskDrive => // Override address with file system address. - tag.removeTag("address") + tag.remove("address") te.filesystemNode.foreach(writeNode(_, tag)) case te: tileentity.Hologram => ignoreSidedness(te.node) case te: tileentity.Keyboard => ignoreSidedness(te.node) case te: tileentity.Screen => ignoreSidedness(te.node) case te: tileentity.Rack => - tag.removeTag("nodes") + tag.remove("nodes") //tag.setNewTagList("servers", stringIterableToNbt(te.servers.map(_.fold("")(_.node.address)))) - //tag.setByteArray("sideIndexes", EnumFacing.values.map(side => te.sides.indexWhere(_.contains(side))).map(_.toByte)) + //tag.putByteArray("sideIndexes", Direction.values.map(side => te.sides.indexWhere(_.contains(side))).map(_.toByte)) // TODO case _ => } - - tag } - override def getWailaBody(stack: ItemStack, tooltip: util.List[String], accessor: IWailaDataAccessor, config: IWailaConfigHandler): util.List[String] = { - val tag = accessor.getNBTData - if (tag == null || tag.hasNoTags) return tooltip + override def appendBody(tooltip: util.List[ITextComponent], accessor: IDataAccessor, config: IPluginConfig): Unit = { + val tag = accessor.getServerData + if (tag == null || tag.isEmpty) return accessor.getTileEntity match { case _: tileentity.Relay => - val address = tag.getTagList("addresses", NBT.TAG_STRING).getStringTagAt(accessor.getSide.ordinal) + val address = tag.getList("addresses", NBT.TAG_STRING).getString(accessor.getSide.ordinal) val signalStrength = tag.getDouble("signalStrength") - if (config.getConfig(ConfigAddress)) { - tooltip.add(Localization.Analyzer.Address(address).getUnformattedText) + if (config.get(ConfigAddress)) { + tooltip.add(Localization.Analyzer.Address(address)) } - tooltip.add(Localization.Analyzer.WirelessStrength(signalStrength).getUnformattedText) + tooltip.add(Localization.Analyzer.WirelessStrength(signalStrength)) case _: tileentity.Assembler => - if (tag.hasKey("progress")) { + if (tag.contains("progress")) { val progress = tag.getDouble("progress") - val timeRemaining = formatTime(tag.getInteger("timeRemaining")) - tooltip.add(Localization.Assembler.Progress(progress, timeRemaining)) - if (tag.hasKey("output")) { + val timeRemaining = formatTime(tag.getInt("timeRemaining")) + tooltip.add(new StringTextComponent(Localization.Assembler.Progress(progress, timeRemaining))) + if (tag.contains("output")) { val output = tag.getString("output") - tooltip.add("Building: " + Localization.localizeImmediately(output)) + tooltip.add(new StringTextComponent("Building: " + Localization.localizeImmediately(output))) } } case _: tileentity.Charger => val chargeSpeed = tag.getDouble("chargeSpeed") - tooltip.add(Localization.Analyzer.ChargerSpeed(chargeSpeed).getUnformattedText) + tooltip.add(Localization.Analyzer.ChargerSpeed(chargeSpeed)) case te: tileentity.Rack => -// val servers = tag.getTagList("servers", NBT.TAG_STRING).map((t: NBTTagString) => t.getString).toArray -// val hitPos = accessor.getMOP.hitVec +// val servers = tag.getList("servers", NBT.TAG_STRING).map((t: StringNBT) => t.getAsString).toArray +// val hitPos = accessor.getMOP.getLocation // val address = te.slotAt(accessor.getSide, (hitPos.xCoord - accessor.getMOP.getBlockPos.getX).toFloat, (hitPos.yCoord - accessor.getMOP.getBlockPos.getY).toFloat, (hitPos.zCoord - accessor.getMOP.getBlockPos.getZ).toFloat) match { // case Some(slot) => servers(slot) // case _ => tag.getByteArray("sideIndexes").map(index => if (index >= 0) servers(index) else "").apply(te.toLocal(accessor.getSide).ordinal) // } -// if (address.nonEmpty && config.getConfig(ConfigAddress)) { -// tooltip.add(Localization.Analyzer.Address(address).getUnformattedText) +// if (address.nonEmpty && config.get(ConfigAddress)) { +// tooltip.add(Localization.Analyzer.Address(address)) // } // TODO case _ => } - def readNode(tag: NBTTagCompound) = { - if (config.getConfig(ConfigAddress) && tag.hasKey("address")) { + def readNode(tag: CompoundNBT) = { + if (config.get(ConfigAddress) && tag.contains("address")) { val address = tag.getString("address") if (address.nonEmpty) { - tooltip.add(Localization.Analyzer.Address(address).getUnformattedText) + tooltip.add(Localization.Analyzer.Address(address)) } } - if (config.getConfig(ConfigEnergy) && tag.hasKey("buffer") && tag.hasKey("bufferSize")) { - val buffer = tag.getInteger("buffer") - val bufferSize = tag.getInteger("bufferSize") + if (config.get(ConfigEnergy) && tag.contains("buffer") && tag.contains("bufferSize")) { + val buffer = tag.getInt("buffer") + val bufferSize = tag.getInt("bufferSize") if (bufferSize > 0) { - tooltip.add(Localization.Analyzer.StoredEnergy(s"$buffer/$bufferSize").getUnformattedText) + tooltip.add(Localization.Analyzer.StoredEnergy(s"$buffer/$bufferSize")) } } - if (config.getConfig(ConfigComponentName) && tag.hasKey("componentName")) { + if (config.get(ConfigComponentName) && tag.contains("componentName")) { val componentName = tag.getString("componentName") if (componentName.nonEmpty) { - tooltip.add(Localization.Analyzer.ComponentName(componentName).getUnformattedText) + tooltip.add(Localization.Analyzer.ComponentName(componentName)) } } } accessor.getTileEntity match { case te: li.cil.oc.api.network.SidedEnvironment => - readNode(tag.getTagList("nodes", NBT.TAG_COMPOUND).getCompoundTagAt(accessor.getSide.ordinal)) + readNode(tag.getList("nodes", NBT.TAG_COMPOUND).getCompound(accessor.getSide.ordinal)) case te: li.cil.oc.api.network.Environment => readNode(tag) case _ => } - - tooltip } - override def getWailaStack(accessor: IWailaDataAccessor, config: IWailaConfigHandler) = accessor.getStack - - override def getWailaHead(stack: ItemStack, tooltip: util.List[String], accessor: IWailaDataAccessor, config: IWailaConfigHandler): util.List[String] = tooltip - - override def getWailaTail(stack: ItemStack, tooltip: util.List[String], accessor: IWailaDataAccessor, config: IWailaConfigHandler): util.List[String] = tooltip + override def getStack(accessor: IDataAccessor, config: IPluginConfig) = accessor.getStack private def formatTime(seconds: Int) = { // Assembly times should not / rarely exceed one hour, so this is good enough. diff --git a/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala b/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala index cf7dd63183..5a5d77766c 100644 --- a/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala +++ b/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala @@ -2,12 +2,10 @@ package li.cil.oc.integration.waila import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods -import net.minecraftforge.fml.common.event.FMLInterModComms +import net.minecraftforge.fml.InterModComms object ModWaila extends ModProxy { override def getMod = Mods.Waila - override def initialize() { - FMLInterModComms.sendMessage(Mods.IDs.Waila, "register", "li.cil.oc.integration.waila.BlockDataProvider.init") - } + override def initialize() = Unit } diff --git a/src/main/scala/li/cil/oc/server/ComponentTracker.scala b/src/main/scala/li/cil/oc/server/ComponentTracker.scala index 0071dace49..a833ff2f0b 100644 --- a/src/main/scala/li/cil/oc/server/ComponentTracker.scala +++ b/src/main/scala/li/cil/oc/server/ComponentTracker.scala @@ -4,5 +4,5 @@ import li.cil.oc.common import net.minecraft.world.World object ComponentTracker extends common.ComponentTracker { - override protected def clear(world: World) = if (!world.isRemote) super.clear(world) + override protected def clear(world: World) = if (!world.isClientSide) super.clear(world) } diff --git a/src/main/scala/li/cil/oc/server/GuiHandler.scala b/src/main/scala/li/cil/oc/server/GuiHandler.scala index e504415be0..12aa244a91 100644 --- a/src/main/scala/li/cil/oc/server/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/server/GuiHandler.scala @@ -1,9 +1,8 @@ package li.cil.oc.server import li.cil.oc.common.{GuiHandler => CommonGuiHandler} -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.world.World object GuiHandler extends CommonGuiHandler { - override def getClientGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int) = null } diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index df6c97bf48..ad8ae222fa 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -1,5 +1,7 @@ package li.cil.oc.server +import java.io.InputStream + import li.cil.oc.Localization import li.cil.oc.api import li.cil.oc.api.internal.Server @@ -15,22 +17,21 @@ import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity.traits.Computer import li.cil.oc.common.{PacketHandler => CommonPacketHandler} -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.network.NetHandlerPlayServer -import net.minecraft.util.EnumHand -import net.minecraftforge.common.DimensionManager -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.network.FMLNetworkEvent.ServerCustomPacketEvent +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Hand +import net.minecraft.util.RegistryKey +import net.minecraft.util.ResourceLocation +import net.minecraft.util.Util +import net.minecraft.util.registry.Registry +import net.minecraft.world.World +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.server.ServerLifecycleHooks object PacketHandler extends CommonPacketHandler { - @SubscribeEvent - def onPacket(e: ServerCustomPacketEvent): Unit = - onPacketData(e.getManager.getNetHandler, e.getPacket.payload, e.getHandler.asInstanceOf[NetHandlerPlayServer].player) - - override protected def world(player: EntityPlayer, dimension: Int) = - Option(DimensionManager.getWorld(dimension)) + override protected def world(player: PlayerEntity, dimension: ResourceLocation): Option[World] = + Option(ServerLifecycleHooks.getCurrentServer.getLevel(RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension))) override def dispatch(p: PacketParser) { p.packetType match { @@ -59,11 +60,11 @@ object PacketHandler extends CommonPacketHandler { } def onComputerPower(p: PacketParser): Unit = { - val entity = p.readTileEntity[Computer]() + val entity = p.readBlockEntity[Computer]() val setPower = p.readBoolean() entity match { case Some(t) => p.player match { - case player: EntityPlayerMP => trySetComputerPower(t.machine, setPower, player) + case player: ServerPlayerEntity => trySetComputerPower(t.machine, setPower, player) case _ => } case _ => // Invalid packet. @@ -71,7 +72,7 @@ object PacketHandler extends CommonPacketHandler { } def onServerPower(p: PacketParser): Unit = { - val entity = p.readTileEntity[Rack]() + val entity = p.readBlockEntity[Rack]() val index = p.readInt() val setPower = p.readBoolean() entity match { @@ -79,7 +80,7 @@ object PacketHandler extends CommonPacketHandler { val mountableIndex = index t.getMountable(mountableIndex) match { case server: Server => p.player match { - case player: EntityPlayerMP => trySetComputerPower(server.machine, setPower, player) + case player: ServerPlayerEntity => trySetComputerPower(server.machine, setPower, player) case _ => // Invalid packet. } case _ => // Invalid packet. @@ -92,15 +93,15 @@ object PacketHandler extends CommonPacketHandler { def onCopyToAnalyzer(p: PacketParser) { val text = p.readUTF() val line = p.readInt() - ComponentTracker.get(p.player.world, text) match { - case Some(buffer: TextBuffer) => buffer.copyToAnalyzer(line, p.player.asInstanceOf[EntityPlayer]) + ComponentTracker.get(p.player.level, text) match { + case Some(buffer: TextBuffer) => buffer.copyToAnalyzer(line, p.player.asInstanceOf[PlayerEntity]) case _ => // Invalid Packet } } def onDriveLock(p: PacketParser): Unit = p.player match { - case player: EntityPlayerMP => { - val heldItem = player.getHeldItem(EnumHand.MAIN_HAND) + case player: ServerPlayerEntity => { + val heldItem = player.getItemInHand(Hand.MAIN_HAND) Delegator.subItem(heldItem) match { case Some(drive: FileSystemLike) => DriveData.lock(heldItem, player) case _ => // Invalid packet @@ -112,8 +113,8 @@ object PacketHandler extends CommonPacketHandler { def onDriveMode(p: PacketParser): Unit = { val unmanaged = p.readBoolean() p.player match { - case player: EntityPlayerMP => - val heldItem = player.getHeldItem(EnumHand.MAIN_HAND) + case player: ServerPlayerEntity => + val heldItem = player.getItemInHand(Hand.MAIN_HAND) Delegator.subItem(heldItem) match { case Some(drive: FileSystemLike) => DriveData.setUnmanaged(heldItem, unmanaged) case _ => // Invalid packet. @@ -127,7 +128,7 @@ object PacketHandler extends CommonPacketHandler { val power = p.readBoolean() entity match { case Some(drone) => p.player match { - case player: EntityPlayerMP => + case player: ServerPlayerEntity => if (power) { drone.preparePowerUp() } @@ -138,13 +139,13 @@ object PacketHandler extends CommonPacketHandler { } } - private def trySetComputerPower(computer: Machine, value: Boolean, player: EntityPlayerMP) { - if (computer.canInteract(player.getName)) { + private def trySetComputerPower(computer: Machine, value: Boolean, player: ServerPlayerEntity) { + if (computer.canInteract(player.getName.getString)) { if (value) { if (!computer.isPaused) { computer.start() computer.lastError match { - case message if message != null => player.sendMessage(Localization.Analyzer.LastError(message)) + case message if message != null => player.sendMessage(Localization.Analyzer.LastError(message), Util.NIL_UUID) case _ => } } @@ -157,8 +158,8 @@ object PacketHandler extends CommonPacketHandler { val address = p.readUTF() val key = p.readChar() val code = p.readInt() - ComponentTracker.get(p.player.world, address) match { - case Some(buffer: api.internal.TextBuffer) => buffer.keyDown(key, code, p.player.asInstanceOf[EntityPlayer]) + ComponentTracker.get(p.player.level, address) match { + case Some(buffer: api.internal.TextBuffer) => buffer.keyDown(key, code, p.player.asInstanceOf[PlayerEntity]) case _ => // Invalid Packet } } @@ -167,8 +168,8 @@ object PacketHandler extends CommonPacketHandler { val address = p.readUTF() val key = p.readChar() val code = p.readInt() - ComponentTracker.get(p.player.world, address) match { - case Some(buffer: api.internal.TextBuffer) => buffer.keyUp(key, code, p.player.asInstanceOf[EntityPlayer]) + ComponentTracker.get(p.player.level, address) match { + case Some(buffer: api.internal.TextBuffer) => buffer.keyUp(key, code, p.player.asInstanceOf[PlayerEntity]) case _ => // Invalid Packet } } @@ -176,8 +177,8 @@ object PacketHandler extends CommonPacketHandler { def onClipboard(p: PacketParser): Unit = { val address = p.readUTF() val copy = p.readUTF() - ComponentTracker.get(p.player.world, address) match { - case Some(buffer: api.internal.TextBuffer) => buffer.clipboard(copy, p.player.asInstanceOf[EntityPlayer]) + ComponentTracker.get(p.player.level, address) match { + case Some(buffer: api.internal.TextBuffer) => buffer.clipboard(copy, p.player.asInstanceOf[PlayerEntity]) case _ => // Invalid Packet } } @@ -188,9 +189,9 @@ object PacketHandler extends CommonPacketHandler { val y = p.readFloat() val dragging = p.readBoolean() val button = p.readByte() - ComponentTracker.get(p.player.world, address) match { + ComponentTracker.get(p.player.level, address) match { case Some(buffer: api.internal.TextBuffer) => - val player = p.player.asInstanceOf[EntityPlayer] + val player = p.player.asInstanceOf[PlayerEntity] if (dragging) buffer.mouseDrag(x, y, button, player) else buffer.mouseDown(x, y, button, player) case _ => // Invalid Packet @@ -202,9 +203,9 @@ object PacketHandler extends CommonPacketHandler { val x = p.readFloat() val y = p.readFloat() val button = p.readByte() - ComponentTracker.get(p.player.world, address) match { + ComponentTracker.get(p.player.level, address) match { case Some(buffer: api.internal.TextBuffer) => - val player = p.player.asInstanceOf[EntityPlayer] + val player = p.player.asInstanceOf[PlayerEntity] buffer.mouseUp(x, y, button, player) case _ => // Invalid Packet } @@ -215,9 +216,9 @@ object PacketHandler extends CommonPacketHandler { val x = p.readFloat() val y = p.readFloat() val button = p.readByte() - ComponentTracker.get(p.player.world, address) match { + ComponentTracker.get(p.player.level, address) match { case Some(buffer: api.internal.TextBuffer) => - val player = p.player.asInstanceOf[EntityPlayer] + val player = p.player.asInstanceOf[PlayerEntity] buffer.mouseScroll(x, y, button, player) case _ => // Invalid Packet } @@ -226,28 +227,28 @@ object PacketHandler extends CommonPacketHandler { def onPetVisibility(p: PacketParser) { val value = p.readBoolean() p.player match { - case player: EntityPlayerMP => + case player: ServerPlayerEntity => if (if (value) { - PetVisibility.hidden.remove(player.getName) + PetVisibility.hidden.remove(player.getName.getString) } else { - PetVisibility.hidden.add(player.getName) + PetVisibility.hidden.add(player.getName.getString) }) { // Something changed. - PacketSender.sendPetVisibility(Some(player.getName)) + PacketSender.sendPetVisibility(Some(player.getName.getString)) } case _ => // Invalid packet. } } def onRackMountableMapping(p: PacketParser): Unit = { - val entity = p.readTileEntity[Rack]() + val entity = p.readBlockEntity[Rack]() val mountableIndex = p.readInt() val nodeIndex = p.readInt() val side = p.readDirection() entity match { case Some(t) => p.player match { - case player: EntityPlayerMP if t.isUsableByPlayer(player) => + case player: ServerPlayerEntity if t.stillValid(player) => t.connect(mountableIndex, nodeIndex, side) case _ => } @@ -256,11 +257,11 @@ object PacketHandler extends CommonPacketHandler { } def onRackRelayState(p: PacketParser): Unit = { - val entity = p.readTileEntity[Rack]() + val entity = p.readBlockEntity[Rack]() val enabled = p.readBoolean() entity match { case Some(t) => p.player match { - case player: EntityPlayerMP if t.isUsableByPlayer(player) => + case player: ServerPlayerEntity if t.stillValid(player) => t.isRelayEnabled = enabled case _ => } @@ -269,11 +270,11 @@ object PacketHandler extends CommonPacketHandler { } def onRobotAssemblerStart(p: PacketParser): Unit = { - val entity = p.readTileEntity[Assembler]() + val entity = p.readBlockEntity[Assembler]() entity match { case Some(assembler) => if (assembler.start(p.player match { - case player: EntityPlayerMP => player.capabilities.isCreativeMode + case player: ServerPlayerEntity => player.isCreative case _ => false })) assembler.output.foreach(stack => Achievement.onAssemble(stack, p.player)) case _ => // Invalid packet. @@ -281,14 +282,14 @@ object PacketHandler extends CommonPacketHandler { } def onRobotStateRequest(p: PacketParser): Unit = { - p.readTileEntity[RobotProxy]() match { - case Some(proxy) => proxy.world.notifyBlockUpdate(proxy.getPos, proxy.world.getBlockState(proxy.getPos), proxy.world.getBlockState(proxy.getPos), 3) + p.readBlockEntity[RobotProxy]() match { + case Some(proxy) => proxy.world.sendBlockUpdated(proxy.getBlockPos, proxy.world.getBlockState(proxy.getBlockPos), proxy.world.getBlockState(proxy.getBlockPos), 3) case _ => // Invalid packet. } } def onMachineItemStateRequest(p: PacketParser): Unit = p.player match { - case player: EntityPlayerMP => { + case player: ServerPlayerEntity => { val stack = p.readItemStack() PacketSender.sendMachineItemState(player, stack, Tablet.get(stack, p.player).machine.isRunning) } @@ -298,19 +299,19 @@ object PacketHandler extends CommonPacketHandler { def onTextBufferInit(p: PacketParser) { val address = p.readUTF() p.player match { - case entity: EntityPlayerMP => - ComponentTracker.get(p.player.world, address) match { + case entity: ServerPlayerEntity => + ComponentTracker.get(p.player.level, address) match { case Some(buffer: TextBuffer) => if (buffer.host match { case screen: Screen if !screen.isOrigin => false case _ => true }) { - val nbt = new NBTTagCompound() - buffer.data.save(nbt) - nbt.setInteger("maxWidth", buffer.getMaximumWidth) - nbt.setInteger("maxHeight", buffer.getMaximumHeight) - nbt.setInteger("viewportWidth", buffer.getViewportWidth) - nbt.setInteger("viewportHeight", buffer.getViewportHeight) + val nbt = new CompoundNBT() + buffer.data.saveData(nbt) + nbt.putInt("maxWidth", buffer.getMaximumWidth) + nbt.putInt("maxHeight", buffer.getMaximumHeight) + nbt.putInt("viewportWidth", buffer.getViewportWidth) + nbt.putInt("viewportHeight", buffer.getViewportHeight) PacketSender.sendTextBufferInit(address, nbt, entity) } case _ => // Invalid packet. @@ -320,11 +321,11 @@ object PacketHandler extends CommonPacketHandler { } def onWaypointLabel(p: PacketParser): Unit = { - val entity = p.readTileEntity[Waypoint]() + val entity = p.readBlockEntity[Waypoint]() val label = p.readUTF().take(32) entity match { case Some(waypoint) => p.player match { - case player: EntityPlayerMP if player.getDistanceSq(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) <= 64 => + case player: ServerPlayerEntity if player.distanceToSqr(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) <= 64 => if (label != waypoint.label) { waypoint.label = label PacketSender.sendWaypointLabel(waypoint) @@ -334,4 +335,6 @@ object PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } } + + protected override def createParser(stream: InputStream, player: PlayerEntity) = new PacketParser(stream, player) } diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index dc4624e7a5..a50794d6ba 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -11,19 +11,20 @@ import li.cil.oc.common.tileentity.Waypoint import li.cil.oc.common.tileentity.traits._ import li.cil.oc.util.BlockPosition import li.cil.oc.util.PackedColor -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.inventory.Container +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.inventory.container.Container import net.minecraft.item.ItemStack import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumParticleTypes +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.IParticleData +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.registries.ForgeRegistries import scala.collection.mutable @@ -37,7 +38,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendAnalyze(address: String, player: EntityPlayerMP) { + def sendAnalyze(address: String, player: ServerPlayerEntity) { val pb = new SimplePacketBuilder(PacketType.Analyze) pb.writeUTF(address) @@ -55,7 +56,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendClientLog(line: String, player: EntityPlayerMP) { + def sendClientLog(line: String, player: ServerPlayerEntity) { val pb = new CompressedPacketBuilder(PacketType.ClientLog) pb.writeUTF(line) @@ -63,7 +64,7 @@ object PacketSender { pb.sendToPlayer(player) } - def sendClipboard(player: EntityPlayerMP, text: String) { + def sendClipboard(player: ServerPlayerEntity, text: String) { val pb = new SimplePacketBuilder(PacketType.Clipboard) pb.writeUTF(text) @@ -90,7 +91,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendMachineItemState(player: EntityPlayerMP, stack: ItemStack, isRunning: Boolean): Unit = { + def sendMachineItemState(player: ServerPlayerEntity, stack: ItemStack, isRunning: Boolean): Unit = { val pb = new SimplePacketBuilder(PacketType.MachineItemStateResponse) pb.writeItemStack(stack) @@ -109,11 +110,11 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendContainerUpdate(c: Container, nbt: NBTTagCompound, player: EntityPlayerMP): Unit = { - if (!nbt.hasNoTags) { + def sendContainerUpdate(c: Container, nbt: CompoundNBT, player: ServerPlayerEntity): Unit = { + if (!nbt.isEmpty) { val pb = new SimplePacketBuilder(PacketType.ContainerUpdate) - pb.writeByte(c.windowId.toByte) + pb.writeInt(c.containerId) pb.writeNBT(nbt) pb.sendToPlayer(player) @@ -148,13 +149,13 @@ object PacketSender { pb.writeUTF(event.getSound) CompressedStreamTools.write(event.getData, pb) - event.getTileEntity match { + event.getBlockEntity match { case t: net.minecraft.tileentity.TileEntity => pb.writeBoolean(true) pb.writeTileEntity(t) case _ => pb.writeBoolean(false) - pb.writeInt(event.getWorld.provider.getDimension) + pb.writeUTF(event.getWorld.dimension.location.toString) pb.writeDouble(event.getX) pb.writeDouble(event.getY) pb.writeDouble(event.getZ) @@ -177,13 +178,13 @@ object PacketSender { val pb = new SimplePacketBuilder(PacketType.NetworkActivity) CompressedStreamTools.write(event.getData, pb) - event.getTileEntity match { + event.getBlockEntity match { case t: net.minecraft.tileentity.TileEntity => pb.writeBoolean(true) pb.writeTileEntity(t) case _ => pb.writeBoolean(false) - pb.writeInt(event.getWorld.provider.getDimension) + pb.writeUTF(event.getWorld.dimension.location.toString) pb.writeDouble(event.getX) pb.writeDouble(event.getY) pb.writeDouble(event.getZ) @@ -309,7 +310,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendLootDisks(p: EntityPlayerMP): Unit = { + def sendLootDisks(p: ServerPlayerEntity): Unit = { // Sending as separate packets, because CompressedStreamTools hiccups otherwise... val stacks = Loot.worldDisks.map(_._1) for (stack <- stacks) { @@ -328,15 +329,15 @@ object PacketSender { } } - def sendNanomachineConfiguration(player: EntityPlayer): Unit = { + def sendNanomachineConfiguration(player: PlayerEntity): Unit = { val pb = new SimplePacketBuilder(PacketType.NanomachinesConfiguration) pb.writeEntity(player) api.Nanomachines.getController(player) match { case controller: ControllerImpl => pb.writeBoolean(true) - val nbt = new NBTTagCompound() - controller.save(nbt) + val nbt = new CompoundNBT() + controller.saveData(nbt) pb.writeNBT(nbt) case _ => pb.writeBoolean(false) @@ -345,7 +346,7 @@ object PacketSender { pb.sendToPlayersNearEntity(player) } - def sendNanomachineInputs(player: EntityPlayer): Unit = { + def sendNanomachineInputs(player: PlayerEntity): Unit = { api.Nanomachines.getController(player) match { case controller: ControllerImpl => val pb = new SimplePacketBuilder(PacketType.NanomachinesInputs) @@ -360,7 +361,7 @@ object PacketSender { } } - def sendNanomachinePower(player: EntityPlayer): Unit = { + def sendNanomachinePower(player: PlayerEntity): Unit = { api.Nanomachines.getController(player) match { case controller: ControllerImpl => val pb = new SimplePacketBuilder(PacketType.NanomachinesPower) @@ -383,22 +384,22 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendParticleEffect(position: BlockPosition, particleType: EnumParticleTypes, count: Int, velocity: Double, direction: Option[EnumFacing] = None): Unit = if (count > 0) { + def sendParticleEffect(position: BlockPosition, particleType: IParticleData, count: Int, velocity: Double, direction: Option[Direction] = None): Unit = if (count > 0) { val pb = new SimplePacketBuilder(PacketType.ParticleEffect) - pb.writeInt(position.world.get.provider.getDimension) + pb.writeUTF(position.world.get.dimension.location.toString) pb.writeInt(position.x) pb.writeInt(position.y) pb.writeInt(position.z) pb.writeDouble(velocity) pb.writeDirection(direction) - pb.writeInt(particleType.getParticleID) + pb.writeRegistryEntry(ForgeRegistries.PARTICLE_TYPES, particleType.getType()) pb.writeByte(count.toByte) pb.sendToNearbyPlayers(position.world.get, position.x, position.y, position.z, Some(32.0)) } - def sendPetVisibility(name: Option[String] = None, player: Option[EntityPlayerMP] = None) { + def sendPetVisibility(name: Option[String] = None, player: Option[ServerPlayerEntity] = None) { val pb = new SimplePacketBuilder(PacketType.PetVisibility) name match { @@ -443,10 +444,10 @@ object PacketSender { val pb = new SimplePacketBuilder(PacketType.RackInventory) pb.writeTileEntity(t) - pb.writeInt(t.getSizeInventory) - for (slot <- 0 until t.getSizeInventory) { + pb.writeInt(t.getContainerSize) + for (slot <- 0 until t.getContainerSize) { pb.writeInt(slot) - pb.writeItemStack(t.getStackInSlot(slot)) + pb.writeItemStack(t.getItem(slot)) } pb.sendToPlayersNearTileEntity(t) @@ -458,7 +459,7 @@ object PacketSender { pb.writeTileEntity(t) pb.writeInt(1) pb.writeInt(slot) - pb.writeItemStack(t.getStackInSlot(slot)) + pb.writeItemStack(t.getItem(slot)) pb.sendToPlayersNearTileEntity(t) } @@ -477,8 +478,8 @@ object PacketSender { val pb = new SimplePacketBuilder(PacketType.RaidStateChange) pb.writeTileEntity(t) - for (slot <- 0 until t.getSizeInventory) { - pb.writeBoolean(!t.getStackInSlot(slot).isEmpty) + for (slot <- 0 until t.getContainerSize) { + pb.writeBoolean(!t.getItem(slot).isEmpty) } pb.sendToPlayersNearTileEntity(t) @@ -489,7 +490,7 @@ object PacketSender { pb.writeTileEntity(t) pb.writeBoolean(t.isOutputEnabled) - for (d <- EnumFacing.values) { + for (d <- Direction.values) { pb.writeByte(t.getOutput(d)) } @@ -505,11 +506,11 @@ object PacketSender { pb.sendToPlayersNearHost(t) } - def sendRobotMove(t: tileentity.Robot, position: BlockPos, direction: EnumFacing) { + def sendRobotMove(t: tileentity.Robot, position: BlockPos, direction: Direction) { val pb = new SimplePacketBuilder(PacketType.RobotMove) // Custom pb.writeTileEntity() with fake coordinates (valid for the client). - pb.writeInt(t.world.provider.getDimension) + pb.writeUTF(t.world.dimension.location.toString) pb.writeInt(position.getX) pb.writeInt(position.getY) pb.writeInt(position.getZ) @@ -683,7 +684,7 @@ object PacketSender { pb.writeInt(fromRow) } - def appendTextBufferRamInit(pb: PacketBuilder, address: String, id: Int, nbt: NBTTagCompound): Unit = { + def appendTextBufferRamInit(pb: PacketBuilder, address: String, id: Int, nbt: CompoundNBT): Unit = { pb.writePacketType(PacketType.TextBufferRamInit) pb.writeUTF(address) @@ -742,7 +743,7 @@ object PacketSender { } } - def sendTextBufferInit(address: String, value: NBTTagCompound, player: EntityPlayerMP) { + def sendTextBufferInit(address: String, value: CompoundNBT, player: ServerPlayerEntity) { val pb = new CompressedPacketBuilder(PacketType.TextBufferInit) pb.writeUTF(address) @@ -772,7 +773,7 @@ object PacketSender { def sendSound(world: World, x: Double, y: Double, z: Double, sound: ResourceLocation, category: SoundCategory, range: Double) { val pb = new SimplePacketBuilder(PacketType.SoundEffect) - pb.writeInt(world.provider.getDimension) + pb.writeUTF(world.dimension.location.toString) pb.writeDouble(x) pb.writeDouble(y) pb.writeDouble(z) @@ -787,7 +788,7 @@ object PacketSender { val pb = new SimplePacketBuilder(PacketType.Sound) val blockPos = BlockPosition(x, y, z) - pb.writeInt(world.provider.getDimension) + pb.writeUTF(world.dimension.location.toString) pb.writeInt(blockPos.x) pb.writeInt(blockPos.y) pb.writeInt(blockPos.z) @@ -801,7 +802,7 @@ object PacketSender { val pb = new SimplePacketBuilder(PacketType.SoundPattern) val blockPos = BlockPosition(x, y, z) - pb.writeInt(world.provider.getDimension) + pb.writeUTF(world.dimension.location.toString) pb.writeInt(blockPos.x) pb.writeInt(blockPos.y) pb.writeInt(blockPos.z) diff --git a/src/main/scala/li/cil/oc/server/Proxy.scala b/src/main/scala/li/cil/oc/server/Proxy.scala deleted file mode 100644 index 692112cdd2..0000000000 --- a/src/main/scala/li/cil/oc/server/Proxy.scala +++ /dev/null @@ -1,14 +0,0 @@ -package li.cil.oc.server - -import li.cil.oc.OpenComputers -import li.cil.oc.common.{Proxy => CommonProxy} -import net.minecraftforge.fml.common.event.FMLInitializationEvent -import net.minecraftforge.fml.common.network.NetworkRegistry - -private[oc] class Proxy extends CommonProxy { - override def init(e: FMLInitializationEvent) { - super.init(e) - - NetworkRegistry.INSTANCE.registerGuiHandler(OpenComputers, GuiHandler) - } -} diff --git a/src/main/scala/li/cil/oc/server/agent/AgentContainer.scala b/src/main/scala/li/cil/oc/server/agent/AgentContainer.scala deleted file mode 100644 index 25d262f745..0000000000 --- a/src/main/scala/li/cil/oc/server/agent/AgentContainer.scala +++ /dev/null @@ -1,43 +0,0 @@ -package li.cil.oc.server.agent - -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.inventory.{Container, IContainerListener, IInventory, Slot} -import li.cil.oc.server.agent -import net.minecraft.item.ItemStack -import net.minecraft.util.NonNullList - -class AgentContainer(player: agent.Player) extends Container { - { - for (slot <- 0 until player.agent.equipmentInventory.getSizeInventory) { - this.addSlotToContainer(new Slot(player.inventory, -1 - slot, 0, 0)) - } - for (slot <- 0 until player.agent.mainInventory.getSizeInventory) { - this.addSlotToContainer(new Slot(player.inventory, slot, 0, 0)) - } - - this.addListener(new IContainerListener { - override def sendAllContents(containerToSend: Container, itemsList: NonNullList[ItemStack]): Unit = {} - - override def sendWindowProperty(containerIn: Container, varToUpdate: Int, newValue: Int): Unit = {} - - override def sendAllWindowProperties(containerIn: Container, inventory: IInventory): Unit = {} - - override def sendSlotContents(containerToSend: Container, index: Int, stack: ItemStack): Unit = { - // an action has updated the agent.inventory via slots - // thus the player.inventory is outdated in this regard - val relativeIndex: Int = containerToSend.inventorySlots.get(index).getSlotIndex - - if (relativeIndex < 0) { - if (~relativeIndex < player.inventory.armorInventory.size) { - player.inventory.armorInventory.set(~relativeIndex, stack) - } - } - else if (relativeIndex < player.inventory.mainInventory.size) { - player.inventory.mainInventory.set(relativeIndex, stack) - } - } - }) - } - - override def canInteractWith(player: EntityPlayer): Boolean = true -} diff --git a/src/main/scala/li/cil/oc/server/agent/FakeNetworkManager.scala b/src/main/scala/li/cil/oc/server/agent/FakeNetworkManager.scala index 310ed05e90..9ef8e9c1fd 100644 --- a/src/main/scala/li/cil/oc/server/agent/FakeNetworkManager.scala +++ b/src/main/scala/li/cil/oc/server/agent/FakeNetworkManager.scala @@ -2,12 +2,12 @@ package li.cil.oc.server.agent import io.netty.util.concurrent.Future import io.netty.util.concurrent.GenericFutureListener -import net.minecraft.network.EnumPacketDirection import net.minecraft.network.NetworkManager -import net.minecraft.network.Packet +import net.minecraft.network.IPacket +import net.minecraft.network.PacketDirection -object FakeNetworkManager extends NetworkManager(EnumPacketDirection.CLIENTBOUND) { - override def sendPacket(packetIn: Packet[_]): Unit = {} +object FakeNetworkManager extends NetworkManager(PacketDirection.CLIENTBOUND) { + override def send(packetIn: IPacket[_]): Unit = {} - override def sendPacket(packetIn: Packet[_], listener: GenericFutureListener[_ <: Future[_ >: Void]], listeners: GenericFutureListener[_ <: Future[_ >: Void]]*): Unit = {} + override def send(packetIn: IPacket[_], listener: GenericFutureListener[_ <: Future[_ >: Void]]): Unit = {} } diff --git a/src/main/scala/li/cil/oc/server/agent/Inventory.scala b/src/main/scala/li/cil/oc/server/agent/Inventory.scala index 6846193336..7d5d850e6c 100644 --- a/src/main/scala/li/cil/oc/server/agent/Inventory.scala +++ b/src/main/scala/li/cil/oc/server/agent/Inventory.scala @@ -1,41 +1,46 @@ package li.cil.oc.server.agent +import java.util.function.Predicate + import li.cil.oc.api.internal import li.cil.oc.util.ExtendedInventory._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.item.Item +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList -import net.minecraft.block.state.IBlockState +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT +import net.minecraft.util.DamageSource +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.block.BlockState import scala.collection.immutable -class Inventory(playerEntity: EntityPlayer, val agent: internal.Agent) extends InventoryPlayer(playerEntity) { +class Inventory(playerEntity: PlayerEntity, val agent: internal.Agent) extends PlayerInventory(playerEntity) { - private def selectedItemStack: ItemStack = agent.mainInventory.getStackInSlot(agent.selectedSlot) + private def selectedItemStack: ItemStack = agent.mainInventory.getItem(agent.selectedSlot) - private def inventorySlots: immutable.IndexedSeq[Int] = (agent.selectedSlot until getSizeInventory) ++ (0 until agent.selectedSlot) + private def inventorySlots: immutable.IndexedSeq[Int] = (agent.selectedSlot until getContainerSize) ++ (0 until agent.selectedSlot) - override def getCurrentItem: ItemStack = agent.equipmentInventory.getStackInSlot(0) + override def getSelected: ItemStack = agent.equipmentInventory.getItem(0) - override def getFirstEmptyStack: Int = { + override def getFreeSlot: Int = { if (selectedItemStack.isEmpty) agent.selectedSlot - else inventorySlots.find(getStackInSlot(_).isEmpty).getOrElse(-1) + else inventorySlots.find(getItem(_).isEmpty).getOrElse(-1) } - override def changeCurrentItem(direction: Int) {} + override def pickSlot(direction: Int) {} - override def clearMatchingItems(item: Item, damage: Int, count: Int, tag: NBTTagCompound): Int = 0 + override def clearOrCountMatchingItems(f: Predicate[ItemStack], count: Int, inv: IInventory): Int = 0 - override def decrementAnimations() { - for (slot <- 0 until getSizeInventory) { - StackOption(getStackInSlot(slot)) match { - case SomeStack(stack) => try stack.updateAnimation(agent.world, if (!agent.world.isRemote) agent.player else null, slot, slot == 0) catch { + override def tick() { + for (slot <- 0 until getContainerSize) { + StackOption(getItem(slot)) match { + case SomeStack(stack) => try stack.inventoryTick(agent.world, if (!agent.world.isClientSide) agent.player else null, slot, slot == 0) catch { case ignored: NullPointerException => // Client side item updates that need a player instance... } case _ => @@ -43,61 +48,59 @@ class Inventory(playerEntity: EntityPlayer, val agent: internal.Agent) extends I } } - override def addItemStackToInventory(stack: ItemStack): Boolean = { + override def add(stack: ItemStack): Boolean = { val slots = this.indices.drop(agent.selectedSlot) ++ this.indices.take(agent.selectedSlot) InventoryUtils.insertIntoInventory(stack, InventoryUtils.asItemHandler(this), slots = Option(slots)) } - override def canHarvestBlock(state: IBlockState): Boolean = state.getMaterial.isToolNotRequired || (!getCurrentItem.isEmpty && getCurrentItem.canHarvestBlock(state)) - - override def getDestroySpeed(state: IBlockState): Float = if (getCurrentItem.isEmpty) 1f else getCurrentItem.getDestroySpeed(state) + override def getDestroySpeed(state: BlockState): Float = if (getSelected.isEmpty) 1f else getSelected.getDestroySpeed(state) - override def writeToNBT(nbt: NBTTagList): NBTTagList = nbt + override def save(nbt: ListNBT): ListNBT = nbt - override def readFromNBT(nbt: NBTTagList) {} + override def load(nbt: ListNBT) {} - override def armorItemInSlot(slot: Int): ItemStack = ItemStack.EMPTY + override def getArmor(slot: Int): ItemStack = ItemStack.EMPTY - override def damageArmor(damage: Float) {} + override def hurtArmor(source: DamageSource, damage: Float) {} - override def dropAllItems(): Unit = {} + override def dropAll(): Unit = {} - override def hasItemStack(stack: ItemStack): Boolean = (0 until getSizeInventory).map(getStackInSlot).filter(!_.isEmpty).exists(_.isItemEqual(stack)) + override def contains(stack: ItemStack): Boolean = (0 until getContainerSize).map(getItem).filter(!_.isEmpty).exists(_.sameItem(stack)) - override def copyInventory(from: InventoryPlayer) {} + override def replaceWith(from: PlayerInventory) {} // IInventory - override def getSizeInventory: Int = agent.mainInventory.getSizeInventory + override def getContainerSize: Int = agent.mainInventory.getContainerSize - override def getStackInSlot(slot: Int): ItemStack = - if (slot < 0) agent.equipmentInventory.getStackInSlot(~slot) - else agent.mainInventory.getStackInSlot(slot) + override def getItem(slot: Int): ItemStack = + if (slot < 0) agent.equipmentInventory.getItem(~slot) + else agent.mainInventory.getItem(slot) - override def decrStackSize(slot: Int, amount: Int): ItemStack = { - if (slot < 0) agent.equipmentInventory.decrStackSize(~slot, amount) - else agent.mainInventory.decrStackSize(slot, amount) + override def removeItem(slot: Int, amount: Int): ItemStack = { + if (slot < 0) agent.equipmentInventory.removeItem(~slot, amount) + else agent.mainInventory.removeItem(slot, amount) } - override def removeStackFromSlot(slot: Int): ItemStack = { - if (slot < 0) agent.equipmentInventory.removeStackFromSlot(~slot) - else agent.mainInventory.removeStackFromSlot(slot) + override def removeItemNoUpdate(slot: Int): ItemStack = { + if (slot < 0) agent.equipmentInventory.removeItemNoUpdate(~slot) + else agent.mainInventory.removeItemNoUpdate(slot) } - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { - if (slot < 0) agent.equipmentInventory.setInventorySlotContents(~slot, stack) - else agent.mainInventory.setInventorySlotContents(slot, stack) + override def setItem(slot: Int, stack: ItemStack): Unit = { + if (slot < 0) agent.equipmentInventory.setItem(~slot, stack) + else agent.mainInventory.setItem(slot, stack) } - override def getName: String = agent.mainInventory.getName + override def getName: ITextComponent = new StringTextComponent(agent.name) - override def getInventoryStackLimit: Int = agent.mainInventory.getInventoryStackLimit + override def getMaxStackSize: Int = agent.mainInventory.getMaxStackSize - override def markDirty(): Unit = agent.mainInventory.markDirty() + override def setChanged(): Unit = agent.mainInventory.setChanged() - override def isUsableByPlayer(player: EntityPlayer): Boolean = agent.mainInventory.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity): Boolean = agent.mainInventory.stillValid(player) - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = - if (slot < 0) agent.equipmentInventory.isItemValidForSlot(~slot, stack) - else agent.mainInventory.isItemValidForSlot(slot, stack) + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = + if (slot < 0) agent.equipmentInventory.canPlaceItem(~slot, stack) + else agent.mainInventory.canPlaceItem(slot, stack) } diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index f56497d32d..7a1416b231 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -3,6 +3,7 @@ package li.cil.oc.server.agent import java.util import java.util.UUID +import com.mojang.datafixers.util.Either import com.mojang.authlib.GameProfile import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -12,38 +13,49 @@ import li.cil.oc.api.network.Connector import li.cil.oc.common.EventHandler import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils -import net.minecraft.block.BlockPistonBase +import net.minecraft.block.PistonBlock import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.IMerchant -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.entity.player.EntityPlayer.SleepResult -import net.minecraft.init.Blocks -import net.minecraft.init.Items +import net.minecraft.entity.EntitySize +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.Pose +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.merchant.IMerchant +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerEntity.SleepResult +import net.minecraft.block.Blocks +import net.minecraft.item.Items import net.minecraft.inventory._ -import net.minecraft.item.ItemBlock +import net.minecraft.inventory.container.INamedContainerProvider +import net.minecraft.inventory.container.PlayerContainer +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemUseContext import net.minecraft.item.ItemStack -import net.minecraft.network.NetHandlerPlayServer -import net.minecraft.potion.PotionEffect -import net.minecraft.server.management.{PlayerInteractionManager, UserListOpsEntry} +import net.minecraft.item.MerchantOffers +import net.minecraft.network.play.ServerPlayNetHandler +import net.minecraft.network.play.client.CPlayerDiggingPacket +import net.minecraft.potion.EffectInstance +import net.minecraft.server.management.{PlayerInteractionManager, OpEntry} import net.minecraft.tileentity._ -import net.minecraft.util.EnumFacing -import net.minecraft.util._ +import net.minecraft.util.ActionResultType +import net.minecraft.util.DamageSource +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.TextComponentString -import net.minecraft.world.IInteractionObject +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import net.minecraft.world.WorldServer +import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.common.util.NonNullSupplier import net.minecraftforge.event.ForgeEventFactory import net.minecraftforge.event.entity.living.LivingEntityUseItemEvent import net.minecraftforge.event.entity.player.PlayerInteractEvent import net.minecraftforge.fml.common.ObfuscationReflectionHelper -import net.minecraftforge.fml.common.eventhandler.{Event, EventPriority, SubscribeEvent} +import net.minecraftforge.eventbus.api.{Event, EventPriority, SubscribeEvent} import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.wrapper._ @@ -52,7 +64,7 @@ import scala.collection.convert.WrapAsScala._ object Player { def profileFor(agent: internal.Agent): GameProfile = { val uuid = agent.ownerUUID - val randomId = (agent.world.rand.nextInt(0xFFFFFF) + 1).toString + val randomId = (agent.world.random.nextInt(0xFFFFFF) + 1).toString val name = Settings.get.nameFormat. replace("$player$", agent.ownerName). replace("$random$", randomId) @@ -71,149 +83,152 @@ object Player { } } - def updatePositionAndRotation(player: Player, facing: EnumFacing, side: EnumFacing) { + def updatePositionAndRotation(player: Player, facing: Direction, side: Direction) { player.facing = facing player.side = side - val direction = new Vec3d( - facing.getFrontOffsetX + side.getFrontOffsetX, - facing.getFrontOffsetY + side.getFrontOffsetY, - facing.getFrontOffsetZ + side.getFrontOffsetZ).normalize() + val direction = new Vector3d( + facing.getStepX + side.getStepX, + facing.getStepY + side.getStepY, + facing.getStepZ + side.getStepZ).normalize() val yaw = Math.toDegrees(-Math.atan2(direction.x, direction.z)).toFloat val pitch = Math.toDegrees(-Math.atan2(direction.y, Math.sqrt((direction.x * direction.x) + (direction.z * direction.z)))).toFloat * 0.99f - player.setLocationAndAngles(player.agent.xPosition, player.agent.yPosition, player.agent.zPosition, yaw, pitch) - player.prevRotationPitch = player.rotationPitch - player.prevRotationYaw = player.rotationYaw + player.setPos(player.agent.xPosition, player.agent.yPosition, player.agent.zPosition) + player.setRot(yaw, pitch) + player.xRotO = player.xRot + player.yRotO = player.yRot } - def setInventoryPlayerItems(player: Player): Unit = { + def setPlayerInventoryItems(player: Player): Unit = { // the offhand is simply the agent's tool item val agent = player.agent def setCopyOrNull(inv: net.minecraft.util.NonNullList[ItemStack], agentInv: IInventory, slot: Int): Unit = { - val item = agentInv.getStackInSlot(slot) + val item = agentInv.getItem(slot) inv(slot) = if (item != null) item.copy() else ItemStack.EMPTY } for (i <- 0 until 4) { - setCopyOrNull(player.inventory.armorInventory, agent.equipmentInventory, i) + setCopyOrNull(player.inventory.armor, agent.equipmentInventory, i) } - // mainInventory is 36 items + // items is 36 items // the agent inventory is 100 items with some space for components // leaving us 88..we'll copy what we can - val size = player.inventory.mainInventory.length min agent.mainInventory.getSizeInventory + val size = player.inventory.items.length min agent.mainInventory.getContainerSize for (i <- 0 until size) { - setCopyOrNull(player.inventory.mainInventory, agent.mainInventory, i) + setCopyOrNull(player.inventory.items, agent.mainInventory, i) } - player.inventoryContainer.detectAndSendChanges() + player.inventoryMenu.broadcastChanges() } - def detectInventoryPlayerChanges(player: Player): Unit = { + def detectPlayerInventoryChanges(player: Player): Unit = { val agent = player.agent - player.inventoryContainer.detectAndSendChanges() + player.inventoryMenu.broadcastChanges() // The follow code will set agent.inventories = FakePlayer's inv.stack def setCopy(inv: IInventory, index: Int, item: ItemStack): Unit = { val result = if (item != null) item.copy else ItemStack.EMPTY - val current = inv.getStackInSlot(index) - if (!ItemStack.areItemStacksEqual(result, current)) { - inv.setInventorySlotContents(index, result) + val current = inv.getItem(index) + if (!ItemStack.matches(result, current)) { + inv.setItem(index, result) } } for (i <- 0 until 4) { - setCopy(agent.equipmentInventory(), i, player.inventory.armorInventory(i)) + setCopy(agent.equipmentInventory(), i, player.inventory.armor(i)) } - val size = player.inventory.mainInventory.length min agent.mainInventory.getSizeInventory + val size = player.inventory.items.length min agent.mainInventory.getContainerSize for (i <- 0 until size) { - setCopy(agent.mainInventory, i, player.inventory.mainInventory(i)) + setCopy(agent.mainInventory, i, player.inventory.items(i)) } } } -class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanceOf[WorldServer], Player.profileFor(agent)) { - connection= new NetHandlerPlayServer(mcServer, FakeNetworkManager, this) +class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanceOf[ServerWorld], Player.profileFor(agent)) { + connection= new ServerPlayNetHandler(server, FakeNetworkManager, this) - capabilities.allowFlying = true - capabilities.disableDamage = true - capabilities.isFlying = true - onGround = true + abilities.mayfly = true + abilities.invulnerable = true + abilities.flying = true + setOnGround(true) - override def getYOffset = 0.5f + override def getMyRidingOffset = 0.5 - override def getEyeHeight = 0f + override def getStandingEyeHeight(pose: Pose, size: EntitySize) = 0f - setSize(1, 1) + override def getDimensions(pose: Pose) = new EntitySize(1, 1, true) + refreshDimensions() { this.inventory = new Inventory(this, agent) - this.inventory.player = this // because the inventory was just overwritten, the container is now detached - this.inventoryContainer = new AgentContainer(this) - this.openContainer = this.inventoryContainer + this.inventoryMenu = new PlayerContainer(inventory, !level.isClientSide, this) + this.containerMenu = this.inventoryMenu try { - ObfuscationReflectionHelper.setPrivateValue(classOf[EntityPlayer], this, new PlayerMainInvWrapper(inventory), "playerMainHandler") - ObfuscationReflectionHelper.setPrivateValue(classOf[EntityPlayer], this, new CombinedInvWrapper(new PlayerArmorInvWrapper(inventory), new PlayerOffhandInvWrapper(inventory)), "playerEquipmentHandler") - ObfuscationReflectionHelper.setPrivateValue(classOf[EntityPlayer], this, new PlayerInvWrapper(inventory), "playerJoinedHandler") + ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + override def get = new PlayerMainInvWrapper(inventory) + }), "playerMainHandler") + ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + override def get = new CombinedInvWrapper(new PlayerArmorInvWrapper(inventory), new PlayerOffhandInvWrapper(inventory)) + }), "playerEquipmentHandler") + ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + override def get = new PlayerInvWrapper(inventory) + }), "playerJoinedHandler") } catch { case _: Exception => } } - var facing, side = EnumFacing.SOUTH + var facing, side = Direction.SOUTH - override def getPosition = new BlockPos(posX, posY, posZ) - - override def getDefaultEyeHeight = 0f - - override def getDisplayName = new TextComponentString(agent.name) - - interactionManager.setBlockReachDistance(1) + override def getName = new StringTextComponent(agent.name) // ----------------------------------------------------------------------- // - def closestEntity[Type <: Entity](clazz: Class[Type], side: EnumFacing = facing): Option[Entity] = { + def closestEntity[Type <: Entity](clazz: Class[Type], side: Direction = facing): Option[Entity] = { val bounds = BlockPosition(agent).offset(side).bounds - Option(world.findNearestEntityWithinAABB(clazz, bounds, this)) + val candidates = level.getEntitiesOfClass(clazz, bounds, null) + if (candidates.isEmpty) return None + Some(candidates.minBy(e => distanceToSqr(e))) } - def entitiesOnSide[Type <: Entity](clazz: Class[Type], side: EnumFacing): util.List[Type] = { + def entitiesOnSide[Type <: Entity](clazz: Class[Type], side: Direction): util.List[Type] = { entitiesInBlock(clazz, BlockPosition(agent).offset(side)) } def entitiesInBlock[Type <: Entity](clazz: Class[Type], blockPos: BlockPosition): util.List[Type] = { - world.getEntitiesWithinAABB(clazz, blockPos.bounds) + level.getEntitiesOfClass(clazz, blockPos.bounds, null) } - private def adjacentItems: util.List[EntityItem] = { - world.getEntitiesWithinAABB(classOf[EntityItem], BlockPosition(agent).bounds.grow(2, 2, 2)) + private def adjacentItems: util.List[ItemEntity] = { + level.getEntitiesOfClass(classOf[ItemEntity], BlockPosition(agent).bounds.inflate(2, 2, 2), null) } - private def collectDroppedItems(itemsBefore: Iterable[EntityItem]) { + private def collectDroppedItems(itemsBefore: Iterable[ItemEntity]) { val itemsAfter = adjacentItems val itemsDropped = itemsAfter -- itemsBefore if (itemsDropped.nonEmpty) { for (drop <- itemsDropped) { - drop.setNoPickupDelay() - drop.onCollideWithPlayer(this) + drop.setDefaultPickUpDelay() + drop.playerTouch(this) } } } // ----------------------------------------------------------------------- // - override def attackTargetEntityWithCurrentItem(entity: Entity) { + override def attack(entity: Entity) { callUsingItemInSlot(agent.equipmentInventory, 0, stack => entity match { - case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage. + case player: PlayerEntity if !canHarmPlayer(player) => // Avoid player damage. case _ => val event = new RobotAttackEntityEvent.Pre(agent, entity) MinecraftForge.EVENT_BUS.post(event) if (!event.isCanceled) { - super.attackTargetEntityWithCurrentItem(entity) + super.attack(entity) MinecraftForge.EVENT_BUS.post(new RobotAttackEntityEvent.Post(agent, entity)) } }) } - override def interactOn(entity: Entity, hand: EnumHand): EnumActionResult = { + override def interactOn(entity: Entity, hand: Hand): ActionResultType = { val cancel = try MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.EntityInteract(this, hand, entity)) catch { case t: Throwable => if (!t.getStackTrace.exists(_.getClassName.startsWith("mods.battlegear2."))) { @@ -222,42 +237,43 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc false } if(!cancel && callUsingItemInSlot(agent.equipmentInventory, 0, stack => { - val result = isItemUseAllowed(stack) && (entity.processInitialInteract(this, hand) || (entity match { - case living: EntityLivingBase if !getHeldItemMainhand.isEmpty => getHeldItemMainhand.interactWithEntity(this, living, hand) + val result = isItemUseAllowed(stack) && (entity.interact(this, hand).consumesAction || (entity match { + case living: LivingEntity if !getItemInHand(Hand.MAIN_HAND).isEmpty => getItemInHand(Hand.MAIN_HAND).interactLivingEntity(this, living, hand).consumesAction case _ => false })) - if (!getHeldItemMainhand.isEmpty) { - if (getHeldItemMainhand.getCount <= 0) { - val orig = getHeldItemMainhand - this.inventory.setInventorySlotContents(this.inventory.currentItem, ItemStack.EMPTY) + if (!getItemInHand(Hand.MAIN_HAND).isEmpty) { + if (getItemInHand(Hand.MAIN_HAND).getCount <= 0) { + val orig = getItemInHand(Hand.MAIN_HAND) + this.inventory.setItem(this.inventory.selected, ItemStack.EMPTY) ForgeEventFactory.onPlayerDestroyItem(this, orig, hand) } else { // because of various hacks for IC2, we expect the in-hand result to be moved to our offhand buffer - this.inventory.offHandInventory.set(0, getHeldItemMainhand) - this.inventory.setInventorySlotContents(this.inventory.currentItem, ItemStack.EMPTY) + this.inventory.offhand.set(0, getItemInHand(Hand.MAIN_HAND)) + this.inventory.setItem(this.inventory.selected, ItemStack.EMPTY) } } result - })) EnumActionResult.SUCCESS else EnumActionResult.PASS + })) ActionResultType.sidedSuccess(level.isClientSide) else ActionResultType.PASS } - def activateBlockOrUseItem(pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, duration: Double): ActivationType.Value = { + def activateBlockOrUseItem(pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, duration: Double): ActivationType.Value = { callUsingItemInSlot(agent.equipmentInventory, 0, stack => { if (shouldCancel(() => fireRightClickBlock(pos, side))) { return ActivationType.None } val item = if (!stack.isEmpty) stack.getItem else null - if (item != null && item.onItemUseFirst(this, world, pos, side, hitX, hitY, hitZ, EnumHand.OFF_HAND) == EnumActionResult.SUCCESS) { + val state = level.getBlockState(pos) + val traceEndPos = new Vector3d(pos.getX + hitX, pos.getY + hitY, pos.getZ + hitZ) + val traceCtx = if (state.getBlock.isAir(state, level, pos)) BlockRayTraceResult.miss(traceEndPos, side, pos) else new BlockRayTraceResult(traceEndPos, side, pos, false) + if (item != null && item.onItemUseFirst(stack, new ItemUseContext(level, this, Hand.OFF_HAND, stack, traceCtx)).consumesAction) { return ActivationType.ItemUsed } - val state = world.getBlockState(pos) - val block = state.getBlock - val canActivate = block != Blocks.AIR && Settings.get.allowActivateBlocks - val shouldActivate = canActivate && (!isSneaking || (item == null || item.doesSneakBypassUse(stack, world, pos, this))) + val canActivate = !state.getBlock.isAir(state, level, pos) && Settings.get.allowActivateBlocks + val shouldActivate = canActivate && (!isCrouching || (item == null || item.doesSneakBypassUse(stack, level, pos, this))) val result = - if (shouldActivate && block.onBlockActivated(world, pos, state, this, EnumHand.OFF_HAND, side, hitX, hitY, hitZ)) + if (shouldActivate && state.use(level, this, Hand.OFF_HAND, new BlockRayTraceResult(new Vector3d(hitX, hitY, hitZ), side, pos, false)).consumesAction) ActivationType.BlockActivated else if (duration <= Double.MinPositiveValue && isItemUseAllowed(stack) && tryPlaceBlockWhileHandlingFunnySpecialCases(stack, pos, side, hitX, hitY, hitZ)) ActivationType.ItemPlaced @@ -270,53 +286,52 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc }) } - override def setItemStackToSlot(slotIn: EntityEquipmentSlot, stack: ItemStack): Unit = { - var superCall: () => Unit = () => super.setItemStackToSlot(slotIn, stack) - if (slotIn == EntityEquipmentSlot.MAINHAND) { - agent.equipmentInventory.setInventorySlotContents(0, stack) + override def setItemSlot(slotIn: EquipmentSlotType, stack: ItemStack): Unit = { + var superCall: () => Unit = () => super.setItemSlot(slotIn, stack) + if (slotIn == EquipmentSlotType.MAINHAND) { + agent.equipmentInventory.setItem(0, stack) superCall = () => { - val slot = inventory.currentItem - // So, if we're not in the main inventory, currentItem is set to -1 + val slot = inventory.selected + // So, if we're not in the main inventory, selected is set to -1 // for compatibility with mods that try accessing the inv directly - // using inventory.currentItem. See li.cil.oc.server.agent.Inventory - if(inventory.currentItem < 0) inventory.currentItem = ~inventory.currentItem - super.setItemStackToSlot(slotIn, stack) - inventory.currentItem = slot + // using inventory.selected. See li.cil.oc.server.agent.Inventory + if(inventory.selected < 0) inventory.selected = ~inventory.selected + super.setItemSlot(slotIn, stack) + inventory.selected = slot } - } else if(slotIn == EntityEquipmentSlot.OFFHAND) { - inventory.offHandInventory.set(0, stack) + } else if(slotIn == EquipmentSlotType.OFFHAND) { + inventory.offhand.set(0, stack) } superCall() } - override def getItemStackFromSlot(slotIn: EntityEquipmentSlot): ItemStack = { - if (slotIn == EntityEquipmentSlot.MAINHAND) - agent.equipmentInventory.getStackInSlot(0) - else if(slotIn == EntityEquipmentSlot.OFFHAND) - inventory.offHandInventory.get(0) - else super.getItemStackFromSlot(slotIn) + override def getItemBySlot(slotIn: EquipmentSlotType): ItemStack = { + if (slotIn == EquipmentSlotType.MAINHAND) + agent.equipmentInventory.getItem(0) + else if(slotIn == EquipmentSlotType.OFFHAND) + inventory.offhand.get(0) + else super.getItemBySlot(slotIn) } - def fireRightClickBlock(pos: BlockPos, side: EnumFacing): PlayerInteractEvent.RightClickBlock = { - val hitVec = new Vec3d(0.5 + side.getDirectionVec.getX * 0.5, 0.5 + side.getDirectionVec.getY * 0.5, 0.5 + side.getDirectionVec.getZ * 0.5) - val event = new PlayerInteractEvent.RightClickBlock(this, EnumHand.OFF_HAND, pos, side, hitVec) + def fireRightClickBlock(pos: BlockPos, side: Direction): PlayerInteractEvent.RightClickBlock = { + val hitVec = new Vector3d(0.5 + side.getStepX * 0.5, 0.5 + side.getStepY * 0.5, 0.5 + side.getStepZ * 0.5) + val event = new PlayerInteractEvent.RightClickBlock(this, Hand.OFF_HAND, pos, new BlockRayTraceResult(hitVec, side, pos, false)) MinecraftForge.EVENT_BUS.post(event) event } - def fireLeftClickBlock(pos: BlockPos, side: EnumFacing): PlayerInteractEvent.LeftClickBlock = { - val hitVec = new Vec3d(0.5 + side.getDirectionVec.getX * 0.5, 0.5 + side.getDirectionVec.getY * 0.5, 0.5 + side.getDirectionVec.getZ * 0.5) - net.minecraftforge.common.ForgeHooks.onLeftClickBlock(this, pos, side, hitVec) + def fireLeftClickBlock(pos: BlockPos, side: Direction): PlayerInteractEvent.LeftClickBlock = { + net.minecraftforge.common.ForgeHooks.onLeftClickBlock(this, pos, side) } def fireRightClickAir(): PlayerInteractEvent.RightClickItem = { - val event = new PlayerInteractEvent.RightClickItem(this, EnumHand.OFF_HAND) + val event = new PlayerInteractEvent.RightClickItem(this, Hand.OFF_HAND) MinecraftForge.EVENT_BUS.post(event) event } private def trySetActiveHand(duration: Double): Boolean = { - stopActiveHand() + releaseUsingItem() val entity = this val durationHandler = new { @SubscribeEvent(priority = EventPriority.LOWEST) @@ -328,8 +343,8 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc } MinecraftForge.EVENT_BUS.register(durationHandler) try { - setActiveHand(EnumHand.OFF_HAND) - isHandActive + startUsingItem(Hand.OFF_HAND) + isUsingItem } catch { case _: Exception => false } finally { @@ -349,25 +364,25 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc return false } - val maxDuration = stack.getMaxItemUseDuration + val maxDuration = stack.getUseDuration val heldTicks = Math.max(0, Math.min(maxDuration, (duration * 20).toInt)) agent.machine.pause(heldTicks / 20.0) // setting the active hand will also set its initial duration - val useItemResult = stack.useItemRightClick(world, this, EnumHand.OFF_HAND) - stopActiveHand() + val useItemResult = stack.use(level, this, Hand.OFF_HAND) + releaseUsingItem() - if (useItemResult.getType != EnumActionResult.SUCCESS) { + if (!useItemResult.getResult.consumesAction) { return false } - val newStack = useItemResult.getResult + val newStack = useItemResult.getObject val stackChanged: Boolean = - !ItemStack.areItemStacksEqual(oldStack, newStack) || - !ItemStack.areItemStacksEqual(oldStack, stack) + !ItemStack.matches(oldStack, newStack) || + !ItemStack.matches(oldStack, stack) if (stackChanged) { - inventory.offHandInventory.set(0, newStack) + inventory.offhand.set(0, newStack) } stackChanged } @@ -386,19 +401,17 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc // Change the offset at which items are used, to avoid hitting // the robot itself (e.g. with bows, potions, mining laser, ...). - posX += facing.getFrontOffsetX / 2.0 - posZ += facing.getFrontOffsetZ / 2.0 + setPos(getX + facing.getStepX / 2.0, getY, getZ + facing.getStepZ / 2.0) try { useItemWithHand(duration, stackOption.get) } finally { - posX -= facing.getFrontOffsetX / 2.0 - posZ -= facing.getFrontOffsetZ / 2.0 + setPos(getX - facing.getStepX / 2.0, getY, getZ - facing.getStepZ / 2.0) } } - def placeBlock(slot: Int, pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + def placeBlock(slot: Int, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { callUsingItemInSlot(agent.mainInventory, slot, stack => { if (shouldCancel(() => fireRightClickBlock(pos, side))) { return false @@ -408,14 +421,14 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc }, repair = false) } - def clickBlock(pos: BlockPos, side: EnumFacing): Double = callUsingItemInSlot(agent.equipmentInventory, 0, stack => { - val state = world.getBlockState(pos) + def clickBlock(pos: BlockPos, side: Direction): Double = callUsingItemInSlot(agent.equipmentInventory, 0, stack => { + val state = level.getBlockState(pos) val block = state.getBlock - if (!block.canHarvestBlock(world, pos, this)) return 0 + if (!state.canHarvestBlock(level, pos, this)) return 0 - val hardness = block.getBlockHardness(state, world, pos) - val cobwebOverride = block == Blocks.WEB && Settings.get.screwCobwebs + val hardness = state.getDestroySpeed(level, pos) + val cobwebOverride = block == Blocks.COBWEB && Settings.get.screwCobwebs val strength = getDigSpeed(state, pos) val breakTime = @@ -425,13 +438,13 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc if (breakTime.isInfinity) return 0 if (breakTime < 0) return breakTime - val preEvent = new RobotBreakBlockEvent.Pre(agent, world, pos, breakTime * Settings.get.harvestRatio) + val preEvent = new RobotBreakBlockEvent.Pre(agent, level, pos, breakTime * Settings.get.harvestRatio) MinecraftForge.EVENT_BUS.post(preEvent) if (preEvent.isCanceled) return 0 val adjustedBreakTime = Math.max(0.05, preEvent.getBreakTime) if (!PlayerInteractionManagerHelper.onBlockClicked(this, pos, side)) { - if (world.isAirBlock(pos)) { + if (level.isEmptyBlock(pos)) { return 1.0 / 20.0 } return 0 @@ -443,10 +456,10 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc }) private def isItemUseAllowed(stack: ItemStack) = stack.isEmpty || { - (Settings.get.allowUseItemsWithDuration || stack.getMaxItemUseDuration <= 0) && !stack.isItemEqual(new ItemStack(Items.LEAD)) + (Settings.get.allowUseItemsWithDuration || stack.getUseDuration <= 0) && !stack.sameItem(new ItemStack(Items.LEAD)) } - override def dropItem(stack: ItemStack, dropAround: Boolean, traceItem: Boolean): EntityItem = + override def drop(stack: ItemStack, dropAround: Boolean, traceItem: Boolean): ItemEntity = InventoryUtils.spawnStackInWorld(BlockPosition(agent), stack, if (dropAround) None else Option(facing)) private def shouldCancel(f: () => PlayerInteractEvent) = { @@ -470,31 +483,31 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc private def callUsingItemInSlot[T](inventory: IInventory, slot: Int, f: ItemStack => T, repair: Boolean = true) = { val itemsBefore = adjacentItems - val stack = inventory.getStackInSlot(slot) + val stack = inventory.getItem(slot) val oldStack = stack.copy() - this.inventory.currentItem = if (inventory == agent.mainInventory) slot else ~slot - this.inventory.offHandInventory.set(0, inventory.getStackInSlot(slot)) + this.inventory.selected = if (inventory == agent.mainInventory) slot else ~slot + this.inventory.offhand.set(0, inventory.getItem(slot)) try { f(stack) } finally { - this.inventory.currentItem = 0 - inventory.setInventorySlotContents(slot, this.inventory.offHandInventory.get(0)) - this.inventory.offHandInventory.set(0, ItemStack.EMPTY) - val newStack = inventory.getStackInSlot(slot) + this.inventory.selected = 0 + inventory.setItem(slot, this.inventory.offhand.get(0)) + this.inventory.offhand.set(0, ItemStack.EMPTY) + val newStack = inventory.getItem(slot) // this is only possible if f() modified the stack object in-place // looking at you, ic2 - if (ItemStack.areItemStacksEqual(oldStack, newStack) && - !ItemStack.areItemStacksEqual(oldStack, stack)) { - inventory.setInventorySlotContents(slot, stack) + if (ItemStack.matches(oldStack, newStack) && + !ItemStack.matches(oldStack, stack)) { + inventory.setItem(slot, stack) } if (!newStack.isEmpty) { if (newStack.getCount <= 0) { - inventory.setInventorySlotContents(slot, ItemStack.EMPTY) + inventory.setItem(slot, ItemStack.EMPTY) } if (repair) { if (newStack.getCount > 0) tryRepair(newStack, oldStack) - else ForgeEventFactory.onPlayerDestroyItem(this, newStack, EnumHand.OFF_HAND) + else ForgeEventFactory.onPlayerDestroyItem(this, newStack, Hand.OFF_HAND) } } collectDroppedItems(itemsBefore) @@ -512,37 +525,40 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc } } - private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, pos: BlockPos, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = { + private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { !stack.isEmpty && stack.getCount > 0 && { - val event = new RobotPlaceBlockEvent.Pre(agent, stack, world, pos) + val event = new RobotPlaceBlockEvent.Pre(agent, stack, level, pos) MinecraftForge.EVENT_BUS.post(event) if (event.isCanceled) false else { - val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0 - setPosition(posX, posY - fakeEyeHeight, posZ) - Player.setInventoryPlayerItems(this) - val didPlace = stack.onItemUse(this, world, pos, EnumHand.OFF_HAND, side, hitX, hitY, hitZ) - Player.detectInventoryPlayerChanges(this) - setPosition(posX, posY + fakeEyeHeight, posZ) - if (didPlace == EnumActionResult.SUCCESS) { - MinecraftForge.EVENT_BUS.post(new RobotPlaceBlockEvent.Post(agent, stack, world, pos)) + val fakeEyeHeight = if (xRot < 0 && isSomeKindOfPiston(stack)) 1.82 else 0 + setPos(getX, getY - fakeEyeHeight, getZ) + Player.setPlayerInventoryItems(this) + val state = level.getBlockState(pos) + val traceEndPos = new Vector3d(pos.getX + hitX, pos.getY + hitY, pos.getZ + hitZ) + val traceCtx = if (state.getBlock.isAir(state, level, pos)) BlockRayTraceResult.miss(traceEndPos, side, pos) else new BlockRayTraceResult(traceEndPos, side, pos, false) + val didPlace = stack.useOn(new ItemUseContext(level, this, Hand.OFF_HAND, stack, traceCtx)) + Player.detectPlayerInventoryChanges(this) + setPos(getX, getY + fakeEyeHeight, getZ) + if (didPlace.consumesAction) { + MinecraftForge.EVENT_BUS.post(new RobotPlaceBlockEvent.Post(agent, stack, level, pos)) } - didPlace == EnumActionResult.SUCCESS + didPlace.consumesAction } } } private def isSomeKindOfPiston(stack: ItemStack) = stack.getItem match { - case itemBlock: ItemBlock => + case itemBlock: BlockItem => val block = itemBlock.getBlock - block != null && block.isInstanceOf[BlockPistonBase] + block != null && block.isInstanceOf[PistonBlock] case _ => false } // ----------------------------------------------------------------------- // - override def addExhaustion(amount: Float) { + override def causeFoodExhaustion(amount: Float) { if (Settings.get.robotExhaustionCost > 0) { agent.machine.node match { case connector: Connector => connector.changeBuffer(-Settings.get.robotExhaustionCost * amount) @@ -552,80 +568,72 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc MinecraftForge.EVENT_BUS.post(new RobotExhaustionEvent(agent, amount)) } - override def closeScreen() {} + override def closeContainer() {} - override def swingArm(hand: EnumHand): Unit = {} + override def swing(hand: Hand): Unit = {} - override def canUseCommand(level: Int, command: String): Boolean = { - ("seed" == command && !mcServer.isDedicatedServer) || - "tell" == command || - "help" == command || - "me" == command || { - val config = mcServer.getPlayerList - config.canSendCommands(getGameProfile) && { - config.getOppedPlayers.getEntry(getGameProfile) match { - case opEntry: UserListOpsEntry => opEntry.getPermissionLevel >= level - case _ => mcServer.getOpPermissionLevel >= level - } + override protected def getPermissionLevel: Int = { + val config = server.getPlayerList + if (config.isOp(getGameProfile)) { + config.getOps.get(getGameProfile) match { + case opEntry: OpEntry => opEntry.getLevel + case _ => server.getOperatorUserPermissionLevel } } + else 0 } - override def canAttackPlayer(player: EntityPlayer): Boolean = Settings.get.canAttackPlayers + override def canHarmPlayer(player: PlayerEntity): Boolean = Settings.get.canAttackPlayers override def canEat(value: Boolean) = false - override def isPotionApplicable(effect: PotionEffect) = false + override def canBeAffected(effect: EffectInstance) = false - override def attackEntityAsMob(entity: Entity) = false + override def doHurtTarget(entity: Entity) = false - override def attackEntityFrom(source: DamageSource, damage: Float) = false + override def hurt(source: DamageSource, damage: Float) = false override def heal(amount: Float) {} override def setHealth(value: Float) {} - override def setDead(): Unit = isDead = true + override def remove(invalidate: Boolean): Unit = super.remove(false) - override def onLivingUpdate() {} + override def aiStep() {} - override def onItemPickup(entity: Entity, count: Int) {} + override def take(entity: Entity, count: Int) {} - override def setRevengeTarget(entity: EntityLivingBase) {} + override def setLastHurtByMob(entity: LivingEntity) {} - override def setLastAttackedEntity(entity: Entity) {} + override def setLastHurtMob(entity: Entity) {} override def startRiding(entityIn: Entity, force: Boolean): Boolean = false - override def trySleep(bedLocation: BlockPos) = SleepResult.OTHER_PROBLEM - - override def sendMessage(message: ITextComponent) {} + override def startSleepInBed(bedLocation: BlockPos) = Either.left[SleepResult, net.minecraft.util.Unit](SleepResult.OTHER_PROBLEM) - override def displayGUIChest(inventory: IInventory) {} + override def sendMessage(message: ITextComponent, sender: UUID) {} - override def displayGuiCommandBlock(commandBlock: TileEntityCommandBlock): Unit = {} + override def openCommandBlock(commandBlock: CommandBlockTileEntity): Unit = {} - override def displayVillagerTradeGui(villager: IMerchant): Unit = { - villager.setCustomer(null) - } + override def sendMerchantOffers(containerId: Int, offers: MerchantOffers, villagerLevel: Int, villagerXP: Int, showProgress: Boolean, canRestock: Boolean): Unit = {} - override def displayGui(guiOwner: IInteractionObject) {} + override def openMenu(guiOwner: INamedContainerProvider) = util.OptionalInt.empty - override def displayGuiEditCommandCart(thing: CommandBlockBaseLogic): Unit = {} + override def openMinecartCommandBlock(thing: CommandBlockLogic): Unit = {} - override def openEditSign(signTile: TileEntitySign) {} + override def openTextEdit(signTile: SignTileEntity) {} // ----------------------------------------------------------------------- // - class DamageOverTime(val player: Player, val pos: BlockPos, val side: EnumFacing, val ticksTotal: Int) { - val world: World = player.world + class DamageOverTime(val player: Player, val pos: BlockPos, val side: Direction, val ticksTotal: Int) { + val level: World = player.level var ticks = 0 var lastDamageSent = 0 def tick(): Unit = { // Cancel if the agent stopped or our action is invalidated some other way. - if (world != player.world || !world.isBlockLoaded(pos) || world.isAirBlock(pos) || !player.agent.machine.isRunning) { - player.interactionManager.cancelDestroyingBlock() + if (level != player.level || !level.isLoaded(pos) || level.isEmptyBlock(pos) || !player.agent.machine.isRunning) { + player.gameMode.handleBlockBreakAction(pos, CPlayerDiggingPacket.Action.ABORT_DESTROY_BLOCK, side, 0) return } @@ -641,11 +649,9 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc } else { callUsingItemInSlot(player.agent.equipmentInventory(), 0, _ => { - this.player.posX -= side.getFrontOffsetX / 2.0 - this.player.posZ -= side.getFrontOffsetZ / 2.0 + this.player.setPos(this.player.getX - side.getStepX / 2.0, this.player.getY, this.player.getZ - side.getStepZ / 2.0) val expGained: Int = PlayerInteractionManagerHelper.blockRemoving(player, pos) - this.player.posX += side.getFrontOffsetX / 2.0 - this.player.posZ += side.getFrontOffsetZ / 2.0 + this.player.setPos(this.player.getX + side.getStepX / 2.0, this.player.getY, this.player.getZ + side.getStepZ / 2.0) if (expGained >= 0) { MinecraftForge.EVENT_BUS.post(new RobotBreakBlockEvent.Post(agent, expGained)) } diff --git a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala index 25351a95ed..dfb862e84e 100644 --- a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala +++ b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala @@ -1,6 +1,7 @@ package li.cil.oc.server.agent -import net.minecraft.util.EnumFacing +import net.minecraft.network.play.client.CPlayerDiggingPacket +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import li.cil.oc.OpenComputers import li.cil.oc.api.network.Node @@ -9,7 +10,7 @@ import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.entity.player.PlayerEvent import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.fml.common.ObfuscationReflectionHelper -import net.minecraftforge.fml.common.eventhandler.{EventPriority, SubscribeEvent} +import net.minecraftforge.eventbus.api.{EventPriority, SubscribeEvent} import scala.collection.convert.WrapAsScala._ @@ -17,24 +18,24 @@ object PlayerInteractionManagerHelper { private def isDestroyingBlock(player: Player): Boolean = { try { - ObfuscationReflectionHelper.getPrivateValue(classOf[PlayerInteractionManager], player.interactionManager, "isDestroyingBlock", "field_73088_d").asInstanceOf[Boolean] + ObfuscationReflectionHelper.getPrivateValue(classOf[PlayerInteractionManager], player.gameMode, "isDestroyingBlock").asInstanceOf[Boolean] } catch { case _: Exception => true } } - def onBlockClicked(player: Player, pos: BlockPos, side: EnumFacing): Boolean = { + def onBlockClicked(player: Player, pos: BlockPos, side: Direction): Boolean = { if (isDestroyingBlock(player)) { - player.interactionManager.cancelDestroyingBlock() + player.gameMode.handleBlockBreakAction(pos, CPlayerDiggingPacket.Action.ABORT_DESTROY_BLOCK, side, 0) } - player.interactionManager.onBlockClicked(pos, side) + player.gameMode.handleBlockBreakAction(pos, CPlayerDiggingPacket.Action.START_DESTROY_BLOCK, side, 0) isDestroyingBlock(player) } def updateBlockRemoving(player: Player): Boolean = { if (!isDestroyingBlock(player)) return false - player.interactionManager.updateBlockRemoving() + player.gameMode.tick() isDestroyingBlock(player) } @@ -61,7 +62,7 @@ object PlayerInteractionManagerHelper { @SubscribeEvent(priority = EventPriority.LOWEST) def onBreakSpeedEvent(breakSpeedEvent: PlayerEvent.BreakSpeed): Unit = { - if (player == breakSpeedEvent.getEntityPlayer) + if (player == breakSpeedEvent.getPlayer) breakSpeedEvent.setNewSpeed(scala.Float.MaxValue) } @@ -78,12 +79,12 @@ object PlayerInteractionManagerHelper { MinecraftForge.EVENT_BUS.register(infBreaker) try { - player.interactionManager.blockRemoving(pos) + player.gameMode.handleBlockBreakAction(pos, CPlayerDiggingPacket.Action.STOP_DESTROY_BLOCK, null, 0) infBreaker.expToDrop } catch { case e: Exception => { OpenComputers.log.info(s"an exception was thrown while trying to call blockRemoving: ${e.getMessage}") - player.interactionManager.cancelDestroyingBlock() + player.gameMode.handleBlockBreakAction(pos, CPlayerDiggingPacket.Action.ABORT_DESTROY_BLOCK, null, 0) -1 } } finally { diff --git a/src/main/scala/li/cil/oc/server/command/CommandHandler.scala b/src/main/scala/li/cil/oc/server/command/CommandHandler.scala deleted file mode 100644 index 0ca8790be2..0000000000 --- a/src/main/scala/li/cil/oc/server/command/CommandHandler.scala +++ /dev/null @@ -1,15 +0,0 @@ -package li.cil.oc.server.command - -import net.minecraftforge.fml.common.event.FMLServerStartingEvent - -object CommandHandler { - def register(e: FMLServerStartingEvent) { - e.registerServerCommand(DebugNanomachinesCommand) - e.registerServerCommand(LogNanomachinesCommand) - e.registerServerCommand(NonDisassemblyAgreementCommand) - e.registerServerCommand(WirelessRenderingCommand) - e.registerServerCommand(SpawnComputerCommand) - e.registerServerCommand(DebugWhitelistCommand) - e.registerServerCommand(SendDebugMessageCommand) - } -} diff --git a/src/main/scala/li/cil/oc/server/command/DebugNanomachinesCommand.scala b/src/main/scala/li/cil/oc/server/command/DebugNanomachinesCommand.scala deleted file mode 100644 index 5f7f1d972d..0000000000 --- a/src/main/scala/li/cil/oc/server/command/DebugNanomachinesCommand.scala +++ /dev/null @@ -1,37 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.api -import li.cil.oc.common.command.SimpleCommand -import li.cil.oc.common.nanomachines.ControllerImpl -import net.minecraft.command.ICommandSender -import net.minecraft.command.WrongUsageException -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.server.MinecraftServer -import net.minecraft.util.text.TextComponentString - -object DebugNanomachinesCommand extends SimpleCommand("oc_debugNanomachines") { - aliases += "oc_dn" - - override def getUsage(source: ICommandSender): String = name - - override def execute(server: MinecraftServer, source: ICommandSender, args: Array[String]): Unit = { - source match { - case player: EntityPlayer => - api.Nanomachines.installController(player) match { - case controller: ControllerImpl => - controller.debug() - player.sendMessage(new TextComponentString("Debug configuration created, see log for mappings.")) - case _ => // Someone did something. - } - case _ => throw new WrongUsageException("Can only be used by players.") - } - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/DebugWhitelistCommand.scala b/src/main/scala/li/cil/oc/server/command/DebugWhitelistCommand.scala deleted file mode 100644 index 3927c87617..0000000000 --- a/src/main/scala/li/cil/oc/server/command/DebugWhitelistCommand.scala +++ /dev/null @@ -1,54 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.Settings -import li.cil.oc.Settings.DebugCardAccess -import li.cil.oc.common.command.SimpleCommand -import net.minecraft.command.{ICommandSender, WrongUsageException} -import net.minecraft.server.MinecraftServer -import net.minecraft.util.text.TextComponentString - -object DebugWhitelistCommand extends SimpleCommand("oc_debugWhitelist") { - // Required OP levels: - // to revoke your cards - 0 - // to do other whitelist manipulation - 2 - - override def getRequiredPermissionLevel = 0 - private def isOp(sender: ICommandSender) = getOpLevel(sender) >= 2 - - override def getUsage(sender: ICommandSender): String = - if (isOp(sender)) name + " [revoke|add|remove] OR " + name + " [revoke|list]" - else name + " revoke" - - override def execute(server: MinecraftServer, sender: ICommandSender, args: Array[String]): Unit = { - val wl = Settings.get.debugCardAccess match { - case w: DebugCardAccess.Whitelist => w - case _ => throw new WrongUsageException("§cDebug card whitelisting is not enabled.") - } - - def revokeUser(player: String): Unit = { - if (wl.isWhitelisted(player)) { - wl.invalidate(player) - sender.sendMessage(new TextComponentString("§aAll your debug cards were invalidated.")) - } else sender.sendMessage(new TextComponentString("§cYou are not whitelisted to use debug card.")) - } - - args match { - case Array("revoke") => revokeUser(sender.getName) - case Array("revoke", player) if isOp(sender) => revokeUser(player) - case Array("list") if isOp(sender) => - val players = wl.whitelist - if (players.nonEmpty) - sender.sendMessage(new TextComponentString("§aCurrently whitelisted players: §e" + players.mkString(", "))) - else - sender.sendMessage(new TextComponentString("§cThere is no currently whitelisted players.")) - case Array("add", player) if isOp(sender) => - wl.add(player) - sender.sendMessage(new TextComponentString("§aPlayer was added to whitelist.")) - case Array("remove", player) if isOp(sender) => - wl.remove(player) - sender.sendMessage(new TextComponentString("§aPlayer was removed from whitelist")) - case _ => - sender.sendMessage(new TextComponentString("§e" + getUsage(sender))) - } - } -} diff --git a/src/main/scala/li/cil/oc/server/command/LogNanomachinesCommand.scala b/src/main/scala/li/cil/oc/server/command/LogNanomachinesCommand.scala deleted file mode 100644 index 8f859eb786..0000000000 --- a/src/main/scala/li/cil/oc/server/command/LogNanomachinesCommand.scala +++ /dev/null @@ -1,39 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.api -import li.cil.oc.common.command.SimpleCommand -import li.cil.oc.common.nanomachines.ControllerImpl -import net.minecraft.command.ICommandSender -import net.minecraft.command.WrongUsageException -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.server.MinecraftServer -import net.minecraftforge.fml.common.FMLCommonHandler - -object LogNanomachinesCommand extends SimpleCommand("oc_nanomachines") { - aliases += "oc_nm" - - override def getUsage(source: ICommandSender): String = name + " [player]" - - override def execute(server: MinecraftServer, source: ICommandSender, command: Array[String]): Unit = { - (if (command.length > 0) { - val player = command(0) - val config = FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList - config.getPlayerByUsername(player) - } else source) match { - case player: EntityPlayer => - api.Nanomachines.installController(player) match { - case controller: ControllerImpl => controller.print() - case _ => // Someone did something. - } - case _ => throw new WrongUsageException("Player entity not found.") - } - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/NonDisassemblyAgreementCommand.scala b/src/main/scala/li/cil/oc/server/command/NonDisassemblyAgreementCommand.scala deleted file mode 100644 index 8812711dd9..0000000000 --- a/src/main/scala/li/cil/oc/server/command/NonDisassemblyAgreementCommand.scala +++ /dev/null @@ -1,49 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.Settings -import li.cil.oc.common.command.SimpleCommand -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.command.WrongUsageException -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.server.MinecraftServer - -object NonDisassemblyAgreementCommand extends SimpleCommand("oc_preventDisassembling") { - aliases += "oc_nodis" - aliases += "oc_prevdis" - - override def getUsage(source: ICommandSender) = name + " " - - override def execute(server: MinecraftServer, source: ICommandSender, command: Array[String]): Unit = { - source match { - case player: EntityPlayer => - val stack = player.getHeldItemMainhand - if (!stack.isEmpty) { - if (!stack.hasTagCompound) { - stack.setTagCompound(new NBTTagCompound()) - } - val nbt = stack.getTagCompound - val preventDisassembly = - if (command != null && command.length > 0) - CommandBase.parseBoolean(command(0)) - else - !nbt.getBoolean(Settings.namespace + "undisassemblable") - if (preventDisassembly) - nbt.setBoolean(Settings.namespace + "undisassemblable", true) - else - nbt.removeTag(Settings.namespace + "undisassemblable") - if (nbt.hasNoTags) stack.setTagCompound(null) - } - case _ => throw new WrongUsageException("Can only be used by players.") - } - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala b/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala deleted file mode 100644 index f6e600bfae..0000000000 --- a/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala +++ /dev/null @@ -1,27 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.api.Network -import li.cil.oc.common.command.SimpleCommand -import li.cil.oc.server.network.DebugNetwork -import net.minecraft.command.ICommandSender -import net.minecraft.command.WrongUsageException -import net.minecraft.server.MinecraftServer - -object SendDebugMessageCommand extends SimpleCommand("oc_sendDebugMessage") { - aliases += "oc_sdbg" - - override def getUsage(sender: ICommandSender): String = name + " [message...]" - - override def execute(server: MinecraftServer, sender: ICommandSender, args: Array[String]): Unit = { - if (args == null || args.length == 0) { - throw new WrongUsageException("no destination address specified.") - } - val destination = args(0) - DebugNetwork.getEndpoint(destination).foreach { endpoint => - val packet = Network.newPacket(sender.getName, destination, 0, args.drop(1).toList.toArray[AnyRef]) - endpoint.receivePacket(packet) - } - } - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/SpawnComputerCommand.scala b/src/main/scala/li/cil/oc/server/command/SpawnComputerCommand.scala deleted file mode 100644 index d0738329bd..0000000000 --- a/src/main/scala/li/cil/oc/server/command/SpawnComputerCommand.scala +++ /dev/null @@ -1,101 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.Constants -import li.cil.oc.api -import li.cil.oc.common.command.SimpleCommand -import li.cil.oc.common.tileentity -import li.cil.oc.server.machine.luac.{LuaStateFactory, NativeLua53Architecture} -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.ExtendedWorld._ -import li.cil.oc.util.InventoryUtils -import net.minecraft.command.ICommandSender -import net.minecraft.command.WrongUsageException -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.server.MinecraftServer -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d -import net.minecraft.util.text.TextComponentString - -object SpawnComputerCommand extends SimpleCommand("oc_spawnComputer") { - aliases += "oc_sc" - - final val MaxDistance = 16 - - override def getUsage(source: ICommandSender): String = name - - override def execute(server: MinecraftServer, source: ICommandSender, command: Array[String]): Unit = { - source match { - case player: EntityPlayer => - val world = player.getEntityWorld - val origin = new Vec3d(player.posX, player.posY + player.getEyeHeight, player.posZ) - val direction = player.getLookVec - val lookAt = origin.addVector(direction.x * MaxDistance, direction.y * MaxDistance, direction.z * MaxDistance) - world.rayTraceBlocks(origin, lookAt) match { - case hit: RayTraceResult if hit.typeOfHit == RayTraceResult.Type.BLOCK => - val hitPos = BlockPosition(hit.getBlockPos, world) - val casePos = hitPos.offset(hit.sideHit) - val screenPos = casePos.offset(EnumFacing.UP) - val keyboardPos = screenPos.offset(EnumFacing.UP) - - if (!world.isAirBlock(casePos) || !world.isAirBlock(screenPos) || !world.isAirBlock(keyboardPos)) { - player.sendMessage(new TextComponentString("Target position obstructed.")) - return - } - - def rotateProperly(pos: BlockPosition):tileentity.traits.Rotatable = { - world.getTileEntity(pos) match { - case rotatable: tileentity.traits.Rotatable => - rotatable.setFromEntityPitchAndYaw(player) - if (!rotatable.validFacings.contains(rotatable.pitch)) { - rotatable.pitch = rotatable.validFacings.headOption.getOrElse(EnumFacing.NORTH) - } - rotatable.invertRotation() - rotatable - case _ => null // not rotatable - } - } - - world.setBlock(casePos, api.Items.get(Constants.BlockName.CaseCreative).block()) - rotateProperly(casePos) - world.setBlock(screenPos, api.Items.get(Constants.BlockName.ScreenTier2).block()) - rotateProperly(screenPos) match { - case rotatable: tileentity.traits.Rotatable => rotatable.pitch match { - case EnumFacing.UP | EnumFacing.DOWN => - rotatable.pitch = EnumFacing.NORTH - case _ => // nothing to do here, pitch is fine - } - case _ => // ??? - } - world.setBlock(keyboardPos, api.Items.get(Constants.BlockName.Keyboard).block()) - world.getTileEntity(keyboardPos) match { - case t: tileentity.traits.Rotatable => - t.setFromEntityPitchAndYaw(player) - t.setFromFacing(EnumFacing.UP) - case _ => // ??? - } - - api.Network.joinOrCreateNetwork(world.getTileEntity(casePos)) - - val apu = api.Items.get(Constants.ItemName.APUCreative).createItemStack(1) - LuaStateFactory.setDefaultArch(apu) - - InventoryUtils.insertIntoInventoryAt(apu, casePos) - InventoryUtils.insertIntoInventoryAt(api.Items.get(Constants.ItemName.RAMTier6).createItemStack(2), casePos) - InventoryUtils.insertIntoInventoryAt(api.Items.get(Constants.ItemName.HDDTier3).createItemStack(1), casePos) - InventoryUtils.insertIntoInventoryAt(api.Items.get(Constants.ItemName.LuaBios).createItemStack(1), casePos) - InventoryUtils.insertIntoInventoryAt(api.Items.get(Constants.ItemName.OpenOS).createItemStack(1), casePos) - case _ => player.sendMessage(new TextComponentString("You need to be looking at a nearby block.")) - } - case _ => throw new WrongUsageException("Can only be used by players.") - } - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/WirelessRenderingCommand.scala b/src/main/scala/li/cil/oc/server/command/WirelessRenderingCommand.scala deleted file mode 100644 index 22ff9226c4..0000000000 --- a/src/main/scala/li/cil/oc/server/command/WirelessRenderingCommand.scala +++ /dev/null @@ -1,29 +0,0 @@ -package li.cil.oc.server.command - -import li.cil.oc.Settings -import li.cil.oc.common.command.SimpleCommand -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.server.MinecraftServer - -object WirelessRenderingCommand extends SimpleCommand("oc_renderWirelessNetwork") { - aliases += "oc_wlan" - - override def getUsage(source: ICommandSender) = name + " " - - override def execute(server: MinecraftServer, source: ICommandSender, command: Array[String]): Unit = { - Settings.rTreeDebugRenderer = - if (command != null && command.length > 0) - CommandBase.parseBoolean(command(0)) - else - !Settings.rTreeDebugRenderer - } - - // OP levels for reference: - // 1 - Ops can bypass spawn protection. - // 2 - Ops can use /clear, /difficulty, /effect, /gamemode, /gamerule, /give, /summon, /setblock and /tp, and can edit command blocks. - // 3 - Ops can use /ban, /deop, /kick, and /op. - // 4 - Ops can use /stop. - - override def getRequiredPermissionLevel = 2 -} diff --git a/src/main/scala/li/cil/oc/server/command/package.scala b/src/main/scala/li/cil/oc/server/command/package.scala deleted file mode 100644 index da29b81294..0000000000 --- a/src/main/scala/li/cil/oc/server/command/package.scala +++ /dev/null @@ -1,31 +0,0 @@ -package li.cil.oc.server - -import net.minecraft.command.ICommandSender -import net.minecraft.entity.player.EntityPlayerMP -import net.minecraft.server.MinecraftServer -import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.TextComponentString -import net.minecraftforge.fml.common.FMLCommonHandler - -import scala.language.implicitConversions - -package object command { - implicit def string2text(s: String): ITextComponent = new TextComponentString(s) - - def getOpLevel(sender: ICommandSender): Int = { - // Shitty minecraft server logic & shitty minecraft server code. - val srv = FMLCommonHandler.instance().getMinecraftServerInstance - if (srv.isSinglePlayer && srv.worlds.head.getWorldInfo.areCommandsAllowed && - srv.getServerOwner.equalsIgnoreCase(sender.getName) /* || srv.commandsAllowedForAll */ ) - return 4 - - sender match { - case _: MinecraftServer => 4 - case p: EntityPlayerMP => - - val e = srv.getPlayerList.getOppedPlayers.getEntry(p.getGameProfile) - if (e == null) 0 else e.getPermissionLevel - case _ => 0 - } - } -} diff --git a/src/main/scala/li/cil/oc/server/component/Agent.scala b/src/main/scala/li/cil/oc/server/component/Agent.scala index ad49f74009..9bf06d0642 100644 --- a/src/main/scala/li/cil/oc/server/component/Agent.scala +++ b/src/main/scala/li/cil/oc/server/component/Agent.scala @@ -15,18 +15,22 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.InventoryUtils import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState +import net.minecraft.block.BlockState import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.item.EntityMinecart -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.Pose +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.item.minecart.MinecartEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory -import net.minecraft.util.EnumActionResult -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.util.math.EntityRayTraceResult +import net.minecraft.util.math.RayTraceContext import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.common.MinecraftForge import scala.collection.convert.WrapAsScala._ @@ -36,13 +40,13 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits override def position = BlockPosition(agent) - override def fakePlayer: EntityPlayer = agent.player + override def fakePlayer: PlayerEntity = agent.player - protected def rotatedPlayer(facing: EnumFacing = agent.facing, side: EnumFacing = agent.facing): Player = { + protected def rotatedPlayer(facing: Direction = agent.facing, side: Direction = agent.facing): Player = { val player = agent.player.asInstanceOf[Player] Player.updatePositionAndRotation(player, facing, side) // no need to set inventory, calling agent.Player already did that - //Player.setInventoryPlayerItems(player) + //Player.setPlayerInventoryItems(player) player } @@ -89,7 +93,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } else { // Always try the direction we're looking first. - Iterable(facing) ++ EnumFacing.values.filter(side => side != facing && side != facing.getOpposite).toIterable + Iterable(facing) ++ Direction.values.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) @@ -98,12 +102,12 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } def attack(player: Player, entity: Entity) = { beginConsumeDrops(entity) - player.attackTargetEntityWithCurrentItem(entity) + player.attack(entity) // Mine carts have to be hit quickly in succession to break, so we click // until it breaks. But avoid an infinite loop... you never know. entity match { - case _: EntityMinecart => for (_ <- 0 until 10 if !entity.isDead) { - player.attackTargetEntityWithCurrentItem(entity) + case _: MinecartEntity => for (_ <- 0 until 10 if entity.isAlive) { + player.attack(entity) } case _ => } @@ -111,7 +115,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits triggerDelay() (true, "entity") } - def click(player: Player, pos: BlockPos, side: EnumFacing) = { + def click(player: Player, pos: BlockPos, side: Direction) = { val breakTime = player.clickBlock(pos, side) val broke = breakTime > 0 if (broke) { @@ -123,21 +127,22 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits var reason: Option[String] = None for (side <- sides) { val player = rotatedPlayer(facing, side) - player.setSneaking(sneaky) + player.setPose(if (sneaky) Pose.CROUCHING else Pose.STANDING) val (success, what) = { val hit = pick(player, Settings.get.swingRange) (Option(hit) match { - case Some(info) => info.typeOfHit + case Some(info) => info.getType case _ => RayTraceResult.Type.MISS }) match { case RayTraceResult.Type.ENTITY => - attack(player, hit.entityHit) + attack(player, hit.asInstanceOf[EntityRayTraceResult].getEntity) case RayTraceResult.Type.BLOCK => - click(player, hit.getBlockPos, hit.sideHit) + val blockHit = hit.asInstanceOf[BlockRayTraceResult] + click(player, blockHit.getBlockPos, blockHit.getDirection) case _ => // Retry with full block bounds, disregarding swing range. - player.closestEntity(classOf[EntityLivingBase]) match { + player.closestEntity(classOf[LivingEntity]) match { case Some(entity) => attack(player, entity) case _ => @@ -150,7 +155,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } } - player.setSneaking(false) + player.setPose(Pose.STANDING) if (success) { return result(true, what) } @@ -162,9 +167,9 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits if (hasBlock) { val blockPos = position.offset(facing) val player = rotatedPlayer(facing, facing) - player.setSneaking(sneaky) + player.setPose(if (sneaky) Pose.CROUCHING else Pose.STANDING) val (ok, why) = click(player, blockPos.toBlockPos, facing) - player.setSneaking(false) + player.setPose(Pose.STANDING) return result(ok, why) } @@ -180,7 +185,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } else { // Always try the direction we're looking first. - Iterable(facing) ++ EnumFacing.values.filter(side => side != facing && side != facing.getOpposite).toIterable + Iterable(facing) ++ Direction.values.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) val duration = @@ -205,22 +210,23 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } def interact(player: Player, entity: Entity) = { beginConsumeDrops(entity) - val result = player.interactOn(entity, EnumHand.MAIN_HAND) + val result = player.interactOn(entity, Hand.MAIN_HAND) endConsumeDrops(player, entity) result } for (side <- sides) { val player = rotatedPlayer(facing, side) - player.setSneaking(sneaky) + player.setPose(if (sneaky) Pose.CROUCHING else Pose.STANDING) val (success, what) = Option(pick(player, Settings.get.useAndPlaceRange)) match { - case Some(hit) if hit.typeOfHit == RayTraceResult.Type.ENTITY && interact(player, hit.entityHit) == EnumActionResult.SUCCESS => + case Some(hit) if hit.getType == RayTraceResult.Type.ENTITY && interact(player, hit.asInstanceOf[EntityRayTraceResult].getEntity).consumesAction => triggerDelay() (true, "item_interacted") - case Some(hit) if hit.typeOfHit == RayTraceResult.Type.BLOCK => - val (blockPos, hx, hy, hz) = clickParamsFromHit(hit) - activationResult(player.activateBlockOrUseItem(blockPos, hit.sideHit, hx, hy, hz, duration)) + case Some(hit) if hit.getType == RayTraceResult.Type.BLOCK => + val blockHit = hit.asInstanceOf[BlockRayTraceResult] + val (blockPos, hx, hy, hz) = clickParamsFromHit(blockHit) + activationResult(player.activateBlockOrUseItem(blockPos, blockHit.getDirection, hx, hy, hz, duration)) case _ => (if (canPlaceInAir) { val (blockPos, hx, hy, hz) = clickParamsForPlace(facing) @@ -241,7 +247,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } } - player.setSneaking(false) + player.setPose(Pose.STANDING) if (success) { return result(true, what) } @@ -259,41 +265,42 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits } else { // Always try the direction we're looking first. - Iterable(facing) ++ EnumFacing.values.filter(side => side != facing && side != facing.getOpposite).toIterable + Iterable(facing) ++ Direction.values.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) - val stack = agent.mainInventory.getStackInSlot(agent.selectedSlot) + val stack = agent.mainInventory.getItem(agent.selectedSlot) if (stack.isEmpty) { return result(Unit, "nothing selected") } for (side <- sides) { val player = rotatedPlayer(facing, side) - player.setSneaking(sneaky) + player.setPose(if (sneaky) Pose.CROUCHING else Pose.STANDING) val success = Option(pick(player, Settings.get.useAndPlaceRange)) match { - case Some(hit) if hit.typeOfHit == RayTraceResult.Type.BLOCK => - val (blockPos, hx, hy, hz) = clickParamsFromHit(hit) - player.placeBlock(agent.selectedSlot, blockPos, hit.sideHit, hx, hy, hz) + case Some(hit) if hit.getType == RayTraceResult.Type.BLOCK => + val blockHit = hit.asInstanceOf[BlockRayTraceResult] + val (blockPos, hx, hy, hz) = clickParamsFromHit(blockHit) + player.placeBlock(agent.selectedSlot, blockPos, blockHit.getDirection, hx, hy, hz) case None if canPlaceInAir && player.closestEntity(classOf[Entity]).isEmpty => val (blockPos, hx, hy, hz) = clickParamsForPlace(facing) // blockPos here is the position of the agent - // When a robot uses angel placement, the ItemBlock code offsets the pos to EnumFacing + // When a robot uses angel placement, the BlockItem code offsets the pos to Direction // but for a drone, the block at its position is air, which is replaceable, and thus - // ItemBlock does not offset the position. We can do it here to correct that, and the code + // BlockItem does not offset the position. We can do it here to correct that, and the code // here is still correct for the robot's use case - val adjustedPos: BlockPos = blockPos.offset(facing) + val adjustedPos: BlockPos = blockPos.relative(facing) // adjustedPos is the position we want to place the block // but onItemUse will try to adjust the placement if the target position is not replaceable // we don't want that - val block: Block = world.getBlockState(adjustedPos).getBlock - if (block.isReplaceable(world, adjustedPos)) { + val state: BlockState = world.getBlockState(adjustedPos) + if (state.getMaterial.isReplaceable) { player.placeBlock(agent.selectedSlot, adjustedPos, facing, hx, hy, hz) } else { false } case _ => false } - player.setSneaking(false) + player.setPose(Pose.STANDING) if (success) { onWorldInteraction(context, Settings.get.placeDelay) return result(true) @@ -306,70 +313,69 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits // ----------------------------------------------------------------------- // protected def beginConsumeDrops(entity: Entity) { - entity.captureDrops = true + entity.captureDrops(new java.util.ArrayList[ItemEntity]()) } protected def endConsumeDrops(player: Player, entity: Entity) { - entity.captureDrops = false + val captured = entity.captureDrops(null) // this inventory size check is a HACK to preserve old behavior that a agent can suck items out // of the capturedDrops. Ideally, we'd only pick up items off the ground. We could clear the - // capturedDrops when Player.attackTargetEntityWithCurrentItem() is called + // capturedDrops when Player.attack() is called // But this felt slightly less hacky, slightly - if (player.inventory.getSizeInventory > 0) { - for (drop <- entity.capturedDrops) { - if (!drop.isDead) { + if (player.inventory.getContainerSize > 0) { + for (drop <- captured) { + if (drop.isAlive) { val stack = drop.getItem InventoryUtils.addToPlayerInventory(stack, player, spawnInWorld = false) } } } - entity.capturedDrops.clear() } // ----------------------------------------------------------------------- // - protected def checkSideForFace(args: Arguments, n: Int, facing: EnumFacing): EnumFacing = agent.toGlobal(args.checkSideForFace(n, agent.toLocal(facing))) + protected def checkSideForFace(args: Arguments, n: Int, facing: Direction): Direction = agent.toGlobal(args.checkSideForFace(n, agent.toLocal(facing))) protected def pick(player: Player, range: Double): RayTraceResult = { - val origin = new Vec3d( - player.posX + player.facing.getFrontOffsetX * 0.5, - player.posY + player.facing.getFrontOffsetY * 0.5, - player.posZ + player.facing.getFrontOffsetZ * 0.5) - val blockCenter = origin.addVector( - player.facing.getFrontOffsetX * 0.51, - player.facing.getFrontOffsetY * 0.51, - player.facing.getFrontOffsetZ * 0.51) - val target = blockCenter.addVector( - player.side.getFrontOffsetX * range, - player.side.getFrontOffsetY * range, - player.side.getFrontOffsetZ * range) - val hit = world.rayTraceBlocks(origin, target) + val origin = new Vector3d( + player.getX + player.facing.getStepX * 0.5, + player.getY + player.facing.getStepY * 0.5, + player.getZ + player.facing.getStepZ * 0.5) + val blockCenter = origin.add( + player.facing.getStepX * 0.51, + player.facing.getStepY * 0.51, + player.facing.getStepZ * 0.51) + val target = blockCenter.add( + player.side.getStepX * range, + player.side.getStepY * range, + player.side.getStepZ * range) + val hit = world.clip(new RayTraceContext(origin, target, RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.ANY, player)) player.closestEntity(classOf[Entity]) match { - case Some(entity@(_: EntityLivingBase | _: EntityMinecart | _: entity.Drone)) if hit == null || new Vec3d(player.posX, player.posY, player.posZ).distanceTo(hit.hitVec) > player.getDistance(entity) => new RayTraceResult(entity) + case Some(entity@(_: LivingEntity | _: MinecartEntity | _: entity.Drone)) if hit.getType == RayTraceResult.Type.MISS || player.distanceToSqr(hit.getLocation) > player.distanceToSqr(entity) => new EntityRayTraceResult(entity) case _ => hit } } - protected def clickParamsFromHit(hit: RayTraceResult): (BlockPos, Float, Float, Float) = { + protected def clickParamsFromHit(hit: BlockRayTraceResult): (BlockPos, Float, Float, Float) = { (hit.getBlockPos, - (hit.hitVec.x - hit.getBlockPos.getX).toFloat, - (hit.hitVec.y - hit.getBlockPos.getY).toFloat, - (hit.hitVec.z - hit.getBlockPos.getZ).toFloat) + (hit.getLocation.x - hit.getBlockPos.getX).toFloat, + (hit.getLocation.y - hit.getBlockPos.getY).toFloat, + (hit.getLocation.z - hit.getBlockPos.getZ).toFloat) } - protected def clickParamsForItemUse(facing: EnumFacing, side: EnumFacing): (BlockPos, Float, Float, Float) = { + protected def clickParamsForItemUse(facing: Direction, side: Direction): (BlockPos, Float, Float, Float) = { val blockPos = position.offset(facing).offset(side) (blockPos.toBlockPos, - 0.5f - side.getFrontOffsetX * 0.5f, - 0.5f - side.getFrontOffsetY * 0.5f, - 0.5f - side.getFrontOffsetZ * 0.5f) + 0.5f - side.getStepX * 0.5f, + 0.5f - side.getStepY * 0.5f, + 0.5f - side.getStepZ * 0.5f) } - protected def clickParamsForPlace(facing: EnumFacing): (BlockPos, Float, Float, Float) = { + protected def clickParamsForPlace(facing: Direction): (BlockPos, Float, Float, Float) = { (position.toBlockPos, - 0.5f + facing.getFrontOffsetX * 0.5f, - 0.5f + facing.getFrontOffsetY * 0.5f, - 0.5f + facing.getFrontOffsetZ * 0.5f) + 0.5f + facing.getStepX * 0.5f, + 0.5f + facing.getStepY * 0.5f, + 0.5f + facing.getStepZ * 0.5f) } } diff --git a/src/main/scala/li/cil/oc/server/component/DataCard.scala b/src/main/scala/li/cil/oc/server/component/DataCard.scala index d7388906de..6a14ee4e51 100644 --- a/src/main/scala/li/cil/oc/server/component/DataCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DataCard.scala @@ -25,7 +25,7 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import org.apache.commons.codec.binary.Base64 import org.apache.commons.io.output.ByteArrayOutputStream @@ -328,15 +328,15 @@ object DataCard { private final val TypeTag = "Type" private final val DataTag = "Data" - override def load(nbt: NBTTagCompound): Unit = { + override def loadData(nbt: CompoundNBT): Unit = { val keyType = nbt.getString(TypeTag) val data = nbt.getByteArray(DataTag) value = ECUserdata.deserializeKey(keyType, data) } - override def save(nbt: NBTTagCompound): Unit = { - nbt.setString(TypeTag, keyType) - nbt.setByteArray(DataTag, value.getEncoded) + override def saveData(nbt: CompoundNBT): Unit = { + nbt.putString(TypeTag, keyType) + nbt.putByteArray(DataTag, value.getEncoded) } } diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index bc5c7dba73..50b26191a1 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -1,5 +1,8 @@ package li.cil.oc.server.component +import java.util.UUID +import java.util.function.Supplier + import com.google.common.base.Strings import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -20,7 +23,7 @@ import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.server.PacketSender import li.cil.oc.server.network.DebugNetwork import li.cil.oc.server.network.DebugNetwork.DebugNode -import li.cil.oc.server.component.DebugCard.{AccessContext, CommandSender} +import li.cil.oc.server.component.DebugCard.AccessContext import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedBlock._ @@ -28,33 +31,43 @@ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.InventoryUtils import net.minecraft.block.Block -import net.minecraft.command.CommandResultStats.Type -import net.minecraft.entity.item.EntityMinecart -import net.minecraft.entity.{Entity, EntityLivingBase} -import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.command.CommandSource +import net.minecraft.command.ICommandSource +import net.minecraft.entity.item.minecart.MinecartEntity +import net.minecraft.entity.{Entity, LivingEntity} +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt._ -import net.minecraft.scoreboard.{IScoreCriteria, Scoreboard} -import net.minecraft.server.management.UserListOpsEntry +import net.minecraft.scoreboard.{ScoreCriteria, Scoreboard} +import net.minecraft.server.MinecraftServer import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation +import net.minecraft.util.RegistryKey import net.minecraft.util.SoundCategory import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.vector.Vector2f +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.registry.Registry import net.minecraft.util.text.ITextComponent -import net.minecraft.world.{GameType, World, WorldServer, WorldSettings} -import net.minecraftforge.common.{DimensionManager, MinecraftForge} +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.{GameType, World, WorldSettings} +import net.minecraft.world.server.ServerWorld +import net.minecraft.world.storage.IServerWorldInfo +import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.event.world.BlockEvent -import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.IFluidBlock import net.minecraftforge.fluids.capability.IFluidHandler -import net.minecraftforge.fml.common.FMLCommonHandler -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.ModAPIManager +import net.minecraftforge.fml.ModList +import net.minecraftforge.fml.server.ServerLifecycleHooks +import net.minecraftforge.registries.ForgeRegistries +import net.minecraftforge.registries.ForgeRegistry +import net.minecraftforge.registries.IForgeRegistry import scala.collection.convert.WrapAsScala._ import scala.collection.mutable @@ -76,12 +89,30 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D def player: Option[String] = access.map(_.player) - private lazy val CommandSender = { - def defaultFakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) - new CommandSender(host, player match { - case Some(name) => Option(FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList.getPlayerByUsername(name)).getOrElse(defaultFakePlayer) + private var CommandMessages: Option[String] = None + + private def createCommandSourceStack(): CommandSource = { + val sender = new ICommandSource { + override def sendMessage(message: ITextComponent, sender: UUID) { + CommandMessages = Option(CommandMessages.fold("")(_ + "\n") + message.getString) + } + + override def acceptsSuccess = true + + override def acceptsFailure = true + + override def shouldInformAdmins = true + } + val world = host.world.asInstanceOf[ServerWorld] + val server = world.getServer + def defaultFakePlayer = FakePlayerFactory.get(world, Settings.get.fakePlayerProfile) + val sourcePlayer = player match { + case Some(name) => Option(server.getPlayerList.getPlayerByName(name)).getOrElse(defaultFakePlayer) case _ => defaultFakePlayer - }) + } + val permLevel = server.getProfilePermissions(sourcePlayer.getGameProfile) + new CommandSource(sender, new Vector3d(host.xPosition, host.yPosition, host.zPosition), Vector2f.ZERO, world, + permLevel, sourcePlayer.getName.getString, sourcePlayer.getDisplayName, server, sourcePlayer) } // ----------------------------------------------------------------------- // @@ -112,17 +143,28 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D result(host.zPosition) } + @Deprecated @Callback(doc = """function([id:number]):userdata -- Get the world object for the specified dimension ID, or the container's.""") def getWorld(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - if (args.count() > 0) result(new DebugCard.WorldValue(DimensionManager.getWorld(args.checkInteger(0)))) + if (args.count() > 0) { + val server = ServerLifecycleHooks.getCurrentServer + val world = args.checkInteger(0) match { + case 0 => server.overworld + case -1 => server.getLevel(World.NETHER) + case 1 => server.getLevel(World.END) + case _ => null + } + result(new DebugCard.WorldValue(world)) + } else result(new DebugCard.WorldValue(host.world)) } + @Deprecated @Callback(doc = """function():table -- Get a list of all world IDs, loaded and unloaded.""") def getWorlds(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(DimensionManager.getStaticDimensionIDs) + result(Array[Int](0, -1, 1)) } @Callback(doc = """function(name:string):userdata -- Get the entity of a player.""") @@ -134,7 +176,7 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D @Callback(doc = """function():table -- Get a list of currently logged-in players.""") def getPlayers(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(FMLCommonHandler.instance.getMinecraftServerInstance.getOnlinePlayerNames) + result(ServerLifecycleHooks.getCurrentServer.getPlayerNames) } @Callback(doc = """function():userdata -- Get the scoreboard object for the world""") @@ -144,41 +186,46 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D } + @Deprecated @Callback(doc = "function(x: number, y: number, z: number[, worldId: number]):boolean, string, table -- returns contents at the location in world by id (default host world)") def scanContentsAt(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val x = args.checkInteger(0) val y = args.checkInteger(1) val z = args.checkInteger(2) - val worldServer = if (args.count() > 3) DimensionManager.getWorld(args.checkInteger(3)) else host.world - val world = worldServer.world + val server = ServerLifecycleHooks.getCurrentServer + val world = if (args.count() > 3) args.checkInteger(3) match { + case 0 => server.overworld + case -1 => server.getLevel(World.NETHER) + case 1 => server.getLevel(World.END) + case _ => null + } else host.world val position: BlockPosition = new BlockPosition(x, y, z, Option(world)) - val fakePlayer = FakePlayerFactory.get(world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) - fakePlayer.posX = position.x + 0.5 - fakePlayer.posY = position.y + 0.5 - fakePlayer.posZ = position.z + 0.5 - - Option(world.findNearestEntityWithinAABB(classOf[Entity], position.bounds, fakePlayer)) match { - case Some(living: EntityLivingBase) => result(true, "EntityLivingBase", living) - case Some(minecart: EntityMinecart) => result(true, "EntityMinecart", minecart) + val fakePlayer = FakePlayerFactory.get(world.asInstanceOf[ServerWorld], Settings.get.fakePlayerProfile) + fakePlayer.setPos(position.x + 0.5, position.y + 0.5, position.z + 0.5) + + val candidates = world.getEntitiesOfClass(classOf[Entity], position.bounds, null) + (if (!candidates.isEmpty) Some(candidates.minBy(fakePlayer.distanceToSqr(_))) else None) match { + case Some(living: LivingEntity) => result(true, "EntityLiving", living) + case Some(minecart: MinecartEntity) => result(true, "EntityMinecart", minecart) case _ => - val block = world.getBlock(position) - val metadata = world.getBlockMetadata(position) - if (block.isAir(position)) { + val state = world.getBlockState(position.toBlockPos) + val block = state.getBlock + if (block.isAir(state, world, position.toBlockPos)) { result(false, "air", block) } - else if (FluidRegistry.lookupFluidForBlock(block) != null) { - val event = new BlockEvent.BreakEvent(world, position.toBlockPos, metadata, fakePlayer) + else if (!block.isInstanceOf[IFluidBlock]) { + val event = new BlockEvent.BreakEvent(world, position.toBlockPos, state, fakePlayer) MinecraftForge.EVENT_BUS.post(event) result(event.isCanceled, "liquid", block) } else if (block.isReplaceable(position)) { - val event = new BlockEvent.BreakEvent(world, position.toBlockPos, metadata, fakePlayer) + val event = new BlockEvent.BreakEvent(world, position.toBlockPos, state, fakePlayer) MinecraftForge.EVENT_BUS.post(event) result(event.isCanceled, "replaceable", block) } - else if (block.getCollisionBoundingBoxFromPool(position) == null) { + else if (state.getCollisionShape(world, position.toBlockPos, ISelectionContext.empty).isEmpty) { result(true, "passable", block) } else { @@ -191,7 +238,7 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D def isModLoaded(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val name = args.checkString(0) - result(Loader.isModLoaded(name) || ModAPIManager.INSTANCE.hasAPI(name)) + result(ModList.get.isLoaded(name)) } @Callback(doc = """function(command:string):number -- Runs an arbitrary command using a fake player.""") @@ -201,13 +248,14 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D if (args.isTable(0)) collectionAsScalaIterable(args.checkTable(0).values()) else Iterable(args.checkString(0)) - CommandSender.synchronized { - CommandSender.prepare() + val source = createCommandSourceStack + CommandMessages.synchronized { + CommandMessages = None var value = 0 for (command <- commands) { - value = FMLCommonHandler.instance.getMinecraftServerInstance.getCommandManager.executeCommand(CommandSender, command.toString) + value = ServerLifecycleHooks.getCurrentServer.getCommands.performCommand(source, command.toString) } - result(value, CommandSender.messages.orNull) + result(value, CommandMessages.orNull) } } @@ -231,8 +279,8 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D private def findNode(position: BlockPosition) = if (host.world.blockExists(position)) { - host.world.getTileEntity(position) match { - case env: SidedEnvironment => EnumFacing.values.map(env.sidedNode).find(_ != null) + host.world.getBlockEntity(position) match { + case env: SidedEnvironment => Direction.values.map(env.sidedNode).find(_ != null) case env: Environment => Option(env.node) case _ => None } @@ -255,7 +303,7 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D @Callback(doc = """function(player:string, text:string) -- Sends text to the specified player's clipboard if possible.""") def sendToClipboard(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - Option(FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList.getPlayerByUsername(args.checkString(0))) match { + Option(ServerLifecycleHooks.getCurrentServer.getPlayerList.getPlayerByName(args.checkString(0))) match { case Some(player) => PacketSender.sendClipboard(player, args.checkString(1)) result(true) @@ -313,25 +361,25 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound): Unit = { - super.load(nbt) - access = AccessContext.load(nbt) - if (nbt.hasKey(Settings.namespace + "remoteX")) { - val x = nbt.getInteger(Settings.namespace + "remoteX") - val y = nbt.getInteger(Settings.namespace + "remoteY") - val z = nbt.getInteger(Settings.namespace + "remoteZ") + override def loadData(nbt: CompoundNBT): Unit = { + super.loadData(nbt) + access = AccessContext.loadData(nbt) + if (nbt.contains(Settings.namespace + "remoteX")) { + val x = nbt.getInt(Settings.namespace + "remoteX") + val y = nbt.getInt(Settings.namespace + "remoteY") + val z = nbt.getInt(Settings.namespace + "remoteZ") remoteNodePosition = Some((x, y, z)) } } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) - access.foreach(_.save(nbt)) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) + access.foreach(_.saveData(nbt)) remoteNodePosition.foreach { case (x, y, z) => - nbt.setInteger(Settings.namespace + "remoteX", x) - nbt.setInteger(Settings.namespace + "remoteY", y) - nbt.setInteger(Settings.namespace + "remoteZ", z) + nbt.putInt(Settings.namespace + "remoteX", x) + nbt.putInt(Settings.namespace + "remoteY", y) + nbt.putInt(Settings.namespace + "remoteZ", z) } } } @@ -342,13 +390,13 @@ object DebugCard { throw new Exception(msg) object AccessContext { - def remove(nbt: NBTTagCompound): Unit = { - nbt.removeTag(Settings.namespace + "player") - nbt.removeTag(Settings.namespace + "accessNonce") + def remove(nbt: CompoundNBT): Unit = { + nbt.remove(Settings.namespace + "player") + nbt.remove(Settings.namespace + "accessNonce") } - def load(nbt: NBTTagCompound): Option[AccessContext] = { - if (nbt.hasKey(Settings.namespace + "player")) + def loadData(nbt: CompoundNBT): Option[AccessContext] = { + if (nbt.contains(Settings.namespace + "player")) Some(AccessContext( nbt.getString(Settings.namespace + "player"), nbt.getString(Settings.namespace + "accessNonce") @@ -359,9 +407,9 @@ object DebugCard { } case class AccessContext(player: String, nonce: String) { - def save(nbt: NBTTagCompound): Unit = { - nbt.setString(Settings.namespace + "player", player) - nbt.setString(Settings.namespace + "accessNonce", nonce) + def saveData(nbt: CompoundNBT): Unit = { + nbt.putString(Settings.namespace + "player", player) + nbt.putString(Settings.namespace + "accessNonce", nonce) } } @@ -370,39 +418,39 @@ object DebugCard { // ----------------------------------------------------------------------- // - def withPlayer(f: (EntityPlayerMP) => Array[AnyRef]): Array[AnyRef] = { + def withPlayer(f: (ServerPlayerEntity) => Array[AnyRef]): Array[AnyRef] = { checkAccess() - FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList.getPlayerByUsername(name) match { - case player: EntityPlayerMP => f(player) + ServerLifecycleHooks.getCurrentServer.getPlayerList.getPlayerByName(name) match { + case player: ServerPlayerEntity => f(player) case _ => result(Unit, "player is offline") } } @Callback(doc = """function():userdata -- Get the player's world object.""") def getWorld(context: Context, args: Arguments): Array[AnyRef] = { - withPlayer(player => result(new DebugCard.WorldValue(player.getEntityWorld))) + withPlayer(player => result(new DebugCard.WorldValue(player.level))) } @Callback(doc = """function():string -- Get the player's game type.""") def getGameType(context: Context, args: Arguments): Array[AnyRef] = - withPlayer(player => result(player.interactionManager.getGameType.getName)) + withPlayer(player => result(player.gameMode.getGameModeForPlayer.getName)) @Callback(doc = """function(gametype:string) -- Set the player's game type (survival, creative, adventure).""") def setGameType(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { val gametype = args.checkString(0) - player.setGameType(GameType.values.find(_.getName == gametype).getOrElse(GameType.SURVIVAL)) + player.gameMode.updateGameMode(GameType.byName(gametype, GameType.SURVIVAL)) null }) @Callback(doc = """function():number, number, number -- Get the player's position.""") def getPosition(context: Context, args: Arguments): Array[AnyRef] = - withPlayer(player => result(player.posX, player.posY, player.posZ)) + withPlayer(player => result(player.getX, player.getY, player.getZ)) @Callback(doc = """function(x:number, y:number, z:number) -- Set the player's position.""") def setPosition(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { - player.setPositionAndUpdate(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2)) + player.teleportTo(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2)) null }) @@ -427,42 +475,43 @@ object DebugCard { @Callback(doc = """function():number -- Get the player's total experience""") def getExperienceTotal(context: Context, args: Arguments): Array[AnyRef] = - withPlayer(player => result(player.experienceTotal)) + withPlayer(player => result(player.totalExperience)) @Callback(doc = """function(level:number) -- Add a level to the player's experience level""") def addExperienceLevel(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { - player.addExperienceLevel(args.checkInteger(0)) + player.giveExperienceLevels(args.checkInteger(0)) null }) @Callback(doc = """function(level:number) -- Remove a level from the player's experience level""") def removeExperienceLevel(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { - player.addExperienceLevel(-args.checkInteger(0)) + player.giveExperienceLevels(-args.checkInteger(0)) null }) @Callback(doc = """function() -- Clear the players inventory""") def clearInventory(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { - player.inventory.clear() + player.inventory.clearContent() null }) + @Deprecated @Callback(doc = """function(id:string, amount:number, meta:number[, nbt:string]):number -- Adds the item stack to the players inventory""") def insertItem(context: Context, args: Arguments): Array[AnyRef] = withPlayer(player => { - val item = Item.REGISTRY.getObject(new ResourceLocation(args.checkString(0))) + val item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(args.checkString(0))) if (item == null) { throw new IllegalArgumentException("invalid item id") } val amount = args.checkInteger(1) - val meta = args.checkInteger(2) + args.checkInteger(2) // meta val tagJson = args.checkString(3) - val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.getTagFromJson(tagJson) - val stack = new ItemStack(item, amount, meta) - stack.setTagCompound(tag) + val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.parseTag(tagJson) + val stack = new ItemStack(item, amount) + stack.setTag(tag) result(InventoryUtils.addToPlayerInventory(stack, player)) }) @@ -470,22 +519,22 @@ object DebugCard { private final val NameTag = "name" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - ctx = AccessContext.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + ctx = AccessContext.loadData(nbt) name = nbt.getString(NameTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - ctx.foreach(_.save(nbt)) - nbt.setString(NameTag, name) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + ctx.foreach(_.saveData(nbt)) + nbt.putString(NameTag, name) } } class ScoreboardValue(world: Option[World])(implicit var ctx: Option[AccessContext]) extends prefab.AbstractValue { var scoreboard: Scoreboard = world.fold(null: Scoreboard)(_.getScoreboard) - var dimension: Int = world.fold(0)(_.provider.getDimension) + var dimension: ResourceLocation = world.fold(World.OVERWORLD)(_.dimension).location def this() = this(None)(None) // For loading. @@ -493,7 +542,7 @@ object DebugCard { def addTeam(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val team = args.checkString(0) - scoreboard.createTeam(team) + scoreboard.addPlayerTeam(team) null } @@ -501,8 +550,8 @@ object DebugCard { def removeTeam(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val teamName = args.checkString(0) - val team = scoreboard.getTeam(teamName) - scoreboard.removeTeam(team) + val team = scoreboard.getPlayersTeam(teamName) + scoreboard.removePlayerTeam(team) null } @@ -510,7 +559,8 @@ object DebugCard { def addPlayerToTeam(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val player = args.checkString(0) - val team = args.checkString(1) + val teamName = args.checkString(1) + val team = scoreboard.getPlayersTeam(teamName) result(scoreboard.addPlayerToTeam(player, team)) } @@ -518,7 +568,7 @@ object DebugCard { def removePlayerFromTeams(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val player = args.checkString(0) - result(scoreboard.removePlayerFromTeams(player)) + result(scoreboard.removePlayerFromTeam(player)) } @Callback(doc = """function(player:string, team:string):boolean - Remove a player from a specific team""") @@ -527,7 +577,7 @@ object DebugCard { checkAccess() val player = args.checkString(0) val teamName = args.checkString(1) - val team = scoreboard.getTeam(teamName) + val team = scoreboard.getPlayersTeam(teamName) scoreboard.removePlayerFromTeam(player, team) null } @@ -537,8 +587,10 @@ object DebugCard { checkAccess() val objName = args.checkString(0) val objType = args.checkString(1) - val criteria = IScoreCriteria.INSTANCES.get(objType) - scoreboard.addScoreObjective(objName, criteria) + val criteria = ScoreCriteria.byName(objType).orElseThrow(new Supplier[IllegalArgumentException] { + override def get = new IllegalArgumentException("invalid criterion") + }) + scoreboard.addObjective(objName, criteria, new StringTextComponent(objName), ScoreCriteria.RenderType.INTEGER) null } @@ -557,8 +609,8 @@ object DebugCard { val name = args.checkString(0) val objective = scoreboard.getObjective(args.checkString(1)) val scoreVal = args.checkInteger(2) - val score = scoreboard.getOrCreateScore(name,objective) - score.setScorePoints(scoreVal) + val score = scoreboard.getOrCreatePlayerScore(name,objective) + score.setScore(scoreVal) null } @@ -567,8 +619,8 @@ object DebugCard { checkAccess() val name = args.checkString(0) val objective = scoreboard.getObjective(args.checkString(1)) - val score = scoreboard.getOrCreateScore(name, objective) - result(score.getScorePoints) + val score = scoreboard.getOrCreatePlayerScore(name, objective) + result(score.getScore) } @Callback(doc = """function(playerName:string, objectiveName:string, score:int) - Increases the score of a player for a certain objective""") @@ -577,8 +629,8 @@ object DebugCard { val name = args.checkString(0) val objective = scoreboard.getObjective(args.checkString(1)) val scoreVal = args.checkInteger(2) - val score = scoreboard.getOrCreateScore(name,objective) - score.increaseScore(scoreVal) + val score = scoreboard.getOrCreatePlayerScore(name,objective) + score.add(scoreVal) null } @@ -588,8 +640,8 @@ object DebugCard { val name = args.checkString(0) val objective = scoreboard.getObjective(args.checkString(1)) val scoreVal = args.checkInteger(2) - val score = scoreboard.getOrCreateScore(name,objective) - score.decreaseScore(scoreVal) + val score = scoreboard.getOrCreatePlayerScore(name,objective) + score.add(-scoreVal) null } @@ -598,17 +650,18 @@ object DebugCard { private final val DimensionTag = "dimension" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - ctx = AccessContext.load(nbt) - dimension = nbt.getInteger(DimensionTag) - scoreboard = DimensionManager.getWorld(dimension).getScoreboard + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + ctx = AccessContext.loadData(nbt) + dimension = new ResourceLocation(nbt.getString(DimensionTag)) + val dimKey = RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension) + scoreboard = ServerLifecycleHooks.getCurrentServer.getLevel(dimKey).getScoreboard } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) - ctx.foreach(_.save(nbt)) - nbt.setInteger(DimensionTag, dimension) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) + ctx.foreach(_.saveData(nbt)) + nbt.putString(DimensionTag, dimension.toString) } } @@ -618,22 +671,35 @@ object DebugCard { // ----------------------------------------------------------------------- // + @Deprecated @Callback(doc = """function():number -- Gets the numeric id of the current dimension.""") def getDimensionId(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.provider.getDimension) + world.dimension match { + case World.OVERWORLD => result(Int.box(0)) + case World.NETHER => result(Int.box(-1)) + case World.END => result(Int.box(1)) + case _ => throw new Error("deprecated") + } } + @Deprecated @Callback(doc = """function():string -- Gets the name of the current dimension.""") def getDimensionName(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.provider.getDimensionType.getName) + result(world.dimension.location.toString) + } + + @Callback(doc = """function():string -- Gets the resource location of the current dimension.""") + def getDimension(context: Context, args: Arguments): Array[AnyRef] = { + checkAccess() + result(world.dimension.location.toString) } @Callback(doc = """function():number -- Gets the seed of the world.""") def getSeed(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.getSeed) + result(world.asInstanceOf[ServerWorld].getSeed) } @Callback(doc = """function():boolean -- Returns whether it is currently raining.""") @@ -645,7 +711,7 @@ object DebugCard { @Callback(doc = """function(value:boolean) -- Sets whether it is currently raining.""") def setRaining(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - world.getWorldInfo.setRaining(args.checkBoolean(0)) + world.getLevelData.setRaining(args.checkBoolean(0)) null } @@ -658,33 +724,39 @@ object DebugCard { @Callback(doc = """function(value:boolean) -- Sets whether it is currently thundering.""") def setThundering(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - world.getWorldInfo.setThundering(args.checkBoolean(0)) + world.getLevelData.asInstanceOf[IServerWorldInfo].setThundering(args.checkBoolean(0)) null } @Callback(doc = """function():number -- Get the current world time.""") def getTime(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.getWorldTime) + result(world.getDayTime) } @Callback(doc = """function(value:number) -- Set the current world time.""") def setTime(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - world.setWorldTime(args.checkDouble(0).toLong) + world.asInstanceOf[ServerWorld].setDayTime(args.checkDouble(0).toLong) null } @Callback(doc = """function():number, number, number -- Get the current spawn point coordinates.""") def getSpawnPoint(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.getWorldInfo.getSpawnX, world.getWorldInfo.getSpawnY, world.getWorldInfo.getSpawnZ) + result(world.getLevelData.getXSpawn, world.getLevelData.getYSpawn, world.getLevelData.getZSpawn) } @Callback(doc = """function(x:number, y:number, z:number) -- Set the spawn point coordinates.""") def setSpawnPoint(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - world.getWorldInfo.setSpawn(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))) + val x = args.checkInteger(0) + val y = args.checkInteger(1) + val z = args.checkInteger(2) + val info = world.getLevelData.asInstanceOf[IServerWorldInfo] + info.setXSpawn(x) + info.setYSpawn(y) + info.setZSpawn(z) null } @@ -700,34 +772,38 @@ object DebugCard { // ----------------------------------------------------------------------- // + @Deprecated @Callback(doc = """function(x:number, y:number, z:number):number -- Get the ID of the block at the specified coordinates.""") def getBlockId(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(Block.getIdFromBlock(world.getBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))).getBlock)) + val block = world.getBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))).getBlock + result(ForgeRegistries.BLOCKS.asInstanceOf[ForgeRegistry[Block]].getID(block)) } + @Deprecated @Callback(doc = """function(x:number, y:number, z:number):number -- Get the metadata of the block at the specified coordinates.""") def getMetadata(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - val state = world.getBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))) - result(state.getBlock.getMetaFromState(state)) + args.checkInteger(0) + args.checkInteger(1) + args.checkInteger(2) + result(0) } + @Deprecated @Callback(doc = """function(x:number, y:number, z:number[, actualState:boolean=false]) - gets the block state for the block at the specified position, optionally getting additional display related data""") def getBlockState(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val pos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) var state = world.getBlockState(pos) - if (args.optBoolean(3, false)) { - state = state.getActualState(world, pos) - } + args.optBoolean(3, false) // actualState result(state) } @Callback(doc = """function(x:number, y:number, z:number):number -- Check whether the block at the specified coordinates is loaded.""") def isLoaded(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.isBlockLoaded(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) + result(world.isLoaded(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) } @Callback(doc = """function(x:number, y:number, z:number):number -- Check whether the block at the specified coordinates has a tile entity.""") @@ -735,15 +811,15 @@ object DebugCard { checkAccess() val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) val state = world.getBlockState(blockPos) - result(state.getBlock.hasTileEntity(state)) + result(state.hasTileEntity) } @Callback(doc = """function(x:number, y:number, z:number):table -- Get the NBT of the block at the specified coordinates.""") def getTileNBT(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) - world.getTileEntity(blockPos) match { - case tileEntity: TileEntity => result(toNbt((nbt) => tileEntity.writeToNBT(nbt): Unit).toTypedMap) + world.getBlockEntity(blockPos) match { + case tileEntity: TileEntity => result(toNbt((nbt) => tileEntity.save(nbt)).toTypedMap) case _ => null } } @@ -752,15 +828,16 @@ object DebugCard { def setTileNBT(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) - world.getTileEntity(blockPos) match { + val state = world.getBlockState(blockPos) + world.getBlockEntity(blockPos) match { case tileEntity: TileEntity => typedMapToNbt(mapAsScalaMap(args.checkTable(3)).toMap) match { - case nbt: NBTTagCompound => - tileEntity.readFromNBT(nbt) - tileEntity.markDirty() + case nbt: CompoundNBT => + tileEntity.load(state, nbt) + tileEntity.setChanged() world.notifyBlockUpdate(blockPos) result(true) - case nbt => result(Unit, s"nbt tag compound expected, got '${NBTBase.NBT_TYPES(nbt.getId)}'") + case nbt => result(Unit, s"nbt tag COMPOUND expected, got 'nbt.getType.getName'") } case _ => result(Unit, "no tile entity") } @@ -769,40 +846,52 @@ object DebugCard { @Callback(doc = """function(x:number, y:number, z:number):number -- Get the light opacity of the block at the specified coordinates.""") def getLightOpacity(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.getBlockLightOpacity(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) + val pos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) + val state = world.getBlockState(pos) + result(state.getLightBlock(world, pos)) } @Callback(doc = """function(x:number, y:number, z:number):number -- Get the light value (emission) of the block at the specified coordinates.""") def getLightValue(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.getLight(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)), false)) + result(world.getLightEmission(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) } @Callback(doc = """function(x:number, y:number, z:number):number -- Get whether the block at the specified coordinates is directly under the sky.""") def canSeeSky(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - result(world.canBlockSeeSky(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) + result(world.canSeeSkyFromBelowWater(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))) + } + + @Deprecated + private def getStateFromMeta(block: Block, meta: Int) = { + val states = block.getStateDefinition.getPossibleStates + if (meta >= 0 && meta < states.size) states.get(meta) else block.defaultBlockState } + @Deprecated @Callback(doc = """function(x:number, y:number, z:number, id:number or string, meta:number):number -- Set the block at the specified coordinates.""") def setBlock(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - val block = if (args.isInteger(3)) Block.getBlockById(args.checkInteger(3)) else Block.getBlockFromName(args.checkString(3)) + val registry = ForgeRegistries.BLOCKS.asInstanceOf[ForgeRegistry[Block]] + val block = if (args.isInteger(3)) registry.getValue(args.checkInteger(3)) else registry.getValue(new ResourceLocation(args.checkString(3))) val metadata = args.checkInteger(4) - result(world.setBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)), block.getStateFromMeta(metadata))) + result(world.setBlockAndUpdate(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)), getStateFromMeta(block, metadata))) } + @Deprecated @Callback(doc = """function(x1:number, y1:number, z1:number, x2:number, y2:number, z2:number, id:number or string, meta:number):number -- Set all blocks in the area defined by the two corner points (x1, y1, z1) and (x2, y2, z2).""") def setBlocks(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val (xMin, yMin, zMin) = (args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)) val (xMax, yMax, zMax) = (args.checkInteger(3), args.checkInteger(4), args.checkInteger(5)) - val block = if (args.isInteger(6)) Block.getBlockById(args.checkInteger(6)) else Block.getBlockFromName(args.checkString(6)) + val registry = ForgeRegistries.BLOCKS.asInstanceOf[ForgeRegistry[Block]] + val block = if (args.isInteger(3)) registry.getValue(args.checkInteger(3)) else registry.getValue(new ResourceLocation(args.checkString(3))) val metadata = args.checkInteger(7) for (x <- math.min(xMin, xMax) to math.max(xMin, xMax)) { for (y <- math.min(yMin, yMax) to math.max(yMin, yMax)) { for (z <- math.min(zMin, zMax) to math.max(zMin, zMax)) { - world.setBlockState(new BlockPos(x, y, z), block.getStateFromMeta(metadata)) + world.setBlockAndUpdate(new BlockPos(x, y, z), getStateFromMeta(block, metadata)) } } } @@ -811,23 +900,25 @@ object DebugCard { // ----------------------------------------------------------------------- // + @Deprecated @Callback(doc = """function(id:string, count:number, damage:number, nbt:string, x:number, y:number, z:number, side:number):boolean - Insert an item stack into the inventory at the specified location. NBT tag is expected in JSON format.""") def insertItem(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - val item = Item.REGISTRY.getObject(new ResourceLocation(args.checkString(0))) + val item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(args.checkString(0))) if (item == null) { throw new IllegalArgumentException("invalid item id") } val count = args.checkInteger(1) val damage = args.checkInteger(2) val tagJson = args.optString(3, "") - val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.getTagFromJson(tagJson) + val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.parseTag(tagJson) val position = BlockPosition(args.checkDouble(4), args.checkDouble(5), args.checkDouble(6), world) val side = args.checkSideAny(7) InventoryUtils.inventoryAt(position, side) match { case Some(inventory) => - val stack = new ItemStack(item, count, damage) - stack.setTagCompound(tag) + val stack = new ItemStack(item, count) + stack.setTag(tag) + stack.setDamageValue(damage) result(InventoryUtils.insertIntoInventory(stack, inventory)) case _ => result(Unit, "no inventory") } @@ -851,15 +942,15 @@ object DebugCard { @Callback(doc = """function(id:string, amount:number, x:number, y:number, z:number, side:number):boolean - Insert some fluid into the tank at the specified location.""") def insertFluid(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() - val fluid = FluidRegistry.getFluid(args.checkString(0)) + val fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(args.checkString(0))) if (fluid == null) { throw new IllegalArgumentException("invalid fluid id") } val amount = args.checkInteger(1) val position = BlockPosition(args.checkDouble(2), args.checkDouble(3), args.checkDouble(4), world) val side = args.checkSideAny(5) - world.getTileEntity(position) match { - case handler: IFluidHandler => result(handler.fill(new FluidStack(fluid, amount), true)) + world.getBlockEntity(position) match { + case handler: IFluidHandler => result(handler.fill(new FluidStack(fluid, amount), IFluidHandler.FluidAction.EXECUTE)) case _ => result(Unit, "no tank") } } @@ -870,8 +961,8 @@ object DebugCard { val amount = args.checkInteger(0) val position = BlockPosition(args.checkDouble(1), args.checkDouble(2), args.checkDouble(3), world) val side = args.checkSideAny(4) - world.getTileEntity(position) match { - case handler: IFluidHandler => result(handler.drain(amount, true)) + world.getBlockEntity(position) match { + case handler: IFluidHandler => result(handler.drain(amount, IFluidHandler.FluidAction.EXECUTE)) case _ => result(Unit, "no tank") } } @@ -881,59 +972,19 @@ object DebugCard { private final val DimensionTag = "dimension" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - ctx = AccessContext.load(nbt) - world = DimensionManager.getWorld(nbt.getInteger(DimensionTag)) - } - - override def save(nbt: NBTTagCompound) { - super.save(nbt) - ctx.foreach(_.save(nbt)) - nbt.setInteger(DimensionTag, world.provider.getDimension) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + ctx = AccessContext.loadData(nbt) + val dimension = new ResourceLocation(nbt.getString(DimensionTag)) + val dimKey = RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension) + world = ServerLifecycleHooks.getCurrentServer.getLevel(dimKey) } - } - - class CommandSender(val host: EnvironmentHost, val underlying: EntityPlayerMP) extends FakePlayer(underlying.getEntityWorld.asInstanceOf[WorldServer], underlying.getGameProfile) { - var messages: Option[String] = None - def prepare(): Unit = { - val blockPos = BlockPosition(host) - posX = blockPos.x - posY = blockPos.y - posZ = blockPos.z - messages = None + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + ctx.foreach(_.saveData(nbt)) + nbt.putString(DimensionTag, world.dimension.location.toString) } - - override def getName: String = underlying.getName - - override def getEntityWorld: World = host.world - - override def sendMessage(message: ITextComponent) { - messages = Option(messages.fold("")(_ + "\n") + message.getUnformattedText) - } - - override def getDisplayName: ITextComponent = underlying.getDisplayName - - override def setCommandStat(`type`: Type, amount: Int): Unit = underlying.setCommandStat(`type`, amount) - - override def getPosition: BlockPos = underlying.getPosition - - override def canUseCommand(level: Int, commandName: String): Boolean = { - val profile = underlying.getGameProfile - val server = underlying.mcServer - val config = server.getPlayerList - server.isSinglePlayer || (config.canSendCommands(profile) && (config.getOppedPlayers.getEntry(profile) match { - case entry: UserListOpsEntry => entry.getPermissionLevel >= level - case _ => server.getOpPermissionLevel >= level - })) - } - - override def getCommandSenderEntity: EntityPlayerMP = underlying - - override def getPositionVector: Vec3d = underlying.getPositionVector - - override def sendCommandFeedback(): Boolean = underlying.sendCommandFeedback() } class TestValue extends AbstractValue { @@ -961,14 +1012,14 @@ object DebugCard { private final val ValueTag = "value" - override def load(nbt: NBTTagCompound): Unit = { - super.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = { + super.loadData(nbt) value = nbt.getString(ValueTag) } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) - nbt.setString(ValueTag, value) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) + nbt.putString(ValueTag, value) } } diff --git a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala index 1ce4946a14..3f95438f6d 100644 --- a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala @@ -25,11 +25,11 @@ import li.cil.oc.common.inventory.ItemStackInventory import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraft.util.Hand import scala.collection.convert.WrapAsJava._ @@ -69,14 +69,14 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs @Callback(doc = """function([velocity:number]):boolean -- Eject the currently present medium from the drive.""") def eject(context: Context, args: Arguments): Array[AnyRef] = { val velocity = args.optDouble(0, 0) max 0 min 1 - val ejected = decrStackSize(0, 1) + val ejected = removeItem(0, 1) if (!ejected.isEmpty) { val entity = InventoryUtils.spawnStackInWorld(BlockPosition(rack), ejected, Option(rack.facing)) if (entity != null) { - val vx = rack.facing.getFrontOffsetX * velocity - val vy = rack.facing.getFrontOffsetY * velocity - val vz = rack.facing.getFrontOffsetZ * velocity - entity.addVelocity(vx, vy, vz) + val vx = rack.facing.getStepX * velocity + val vy = rack.facing.getStepY * velocity + val vz = rack.facing.getStepZ * velocity + entity.push(vx, vy, vz) } result(true) } @@ -86,7 +86,7 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs // ----------------------------------------------------------------------- // // Analyzable - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = filesystemNode.fold(null: Array[Node])(Array(_)) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = filesystemNode.fold(null: Array[Node])(Array(_)) // ----------------------------------------------------------------------- // // ItemStackInventory @@ -96,19 +96,19 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs // ----------------------------------------------------------------------- // // IInventory - override def getSizeInventory: Int = 1 + override def getContainerSize: Int = 1 - override def isItemValidForSlot(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack))) match { + override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = (slot, Option(Driver.driverFor(stack))) match { case (0, Some(driver)) => driver.slot(stack) == Slot.Floppy case _ => false } - override def isUsableByPlayer(player: EntityPlayer): Boolean = rack.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity): Boolean = rack.stillValid(player) // ----------------------------------------------------------------------- // // ComponentInventory - override def container: ItemStack = rack.getStackInSlot(slot) + override def container: ItemStack = rack.getItem(slot) override protected def onItemAdded(slot: Int, stack: ItemStack) { super.onItemAdded(slot, stack) @@ -118,7 +118,7 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs } case _ => } - if (!rack.world.isRemote) { + if (!rack.world.isClientSide) { rack.markChanged(this.slot) Sound.playDiskInsert(rack) } @@ -126,7 +126,7 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs override protected def onItemRemoved(slot: Int, stack: ItemStack) { super.onItemRemoved(slot, stack) - if (!rack.world.isRemote) { + if (!rack.world.isClientSide) { rack.markChanged(this.slot) Sound.playDiskEject(rack) } @@ -140,24 +140,24 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs // ----------------------------------------------------------------------- // // Persistable - override def load(nbt: NBTTagCompound) { - super[AbstractManagedEnvironment].load(nbt) - super[ComponentInventory].load(nbt) + override def loadData(nbt: CompoundNBT) { + super[AbstractManagedEnvironment].loadData(nbt) + super[ComponentInventory].loadData(nbt) connectComponents() } - override def save(nbt: NBTTagCompound) { - super[AbstractManagedEnvironment].save(nbt) - super[ComponentInventory].save(nbt) + override def saveData(nbt: CompoundNBT) { + super[AbstractManagedEnvironment].saveData(nbt) + super[ComponentInventory].saveData(nbt) } // ----------------------------------------------------------------------- // // RackMountable - override def getData: NBTTagCompound = { - val nbt = new NBTTagCompound() - nbt.setLong("lastAccess", lastAccess) - nbt.setTag("disk", toNbt(getStackInSlot(0))) + override def getData: CompoundNBT = { + val nbt = new CompoundNBT() + nbt.putLong("lastAccess", lastAccess) + nbt.put("disk", toNbt(getItem(0))) nbt } @@ -165,24 +165,24 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs override def getConnectableAt(index: Int): RackBusConnectable = null - override def onActivate(player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { - if (player.isSneaking) { - val isDiskInDrive = !getStackInSlot(0).isEmpty - val isHoldingDisk = isItemValidForSlot(0, heldItem) + override def onActivate(player: PlayerEntity, hand: Hand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { + if (player.isCrouching) { + val isDiskInDrive = !getItem(0).isEmpty + val isHoldingDisk = canPlaceItem(0, heldItem) if (isDiskInDrive) { - if (!rack.world.isRemote) { + if (!rack.world.isClientSide) { InventoryUtils.dropSlot(BlockPosition(rack), this, 0, 1, Option(rack.facing)) } } if (isHoldingDisk) { // Insert the disk. - setInventorySlotContents(0, player.inventory.decrStackSize(player.inventory.currentItem, 1)) + setItem(0, player.inventory.removeItem(player.inventory.selected, 1)) } isDiskInDrive || isHoldingDisk } else { val position = BlockPosition(rack) - player.openGui(OpenComputers, GuiType.DiskDriveMountableInRack.id, rack.world, position.x, GuiType.embedSlot(position.y, slot), position.z) + OpenComputers.openGui(player, GuiType.DiskDriveMountableInRack.id, rack.world, position.x, GuiType.embedSlot(position.y, slot), position.z) true } } diff --git a/src/main/scala/li/cil/oc/server/component/Drive.scala b/src/main/scala/li/cil/oc/server/component/Drive.scala index b62bb350be..5f971df817 100644 --- a/src/main/scala/li/cil/oc/server/component/Drive.scala +++ b/src/main/scala/li/cil/oc/server/component/Drive.scala @@ -24,8 +24,9 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.common.DimensionManager +import net.minecraft.nbt.CompoundNBT +import net.minecraft.world.storage.FolderName +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsJava._ @@ -35,7 +36,7 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op withConnector(). create() - private def savePath = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + node.address + ".bin") + private def savePath = ServerLifecycleHooks.getCurrentServer.getWorldPath(new FolderName(Settings.savePath + node.address + ".bin")).toFile private final val sectorSize = 512 @@ -137,8 +138,8 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op private final val HeadPosTag = "headPos" - override def load(nbt: NBTTagCompound) = this.synchronized { - super.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = this.synchronized { + super.loadData(nbt) if (node.address != null) try { val path = savePath @@ -157,15 +158,15 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op case t: Throwable => OpenComputers.log.warn(s"Failed loading drive contents for '${node.address}'.", t) } - headPos = nbt.getInteger(HeadPosTag) max 0 min sectorToHeadPos(sectorCount) + headPos = nbt.getInt(HeadPosTag) max 0 min sectorToHeadPos(sectorCount) if (label != null) { - label.load(nbt) + label.loadData(nbt) } } - override def save(nbt: NBTTagCompound) = this.synchronized { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = this.synchronized { + super.saveData(nbt) if (node.address != null) try { val path = savePath @@ -180,10 +181,10 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op case t: Throwable => OpenComputers.log.warn(s"Failed saving drive contents for '${node.address}'.", t) } - nbt.setInteger(HeadPosTag, headPos) + nbt.putInt(HeadPosTag, headPos) if (label != null) { - label.save(nbt) + label.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index 359a2f7f0b..66126005e5 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -17,9 +17,9 @@ import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.common.entity import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.item.EntityItem -import net.minecraft.init.SoundEvents -import net.minecraft.util.EnumFacing +import net.minecraft.entity.item.ItemEntity +import net.minecraft.util.SoundEvents +import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import scala.collection.convert.WrapAsJava._ @@ -44,11 +44,11 @@ class Drone(val agent: entity.Drone) extends AbstractManagedEnvironment with Age override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSideAny(n) - override protected def suckableItems(side: EnumFacing) = entitiesInBlock(classOf[EntityItem], position) ++ super.suckableItems(side) + override protected def suckableItems(side: Direction) = entitiesInBlock(classOf[ItemEntity], position) ++ super.suckableItems(side) - override protected def onSuckCollect(entity: EntityItem) = { + override protected def onSuckCollect(entity: ItemEntity) = { if (InventoryUtils.insertIntoInventory(entity.getItem, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))) { - world.playSound(agent.player, agent.posX, agent.posY, agent.posZ, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.NEUTRAL, 0.2f, ((world.rand.nextFloat - world.rand.nextFloat) * 0.7f + 1) * 2) + world.playSound(agent.player, agent.getX, agent.getY, agent.getZ, SoundEvents.ITEM_PICKUP, SoundCategory.NEUTRAL, 0.2f, ((world.random.nextFloat - world.random.nextFloat) * 0.7f + 1) * 2) } } @@ -93,14 +93,14 @@ class Drone(val agent: entity.Drone) extends AbstractManagedEnvironment with Age @Callback(doc = "function():number -- Get the current distance to the target position.") def getOffset(context: Context, args: Arguments): Array[AnyRef] = - result(agent.getDistance(agent.targetX.floatValue(), agent.targetY.floatValue(), agent.targetZ.floatValue())) + result(agent.position.distanceTo(agent.getTarget())) @Callback(doc = "function():number -- Get the current velocity in m/s.") def getVelocity(context: Context, args: Arguments): Array[AnyRef] = result(math.sqrt(agent.motionX * agent.motionX + agent.motionY * agent.motionY + agent.motionZ * agent.motionZ) * 20) // per second @Callback(doc = "function():number -- Get the maximum velocity, in m/s.") - def getMaxVelocity(context: Context, args: Arguments): Array[AnyRef] = { + def getV1elocity(context: Context, args: Arguments): Array[AnyRef] = { result(agent.maxVelocity * 20) // per second } diff --git a/src/main/scala/li/cil/oc/server/component/EEPROM.scala b/src/main/scala/li/cil/oc/server/component/EEPROM.scala index 1367527e6b..e65b216888 100644 --- a/src/main/scala/li/cil/oc/server/component/EEPROM.scala +++ b/src/main/scala/li/cil/oc/server/component/EEPROM.scala @@ -15,7 +15,7 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ @@ -121,21 +121,21 @@ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { private final val ReadonlyTag = Settings.namespace + "readonly" private final val UserdataTag = Settings.namespace + "userdata" - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) codeData = nbt.getByteArray(EEPROMTag) - if (nbt.hasKey(LabelTag)) { + if (nbt.contains(LabelTag)) { label = nbt.getString(LabelTag) } readonly = nbt.getBoolean(ReadonlyTag) volatileData = nbt.getByteArray(UserdataTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setByteArray(EEPROMTag, codeData) - nbt.setString(LabelTag, label) - nbt.setBoolean(ReadonlyTag, readonly) - nbt.setByteArray(UserdataTag, volatileData) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putByteArray(EEPROMTag, codeData) + nbt.putString(LabelTag, label) + nbt.putBoolean(ReadonlyTag, readonly) + nbt.putByteArray(UserdataTag, volatileData) } } diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index c496625a9a..622e881189 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -24,9 +24,9 @@ import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.common.SaveHandler import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagIntArray -import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.IntArrayNBT +import net.minecraft.nbt.ListNBT import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.WrapAsJava._ @@ -303,10 +303,10 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) - nbt.getTagList("owners", NBT.TAG_COMPOUND).foreach((ownerNbt: NBTTagCompound) => { + nbt.getList("owners", NBT.TAG_COMPOUND).foreach((ownerNbt: CompoundNBT) => { val address = ownerNbt.getString("address") if (address != "") { owners += address -> ownerNbt.getIntArray("handles").to[mutable.Set] @@ -314,29 +314,29 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option }) if (label != null) { - label.load(nbt) + label.loadData(nbt) } - fileSystem.load(nbt.getCompoundTag("fs")) + fileSystem.loadData(nbt.getCompound("fs")) } - override def save(nbt: NBTTagCompound) = fileSystem.synchronized { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = fileSystem.synchronized { + super.saveData(nbt) if (label != null) { - label.save(nbt) + label.saveData(nbt) } if (!SaveHandler.savingForClients) { - val ownersNbt = new NBTTagList() + val ownersNbt = new ListNBT() for ((address, handles) <- owners) { - val ownerNbt = new NBTTagCompound() - ownerNbt.setString("address", address) - ownerNbt.setTag("handles", new NBTTagIntArray(handles.toArray)) - ownersNbt.appendTag(ownerNbt) + val ownerNbt = new CompoundNBT() + ownerNbt.putString("address", address) + ownerNbt.put("handles", new IntArrayNBT(handles.toArray)) + ownersNbt.add(ownerNbt) } - nbt.setTag("owners", ownersNbt) + nbt.put("owners", ownersNbt) - nbt.setNewCompoundTag("fs", fileSystem.save) + nbt.setNewCompoundTag("fs", fileSystem.saveData) } } @@ -395,16 +395,16 @@ final class HandleValue extends AbstractValue { private val OwnerTag = "owner" private val HandleTag = "handle" - override def load(nbt: NBTTagCompound): Unit = { - super.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = { + super.loadData(nbt) owner = nbt.getString(OwnerTag) - handle = nbt.getInteger(HandleTag) + handle = nbt.getInt(HandleTag) } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) - nbt.setString(OwnerTag, owner) - nbt.setInteger(HandleTag, handle) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) + nbt.putString(OwnerTag, owner) + nbt.putInt(HandleTag, handle) } override def toString: String = handle.toString diff --git a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala index c6495ae4f8..7ec82021ce 100644 --- a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala @@ -26,11 +26,15 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.DatabaseAccess import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.Block +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraft.world.World +import net.minecraft.world.biome.Biome.RainType +import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.MinecraftForge import scala.collection.convert.WrapAsJava._ @@ -55,7 +59,7 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit // ----------------------------------------------------------------------- // - override protected def checkSideForAction(args: Arguments, n: Int): EnumFacing = { + override protected def checkSideForAction(args: Arguments, n: Int): Direction = { val side = args.checkSideAny(n) val is_uc = host.isInstanceOf[Microcontroller] host match { @@ -69,15 +73,15 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit override def position: BlockPosition = host match { case robot: EntityRobot => robot.proxy.position - case drone: EntityDrone => BlockPosition(drone.getPosition, drone.getEntityWorld) + case drone: EntityDrone => BlockPosition(drone.blockPosition, drone.level) case uc: Microcontroller => uc.position case tablet: TabletWrapper => BlockPosition(tablet.xPosition, tablet.yPosition, tablet.zPosition, tablet.world) case _ => BlockPosition(host) } private def canSeeSky: Boolean = { - val blockPos = position.offset(EnumFacing.UP) - !host.world.provider.isNether && host.world.canBlockSeeSky(blockPos.toBlockPos) + val blockPos = position.offset(Direction.UP) + host.world.dimension != World.NETHER && host.world.canSeeSkyFromBelowWater(blockPos.toBlockPos) } @Callback(doc = """function():boolean -- Returns whether there is a clear line of sight to the sky directly above.""") @@ -87,11 +91,11 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit @Callback(doc = """function():boolean -- Return whether the sun is currently visible directly above.""") def isSunVisible(computer: Context, args: Arguments): Array[AnyRef] = { - val blockPos = BlockPosition(host).offset(EnumFacing.UP) + val blockPos = BlockPosition(host).offset(Direction.UP) result( - host.world.isDaytime && + host.world.isDay && canSeeSky && - (!host.world.getBiome(blockPos.toBlockPos).canRain || (!host.world.isRaining && !host.world.isThundering))) + (host.world.getBiome(blockPos.toBlockPos).getPrecipitation == RainType.NONE || (!host.world.isRaining && !host.world.isThundering))) } @Callback(doc = """function(x:number, z:number[, y:number, w:number, d:number, h:number][, ignoreReplaceable:boolean|options:table]):table -- Analyzes the density of the column at the specified relative coordinates.""") @@ -168,13 +172,17 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit return result(Unit, "not enough energy") val blockPos = BlockPosition(host).offset(globalSide) - val block = host.world.getBlock(blockPos) - val item = Item.getItemFromBlock(block) + val blockState = host.world.getBlockState(blockPos.toBlockPos) + val item = blockState.getBlock().asItem() if (item == null) result(Unit, "block has no registered item representation") else { - val metadata = host.world.getBlockMetadata(blockPos) - val damage = block.damageDropped(metadata) - val stack = new ItemStack(item, 1, damage) + val stacks = Block.getDrops(blockState, host.world.asInstanceOf[ServerWorld], blockPos.toBlockPos, host.world.getBlockEntity(blockPos.toBlockPos)) + val stack = if (!stacks.isEmpty) { + val drop = stacks.find(s => s.getItem == item).getOrElse(stacks.get(0)) + drop.setCount(1) + drop + } + else new ItemStack(item, 1) DatabaseAccess.withDatabase(node, args.checkString(1), database => { val toSlot = args.checkSlot(database.data, 2) val nonEmpty = database.getStackInSlot(toSlot) != ItemStack.EMPTY // not the same as isEmpty! zero size stacks! @@ -188,14 +196,14 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit super.onMessage(message) if (message.name == "tablet.use") message.source.host match { case machine: api.machine.Machine => (machine.host, message.data) match { - case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => + case (tablet: internal.Tablet, Array(nbt: CompoundNBT, stack: ItemStack, player: PlayerEntity, blockPos: BlockPosition, side: Direction, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => if (node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) { val event = new Analyze(host, Map.empty[AnyRef, AnyRef], blockPos.toBlockPos) MinecraftForge.EVENT_BUS.post(event) if (!event.isCanceled) { for ((key, value) <- event.data) value match { - case number: java.lang.Number => nbt.setDouble(key, number.doubleValue()) - case string: String if !string.isEmpty => nbt.setString(key, string) + case number: java.lang.Number => nbt.putDouble(key, number.doubleValue()) + case string: String if !string.isEmpty => nbt.putString(key, string) case _ => // Unsupported, ignore. } } diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index cac1beed12..2eaadc75e5 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -12,7 +12,7 @@ import li.cil.oc.api.network._ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.PackedColor -import net.minecraft.nbt.{NBTTagCompound, NBTTagList} +import net.minecraft.nbt.{CompoundNBT, ListNBT} import li.cil.oc.common.component import li.cil.oc.common.component.GpuTextBuffer @@ -449,8 +449,8 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device // if (bufferIndex != RESERVED_SCREEN_INDEX && args.count() == 0) { // return screen { // case ram: GpuTextBuffer => { -// val nbt = new NBTTagCompound -// ram.data.save(nbt) +// val nbt = new CompoundNBT +// ram.data.saveData(nbt) // result(nbt) // } // } @@ -615,12 +615,12 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device private final val NBT_PAGES: String = "pages" private final val NBT_PAGE_IDX: String = "page_idx" private final val NBT_PAGE_DATA: String = "page_data" - private val COMPOUND_ID = (new NBTTagCompound).getId + private val COMPOUND_ID = (new CompoundNBT).getId - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) - if (nbt.hasKey(SCREEN_KEY)) { + if (nbt.contains(SCREEN_KEY)) { nbt.getString(SCREEN_KEY) match { case screen: String if !screen.isEmpty => screenAddress = Some(screen) case _ => screenAddress = None @@ -628,50 +628,50 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device screenInstance = None } - if (nbt.hasKey(BUFFER_INDEX_KEY)) { - bufferIndex = nbt.getInteger(BUFFER_INDEX_KEY) + if (nbt.contains(BUFFER_INDEX_KEY)) { + bufferIndex = nbt.getInt(BUFFER_INDEX_KEY) } removeAllBuffers() // JUST in case - if (nbt.hasKey(VIDEO_RAM_KEY)) { - val videoRamNbt = nbt.getCompoundTag(VIDEO_RAM_KEY) - val nbtPages = videoRamNbt.getTagList(NBT_PAGES, COMPOUND_ID) - for (i <- 0 until nbtPages.tagCount) { - val nbtPage = nbtPages.getCompoundTagAt(i) - val idx: Int = nbtPage.getInteger(NBT_PAGE_IDX) - val data = nbtPage.getCompoundTag(NBT_PAGE_DATA) + if (nbt.contains(VIDEO_RAM_KEY)) { + val videoRamNbt = nbt.getCompound(VIDEO_RAM_KEY) + val nbtPages = videoRamNbt.getList(NBT_PAGES, COMPOUND_ID) + for (i <- 0 until nbtPages.size) { + val nbtPage = nbtPages.getCompound(i) + val idx: Int = nbtPage.getInt(NBT_PAGE_IDX) + val data = nbtPage.getCompound(NBT_PAGE_DATA) loadBuffer(node.address, idx, data) } } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) if (screenAddress.isDefined) { - nbt.setString(SCREEN_KEY, screenAddress.get) + nbt.putString(SCREEN_KEY, screenAddress.get) } - nbt.setInteger(BUFFER_INDEX_KEY, bufferIndex) + nbt.putInt(BUFFER_INDEX_KEY, bufferIndex) - val videoRamNbt = new NBTTagCompound - val nbtPages = new NBTTagList + val videoRamNbt = new CompoundNBT + val nbtPages = new ListNBT val indexes = bufferIndexes() for (idx: Int <- indexes) { getBuffer(idx) match { case Some(page) => { - val nbtPage = new NBTTagCompound - nbtPage.setInteger(NBT_PAGE_IDX, idx) - val data = new NBTTagCompound - page.data.save(data) - nbtPage.setTag(NBT_PAGE_DATA, data) - nbtPages.appendTag(nbtPage) + val nbtPage = new CompoundNBT + nbtPage.putInt(NBT_PAGE_IDX, idx) + val data = new CompoundNBT + page.data.saveData(data) + nbtPage.put(NBT_PAGE_DATA, data) + nbtPages.add(nbtPage) } case _ => // ignore } } - videoRamNbt.setTag(NBT_PAGES, nbtPages) - nbt.setTag(VIDEO_RAM_KEY, videoRamNbt) + videoRamNbt.put(NBT_PAGES, nbtPages) + nbt.put(VIDEO_RAM_KEY, videoRamNbt) } } diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index e4c4df868a..2075a6e982 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -32,7 +32,7 @@ import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.util.ThreadPoolFactory import net.minecraft.server.MinecraftServer -import net.minecraftforge.fml.common.FMLCommonHandler +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -474,7 +474,7 @@ object InternetCard { private class RequestSender(val url: URL, val post: Option[String], val headers: Map[String, String], val method: Option[String]) extends Callable[InputStream] { override def call() = try { checkLists(InetAddress.getByName(url.getHost), url.getHost) - val proxy = Option(FMLCommonHandler.instance.getMinecraftServerInstance.getServerProxy).getOrElse(java.net.Proxy.NO_PROXY) + val proxy = java.net.Proxy.NO_PROXY url.openConnection(proxy) match { case http: HttpURLConnection => try { http.setDoInput(true) diff --git a/src/main/scala/li/cil/oc/server/component/Keyboard.scala b/src/main/scala/li/cil/oc/server/component/Keyboard.scala index 78c6a8a425..f5719dfcad 100644 --- a/src/main/scala/li/cil/oc/server/component/Keyboard.scala +++ b/src/main/scala/li/cil/oc/server/component/Keyboard.scala @@ -15,7 +15,7 @@ import li.cil.oc.api.network.Message import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import scala.collection.convert.WrapAsJava._ import scala.collection.mutable @@ -28,7 +28,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit withComponent("keyboard"). create() - val pressedKeys = mutable.Map.empty[EntityPlayer, mutable.Map[Integer, Character]] + val pressedKeys = mutable.Map.empty[PlayerEntity, mutable.Map[Integer, Character]] var usableOverride: Option[api.internal.Keyboard.UsabilityChecker] = None @@ -47,7 +47,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit // ----------------------------------------------------------------------- // - def releasePressedKeys(player: EntityPlayer) { + def releasePressedKeys(player: PlayerEntity) { pressedKeys.get(player) match { case Some(keys) => for ((code, char) <- keys) { if (Settings.get.inputUsername) { @@ -66,7 +66,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit override def onMessage(message: Message) = { message.data match { - case Array(p: EntityPlayer, char: Character, code: Integer) if message.name == "keyboard.keyDown" => + case Array(p: PlayerEntity, char: Character, code: Integer) if message.name == "keyboard.keyDown" => if (isUsableByPlayer(p)) { pressedKeys.getOrElseUpdate(p, mutable.Map.empty[Integer, Character]) += code -> char if (Settings.get.inputUsername) { @@ -76,7 +76,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit signal(p, "key_down", char, code) } } - case Array(p: EntityPlayer, char: Character, code: Integer) if message.name == "keyboard.keyUp" => + case Array(p: PlayerEntity, char: Character, code: Integer) if message.name == "keyboard.keyUp" => pressedKeys.get(p) match { case Some(keys) if keys.contains(code) => keys -= code @@ -88,7 +88,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit } case _ => } - case Array(p: EntityPlayer, value: String) if message.name == "keyboard.clipboard" => + case Array(p: PlayerEntity, value: String) if message.name == "keyboard.clipboard" => if (isUsableByPlayer(p)) { for (line <- value.linesWithSeparators) { if (Settings.get.inputUsername) { @@ -105,9 +105,9 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit // ----------------------------------------------------------------------- // - def isUsableByPlayer(p: EntityPlayer) = usableOverride match { + def isUsableByPlayer(p: PlayerEntity) = usableOverride match { case Some(callback) => callback.isUsableByPlayer(this, p) - case _ => p.getDistanceSq(host.xPosition, host.yPosition, host.zPosition) <= 64 + case _ => p.distanceToSqr(host.xPosition, host.yPosition, host.zPosition) <= 64 } protected def signal(args: AnyRef*) = diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index 564b5c321b..1795f9dfad 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -16,7 +16,7 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.common.Tier import li.cil.oc.server.network.QuantumNetwork -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -88,17 +88,17 @@ class LinkedCard extends AbstractManagedEnvironment with QuantumNetwork.QuantumN private final val TunnelTag = Settings.namespace + "tunnel" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - if (nbt.hasKey(TunnelTag)) { + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + if (nbt.contains(TunnelTag)) { tunnel = nbt.getString(TunnelTag) } loadWakeMessage(nbt) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setString(TunnelTag, tunnel) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putString(TunnelTag, tunnel) saveWakeMessage(nbt) } } diff --git a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala index ac476b248c..48a11fad2e 100644 --- a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala @@ -15,10 +15,12 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.util.SideTracker -import net.minecraft.entity.EntityLivingBase -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.potion.Potion -import net.minecraft.util.math.{AxisAlignedBB, BlockPos, Vec3d} +import net.minecraft.entity.LivingEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.potion.Effect +import net.minecraft.potion.Effects +import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceContext, RayTraceResult} +import net.minecraft.util.math.vector.Vector3d import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -34,7 +36,7 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi private var sensitivity = 0.4 - private val trackedEntities = mutable.Map.empty[EntityLivingBase, (Double, Double, Double)] + private val trackedEntities = mutable.Map.empty[LivingEntity, (Double, Double, Double)] private final lazy val deviceInfo = Map( DeviceAttribute.Class -> DeviceClass.Generic, @@ -56,19 +58,19 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi private def z = host.zPosition - private def isServer: Boolean = if (world != null) !world.isRemote else SideTracker.isServer + private def isServer: Boolean = if (world != null) !world.isClientSide else SideTracker.isServer override def canUpdate: Boolean = isServer override def update() { super.update() - if (world.getTotalWorldTime % 10 == 0) { + if (world.getGameTime % 10 == 0) { // Get a list of all living entities we could possibly detect, using a rough // bounding box check, then refining it using the actual distance and an // actual visibility check. - val entities = world.getEntitiesWithinAABB(classOf[EntityLivingBase], sensorBounds) - .map(_.asInstanceOf[EntityLivingBase]) - .filter(entity => entity.isEntityAlive && isInRange(entity) && isVisible(entity)) + val entities = world.getEntitiesOfClass(classOf[LivingEntity], sensorBounds) + .map(_.asInstanceOf[LivingEntity]) + .filter(entity => entity.isAlive && isInRange(entity) && isVisible(entity)) .toSet // Get rid of all tracked entities that are no longer visible. trackedEntities.retain((key, _) => entities.contains(key)) @@ -77,7 +79,7 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi trackedEntities.get(entity) match { case Some((prevX, prevY, prevZ)) => // Known entity, check if it moved enough to trigger. - if (entity.getDistanceSq(prevX, prevY, prevZ) > sensitivity * sensitivity * 2) { + if (entity.distanceToSqr(prevX, prevY, prevZ) > sensitivity * sensitivity * 2) { sendSignal(entity) } case _ => @@ -85,7 +87,7 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi sendSignal(entity) } // Update tracked position. - trackedEntities += entity ->(entity.posX, entity.posY, entity.posZ) + trackedEntities += entity ->(entity.getX, entity.getY, entity.getZ) } } } @@ -94,31 +96,32 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi x + 0.5 - radius, y + 0.5 - radius, z + 0.5 - radius, x + 0.5 + radius, y + 0.5 + radius, z + 0.5 + radius) - private def isInRange(entity: EntityLivingBase) = entity.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= radius * radius + private def isInRange(entity: LivingEntity) = entity.distanceToSqr(x + 0.5, y + 0.5, z + 0.5) <= radius * radius - private def isClearPath(target: Vec3d): Boolean = { - val origin = new Vec3d(x, y, z) + private def isClearPath(target: Vector3d): Boolean = { + val origin = new Vector3d(x, y, z) val path = target.subtract(origin).normalize() val eye = origin.add(path) - world.rayTraceBlocks(eye, target) == null + val trace = world.clip(new RayTraceContext(eye, target, RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.ANY, null)) + trace.getType == RayTraceResult.Type.MISS } - private def isVisible(entity: EntityLivingBase) = - entity.getActivePotionEffect(Potion.getPotionFromResourceLocation("invisibility")) == null && + private def isVisible(entity: LivingEntity) = + entity.getEffect(Effects.INVISIBILITY) == null && // Note: it only working in lit conditions works and is neat, but this // is pseudo-infrared driven (it only works for *living* entities, after // all), so I think it makes more sense for it to work in the dark, too. /* entity.getBrightness(0) > 0.2 && */ { - val target = entity.getPositionVector - isClearPath(target) || isClearPath(target.addVector(0.0D, entity.getEyeHeight, 0.0D)) + val target = entity.position + isClearPath(target) || isClearPath(target.add(0.0D, entity.getEyeHeight, 0.0D)) } - private def sendSignal(entity: EntityLivingBase) { + private def sendSignal(entity: LivingEntity) { if (Settings.get.inputUsername) { - node.sendToReachable("computer.signal", "motion", Double.box(entity.posX - (x + 0.5)), Double.box(entity.posY - (y + 0.5)), Double.box(entity.posZ - (z + 0.5)), entity.getName) + node.sendToReachable("computer.signal", "motion", Double.box(entity.getX - (x + 0.5)), Double.box(entity.getY - (y + 0.5)), Double.box(entity.getZ - (z + 0.5)), entity.getName) } else { - node.sendToReachable("computer.signal", "motion", Double.box(entity.posX - (x + 0.5)), Double.box(entity.posY - (y + 0.5)), Double.box(entity.posZ - (z + 0.5))) + node.sendToReachable("computer.signal", "motion", Double.box(entity.getX - (x + 0.5)), Double.box(entity.getY - (y + 0.5)), Double.box(entity.getZ - (z + 0.5))) } } @@ -138,14 +141,14 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi private final val SensitivityTag = Settings.namespace + "sensitivity" - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) sensitivity = nbt.getDouble(SensitivityTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setDouble(SensitivityTag, sensitivity) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putDouble(SensitivityTag, sensitivity) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala index abcfcca447..ce119b5e13 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -160,17 +160,17 @@ class NetworkCard(val host: EnvironmentHost) extends AbstractManagedEnvironment private final val OpenPortsTag = "openPorts" - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) assert(openPorts.isEmpty) openPorts ++= nbt.getIntArray(OpenPortsTag) loadWakeMessage(nbt) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) - nbt.setIntArray(OpenPortsTag, openPorts.toArray) + nbt.putIntArray(OpenPortsTag, openPorts.toArray) saveWakeMessage(nbt) } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala index 61132bf467..3987fc780e 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala @@ -11,7 +11,7 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.common.tileentity.traits.BundledRedstoneAware -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ @@ -33,7 +33,7 @@ trait RedstoneBundled extends RedstoneVanilla { override def redstone: EnvironmentHost with BundledRedstoneAware - private def getBundleKey(args: Arguments): (Option[EnumFacing], Option[Int]) = { + private def getBundleKey(args: Arguments): (Option[Direction], Option[Int]) = { args.count match { case 2 => (Option(checkSide(args, 0)), Option(checkColor(args, 1))) case 1 => (Option(checkSide(args, 0)), None) @@ -103,10 +103,10 @@ trait RedstoneBundled extends RedstoneVanilla { def setBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { var ret: AnyRef = null if (getBundleAssignment(args) match { - case (side: EnumFacing, color: Int, value: Int) => + case (side: Direction, color: Int, value: Int) => ret = new java.lang.Integer(redstone.getBundledOutput(side, color)) redstone.setBundledOutput(side, color, value) - case (side: EnumFacing, value: util.Map[_, _], _) => + case (side: Direction, value: util.Map[_, _], _) => ret = redstone.getBundledOutput(side) redstone.setBundledOutput(side, value) case (value: util.Map[_, _], _, _) => diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala index 117dab5820..d53463e6b4 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala @@ -8,7 +8,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable.ArrayBuffer @@ -53,13 +53,13 @@ trait RedstoneSignaller extends AbstractManagedEnvironment { private final val WakeThresholdNbt = "wakeThreshold" - override def load(nbt: NBTTagCompound): Unit = { - super.load(nbt) - wakeThreshold = nbt.getInteger(WakeThresholdNbt) + override def loadData(nbt: CompoundNBT): Unit = { + super.loadData(nbt) + wakeThreshold = nbt.getInt(WakeThresholdNbt) } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) - nbt.setInteger(WakeThresholdNbt, wakeThreshold) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) + nbt.putInt(WakeThresholdNbt, wakeThreshold) } } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala index ad5ff32388..eadfc5dd44 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala @@ -16,7 +16,8 @@ import li.cil.oc.common.tileentity.traits.{RedstoneAware, RedstoneChangedEventAr import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.util.EnumFacing +import li.cil.oc.util.RotationHelper +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ @@ -36,7 +37,7 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { override def getDeviceInfo: util.Map[String, String] = deviceInfo - protected val SIDE_RANGE: Array[EnumFacing] = EnumFacing.values + protected val SIDE_RANGE: Array[Direction] = Direction.values // ----------------------------------------------------------------------- // @Callback(direct = true, doc = "function([side:number]):number or table -- Get the redstone input (all sides, or optionally on the specified side)") @@ -59,7 +60,7 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { def setOutput(context: Context, args: Arguments): Array[AnyRef] = { var ret: AnyRef = null if (getAssignment(args) match { - case (side: EnumFacing, value: Int) => + case (side: Direction, value: Int) => ret = new java.lang.Integer(redstone.getOutput(side)) redstone.setOutput(side, value) case (value: util.Map[_, _], _) => @@ -78,7 +79,7 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { val blockPos = BlockPosition(redstone).offset(side) if (redstone.world.blockExists(blockPos)) { val block = redstone.world.getBlock(blockPos) - if (block.hasComparatorInputOverride(redstone.world.getBlockState(blockPos.toBlockPos))) { + if (redstone.world.getBlockState(blockPos.toBlockPos).hasAnalogOutputSignal) { val comparatorOverride = block.getComparatorInputOverride(blockPos, side.getOpposite) return result(comparatorOverride) } @@ -114,11 +115,11 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { } } - protected def checkSide(args: Arguments, index: Int): EnumFacing = { + protected def checkSide(args: Arguments, index: Int): Direction = { val side = args.checkInteger(index) if (side < 0 || side > 5) throw new IllegalArgumentException("invalid side") - redstone.toGlobal(EnumFacing.getFront(side)) + redstone.toGlobal(Direction.from3DDataValue(side)) } private def valuesToMap(ar: Array[Int]): Map[Int, Int] = SIDE_RANGE.map(_.ordinal).map{ case side if side < ar.length => side -> ar(side) }.toMap diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index e2d384ee5a..42af249a5e 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -14,12 +14,11 @@ import li.cil.oc.common.EventHandler import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs import li.cil.oc.integration.Mods import li.cil.oc.integration.util -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.fml.common.Optional +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ -trait RedstoneWireless extends RedstoneSignaller with WirelessReceivingDevice with WirelessTransmittingDevice with DeviceInfo { +trait RedstoneWireless extends RedstoneSignaller with DeviceInfo { def redstone: EnvironmentHost var wirelessFrequency = 0 @@ -118,17 +117,17 @@ trait RedstoneWireless extends RedstoneSignaller with WirelessReceivingDevice wi private final val WirelessInputTag = "wirelessInput" private final val WirelessOutputTag = "wirelessOutput" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - wirelessFrequency = nbt.getInteger(WirelessFrequencyTag) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + wirelessFrequency = nbt.getInt(WirelessFrequencyTag) wirelessInput = nbt.getBoolean(WirelessInputTag) wirelessOutput = nbt.getBoolean(WirelessOutputTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setInteger(WirelessFrequencyTag, wirelessFrequency) - nbt.setBoolean(WirelessInputTag, wirelessInput) - nbt.setBoolean(WirelessOutputTag, wirelessOutput) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putInt(WirelessFrequencyTag, wirelessFrequency) + nbt.putBoolean(WirelessInputTag, wirelessInput) + nbt.putBoolean(WirelessOutputTag, wirelessOutput) } } diff --git a/src/main/scala/li/cil/oc/server/component/Robot.scala b/src/main/scala/li/cil/oc/server/component/Robot.scala index 0dced123ba..85a1cbf48e 100644 --- a/src/main/scala/li/cil/oc/server/component/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/Robot.scala @@ -23,9 +23,9 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumParticleTypes +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.ParticleTypes +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ @@ -43,7 +43,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with DeviceAttribute.Description -> "Robot", DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor, DeviceAttribute.Product -> "Caterpillar", - DeviceAttribute.Capacity -> agent.getSizeInventory.toString + DeviceAttribute.Capacity -> agent.getContainerSize.toString ) override def getDeviceInfo: util.Map[String, String] = deviceInfo @@ -73,7 +73,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with @Callback(doc = "function():number -- Get the durability of the currently equipped tool.") def durability(context: Context, args: Arguments): Array[AnyRef] = { - StackOption(agent.equipmentInventory.getStackInSlot(0)) match { + StackOption(agent.equipmentInventory.getItem(0)) match { case SomeStack(item) => ToolDurabilityProviders.getDurability(item) match { case Some(durability) => result(durability) @@ -97,7 +97,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with val (something, what) = blockContent(direction) if (something) { context.pause(0.4) - PacketSender.sendParticleEffect(BlockPosition(agent), EnumParticleTypes.CRIT, 8, 0.25, Some(direction)) + PacketSender.sendParticleEffect(BlockPosition(agent), ParticleTypes.CRIT, 8, 0.25, Some(direction)) result(Unit, what) } else { @@ -111,7 +111,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with else { node.changeBuffer(Settings.get.robotMoveCost) context.pause(0.4) - PacketSender.sendParticleEffect(BlockPosition(agent), EnumParticleTypes.CRIT, 8, 0.25, Some(direction)) + PacketSender.sendParticleEffect(BlockPosition(agent), ParticleTypes.CRIT, 8, 0.25, Some(direction)) result(Unit, "impossible move") } } @@ -122,8 +122,8 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with def turn(context: Context, args: Arguments): Array[AnyRef] = { val clockwise = args.checkBoolean(0) if (node.tryChangeBuffer(-Settings.get.robotTurnCost)) { - if (clockwise) agent.rotate(EnumFacing.UP) - else agent.rotate(EnumFacing.DOWN) + if (clockwise) agent.rotate(Direction.UP) + else agent.rotate(Direction.DOWN) agent.animateTurn(clockwise, Settings.get.turnDelay) context.pause(Settings.get.turnDelay) result(true) @@ -157,13 +157,13 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with private final val RomRobotTag = "romRobot" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - romRobot.foreach(_.load(nbt.getCompoundTag(RomRobotTag))) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + romRobot.foreach(_.loadData(nbt.getCompound(RomRobotTag))) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - romRobot.foreach(fs => nbt.setNewCompoundTag(RomRobotTag, fs.save)) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + romRobot.foreach(fs => nbt.setNewCompoundTag(RomRobotTag, fs.saveData)) } } diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index 6710a8695d..e55d138b35 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -30,21 +30,22 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.server.network.Connector import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.world.World import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.LazyOptional import scala.collection.convert.WrapAsJava._ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment with MachineHost with ServerInventory with ComponentInventory with Analyzable with internal.Server with ICapabilityProvider with DeviceInfo { lazy val machine: api.machine.Machine = Machine.create(this) - val node: Node = if (!rack.world.isRemote) machine.node else null + val node: Node = if (!rack.world.isClientSide) machine.node else null var wasRunning = false var hadErrored = false @@ -56,7 +57,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit DeviceAttribute.Description -> "Server", DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor, DeviceAttribute.Product -> "Blader", - DeviceAttribute.Capacity -> getSizeInventory.toString + DeviceAttribute.Capacity -> getContainerSize.toString ) override def getDeviceInfo: util.Map[String, String] = deviceInfo @@ -81,25 +82,25 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit private final val MachineTag = "machine" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - if (!rack.world.isRemote) { - machine.load(nbt.getCompoundTag(MachineTag)) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + if (!rack.world.isClientSide) { + machine.loadData(nbt.getCompound(MachineTag)) } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - if (!rack.world.isRemote) { - nbt.setNewCompoundTag(MachineTag, machine.save) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + if (!rack.world.isClientSide) { + nbt.setNewCompoundTag(MachineTag, machine.saveData) } } // ----------------------------------------------------------------------- // // MachineHost - override def internalComponents(): Iterable[ItemStack] = (0 until getSizeInventory).collect { - case i if !getStackInSlot(i).isEmpty && isComponentSlot(i, getStackInSlot(i)) => getStackInSlot(i) + override def internalComponents(): Iterable[ItemStack] = (0 until getContainerSize).collect { + case i if !getItem(i).isEmpty && isComponentSlot(i, getItem(i)) => getItem(i) } override def componentSlot(address: String): Int = components.indexWhere(_.exists(env => env.node != null && env.node.address == address)) @@ -129,7 +130,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit case _ => 0 } - override def isUsableByPlayer(player: EntityPlayer): Boolean = rack.isUsableByPlayer(player) + override def stillValid(player: PlayerEntity): Boolean = rack.stillValid(player) // ----------------------------------------------------------------------- // // ItemStackInventory @@ -139,7 +140,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit // ----------------------------------------------------------------------- // // ComponentInventory - override def container: ItemStack = rack.getStackInSlot(slot) + override def container: ItemStack = rack.getItem(slot) override protected def connectItemNode(node: Node) { if (node != null) { @@ -150,7 +151,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit override protected def onItemRemoved(slot: Int, stack: ItemStack): Unit = { super.onItemRemoved(slot, stack) - if (!rack.world.isRemote) { + if (!rack.world.isClientSide) { val slotType = InventorySlots.server(tier)(slot).slot if (slotType == Slot.CPU) { machine.stop() @@ -161,12 +162,12 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit // ----------------------------------------------------------------------- // // RackMountable - override def getData: NBTTagCompound = { - val nbt = new NBTTagCompound() - nbt.setBoolean("isRunning", wasRunning) - nbt.setBoolean("hasErrored", hadErrored) - nbt.setLong("lastFileSystemAccess", lastFileSystemAccess) - nbt.setLong("lastNetworkActivity", lastNetworkActivity) + override def getData: CompoundNBT = { + val nbt = new CompoundNBT() + nbt.putBoolean("isRunning", wasRunning) + nbt.putBoolean("hasErrored", hadErrored) + nbt.putLong("lastFileSystemAccess", lastFileSystemAccess) + nbt.putLong("lastNetworkActivity", lastNetworkActivity) nbt } @@ -179,10 +180,10 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit case Some(busConnectable: RackBusConnectable) => busConnectable }.apply(index) - override def onActivate(player: EntityPlayer, hand: EnumHand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { - if (!player.getEntityWorld.isRemote) { - if (player.isSneaking) { - if (!machine.isRunning && isUsableByPlayer(player)) { + override def onActivate(player: PlayerEntity, hand: Hand, heldItem: ItemStack, hitX: Float, hitY: Float): Boolean = { + if (!player.level.isClientSide) { + if (player.isCrouching) { + if (!machine.isRunning && stillValid(player)) { wasRunning = false hadErrored = false machine.start() @@ -190,7 +191,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit } else { val position = BlockPosition(rack) - player.openGui(OpenComputers, GuiType.ServerInRack.id, world, position.x, GuiType.embedSlot(position.y, slot), position.z) + OpenComputers.openGui(player, GuiType.ServerInRack.id, world, position.x, GuiType.embedSlot(position.y, slot), position.z) } } true @@ -202,7 +203,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit override def canUpdate: Boolean = true override def update(): Unit = { - if (!rack.world.isRemote) { + if (!rack.world.isClientSide) { machine.update() val isRunning = machine.isRunning @@ -229,17 +230,19 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit // ----------------------------------------------------------------------- // // Analyzable - override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) + override def onAnalyze(player: PlayerEntity, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = Array(machine.node) // ----------------------------------------------------------------------- // // ICapabilityProvider - override def hasCapability(capability: Capability[_], facing: EnumFacing): Boolean = components.exists { - case Some(component: ICapabilityProvider) => component.hasCapability(capability, host.toLocal(facing)) - case _ => false + override def getCapability[T](capability: Capability[T], facing: Direction): LazyOptional[T] = { + for (curr <- components) curr match { + case Some(comp: ICapabilityProvider) => { + val cap = comp.getCapability(capability, host.toLocal(facing)) + if (cap.isPresent) return cap + } + case _ => + } + LazyOptional.empty[T] } - - override def getCapability[T](capability: Capability[T], facing: EnumFacing): T = components.collectFirst { - case Some(component: ICapabilityProvider) if component.hasCapability(capability, host.toLocal(facing)) => component.getCapability[T](capability, host.toLocal(facing)) - }.getOrElse(null.asInstanceOf[T]) } diff --git a/src/main/scala/li/cil/oc/server/component/Tablet.scala b/src/main/scala/li/cil/oc/server/component/Tablet.scala index 5a5258a93a..d3848c8527 100644 --- a/src/main/scala/li/cil/oc/server/component/Tablet.scala +++ b/src/main/scala/li/cil/oc/server/component/Tablet.scala @@ -29,7 +29,7 @@ class Tablet(val tablet: TabletWrapper) extends AbstractManagedEnvironment with DeviceAttribute.Description -> "Tablet", DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor, DeviceAttribute.Product -> "Jogger", - DeviceAttribute.Capacity -> tablet.getSizeInventory.toString + DeviceAttribute.Capacity -> tablet.getContainerSize.toString ) override def getDeviceInfo: util.Map[String, String] = deviceInfo @@ -37,8 +37,8 @@ class Tablet(val tablet: TabletWrapper) extends AbstractManagedEnvironment with // ----------------------------------------------------------------------- // @Callback(doc = """function():number -- Gets the pitch of the player holding the tablet.""") - def getPitch(context: Context, args: Arguments): Array[AnyRef] = result(tablet.player.rotationPitch) + def getPitch(context: Context, args: Arguments): Array[AnyRef] = result(tablet.player.xRot) @Callback(doc = """function():number -- Gets the yaw of the player holding the tablet.""") - def getYaw(context: Context, args: Arguments): Array[AnyRef] = result(tablet.player.rotationYaw) + def getYaw(context: Context, args: Arguments): Array[AnyRef] = result(tablet.player.yRot) } diff --git a/src/main/scala/li/cil/oc/server/component/Trade.scala b/src/main/scala/li/cil/oc/server/component/Trade.scala index c3c7a8f717..6489ea5896 100644 --- a/src/main/scala/li/cil/oc/server/component/Trade.scala +++ b/src/main/scala/li/cil/oc/server/component/Trade.scala @@ -9,14 +9,17 @@ import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.common.EventHandler import li.cil.oc.util.InventoryUtils import net.minecraft.entity.Entity -import net.minecraft.entity.IMerchant +import net.minecraft.entity.merchant.IMerchant import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.item.MerchantOffer +import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.util.ResourceLocation +import net.minecraft.util.RegistryKey import net.minecraft.util.math.BlockPos -import net.minecraft.village.MerchantRecipe -import net.minecraftforge.common.DimensionManager +import net.minecraft.util.registry.Registry +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.WrapAsScala._ import scala.ref.WeakReference @@ -30,15 +33,15 @@ class Trade(val info: TradeInfo) extends AbstractValue { def maxRange = Settings.get.tradingRange def isInRange = (info.merchant.get, info.host) match { - case (Some(merchant: Entity), Some(host)) => merchant.getDistanceSq(host.xPosition, host.yPosition, host.zPosition) < maxRange * maxRange + case (Some(merchant: Entity), Some(host)) => merchant.distanceToSqr(host.xPosition, host.yPosition, host.zPosition) < maxRange * maxRange case _ => false } // Queue the load because when load is called we can't access the world yet // and we need to access it to get the Robot's TileEntity / Drone's Entity. - override def load(nbt: NBTTagCompound) = EventHandler.scheduleServer(() => info.load(nbt)) + override def loadData(nbt: CompoundNBT) = EventHandler.scheduleServer(() => info.loadData(nbt)) - override def save(nbt: NBTTagCompound) = info.save(nbt) + override def saveData(nbt: CompoundNBT) = info.saveData(nbt) @Callback(doc = "function():number -- Returns a sort index of the merchant that provides this trade") def getMerchantId(context: Context, arguments: Arguments): Array[AnyRef] = @@ -46,16 +49,16 @@ class Trade(val info: TradeInfo) extends AbstractValue { @Callback(doc = "function():table, table -- Returns the items the merchant wants for this trade.") def getInput(context: Context, arguments: Arguments): Array[AnyRef] = - result(info.recipe.map(_.getItemToBuy.copy()).orNull, - if (info.recipe.exists(_.hasSecondItemToBuy)) info.recipe.map(_.getSecondItemToBuy.copy()).orNull else null) + result(info.recipe.map(_.getCostA.copy()).orNull, + if (info.recipe.exists(!_.getCostB.isEmpty)) info.recipe.map(_.getCostB.copy()).orNull else null) @Callback(doc = "function():table -- Returns the item the merchant offers for this trade.") def getOutput(context: Context, arguments: Arguments): Array[AnyRef] = - result(info.recipe.map(_.getItemToSell.copy()).orNull) + result(info.recipe.map(_.getResult.copy()).orNull) @Callback(doc = "function():boolean -- Returns whether the merchant currently wants to trade this.") def isEnabled(context: Context, arguments: Arguments): Array[AnyRef] = - result(info.merchant.get.exists(merchant => !info.recipe.exists(_.isRecipeDisabled))) // Make sure merchant is neither dead/gone nor the recipe has been disabled. + result(info.merchant.get.exists(merchant => !info.recipe.exists(_.isOutOfStock))) // Make sure merchant is neither dead/gone nor the recipe has been disabled. @Callback(doc = "function():boolean, string -- Returns true when trade succeeds and nil, error when not.") def trade(context: Context, arguments: Arguments): Array[AnyRef] = { @@ -64,15 +67,15 @@ class Trade(val info: TradeInfo) extends AbstractValue { case Some(inventory) => // Make sure merchant hasn't died, it somehow gone or moved out of range and still wants to trade this. info.merchant.get match { - case Some(merchant: Entity) if merchant.isEntityAlive && isInRange => - if (!merchant.isEntityAlive) { + case Some(merchant: Entity) if merchant.isAlive && isInRange => + if (!merchant.isAlive) { result(false, "trader died") } else if (!isInRange) { result(false, "out of range") } else { info.recipe match { case Some(recipe) => - if (recipe.isRecipeDisabled) { + if (recipe.isOutOfStock) { result(false, "trade is disabled") } else { if (!hasRoomForRecipe(inventory, recipe)) { @@ -94,18 +97,18 @@ class Trade(val info: TradeInfo) extends AbstractValue { } } - def hasRoomForRecipe(inventory: IInventory, recipe: MerchantRecipe) : Boolean = { - val remainder = recipe.getItemToSell.copy() + def hasRoomForRecipe(inventory: IInventory, recipe: MerchantOffer) : Boolean = { + val remainder = recipe.getResult.copy() InventoryUtils.insertIntoInventory(remainder, InventoryUtils.asItemHandler(inventory), remainder.getCount, simulate = true) remainder.getCount == 0 } - def completeTrade(inventory: IInventory, recipe: MerchantRecipe, exact: Boolean) : Boolean = { + def completeTrade(inventory: IInventory, recipe: MerchantOffer, exact: Boolean) : Boolean = { // Now we'll check if we have enough items to perform the trade, caching first info.merchant.get match { case Some(merchant) => { - val firstInputStack = recipe.getItemToBuy - val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None + val firstInputStack = recipe.getCostA + val secondInputStack = if (!recipe.getCostB.isEmpty) Option(recipe.getCostB) else None def containsAccumulativeItemStack(stack: ItemStack) = InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true, exact = exact).getCount == 0 @@ -115,7 +118,7 @@ class Trade(val info: TradeInfo) extends AbstractValue { return false // Now we need to check if we have enough inventory space to accept the item we get for the trade. - val outputStack = recipe.getItemToSell.copy() + val outputStack = recipe.getResult.copy() // We established that out inventory allows to perform the trade, now actually do the trade. InventoryUtils.extractFromInventory(firstInputStack, InventoryUtils.asItemHandler(inventory), exact = exact) @@ -123,7 +126,7 @@ class Trade(val info: TradeInfo) extends AbstractValue { InventoryUtils.insertIntoInventory(outputStack, InventoryUtils.asItemHandler(inventory), outputStack.getCount) // Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes. - merchant.useRecipe(recipe) + merchant.notifyTrade(recipe) true } case _ => false @@ -137,7 +140,7 @@ class TradeInfo(var host: Option[EnvironmentHost], var merchant: WeakReference[I def this(host: EnvironmentHost, merchant: IMerchant, recipeID: Int, merchantID: Int) = this(Option(host), new WeakReference[IMerchant](merchant), recipeID, merchantID) - def recipe = merchant.get.map(_.getRecipes(null).get(recipeID)) + def recipe = merchant.get.map(_.getOffers.get(recipeID)) def inventory = host match { case Some(agent: li.cil.oc.api.internal.Agent) => Option(agent.mainInventory()) @@ -156,7 +159,7 @@ class TradeInfo(var host: Option[EnvironmentHost], var merchant: WeakReference[I private final val RecipeID = "recipeID" private final val MerchantID = "merchantID" - def load(nbt: NBTTagCompound): Unit = { + def loadData(nbt: CompoundNBT): Unit = { val isEntity = nbt.getBoolean(HostIsEntityTag) // If drone we find it again by its UUID, if Robot we know the X/Y/Z of the TileEntity. host = if (isEntity) loadHostEntity(nbt) else loadHostTileEntity(nbt) @@ -164,61 +167,60 @@ class TradeInfo(var host: Option[EnvironmentHost], var merchant: WeakReference[I case Some(merchant: IMerchant) => merchant case _ => null }) - recipeID = nbt.getInteger(RecipeID) - merchantID = if (nbt.hasKey(MerchantID)) nbt.getInteger(MerchantID) else -1 + recipeID = nbt.getInt(RecipeID) + merchantID = if (nbt.contains(MerchantID)) nbt.getInt(MerchantID) else -1 } - def save(nbt: NBTTagCompound): Unit = { + def saveData(nbt: CompoundNBT): Unit = { host match { case Some(entity: Entity) => - nbt.setBoolean(HostIsEntityTag, true) - nbt.setInteger(DimensionIDTag, entity.world.provider.getDimension) - nbt.setLong(HostUUIDLeast, entity.getPersistentID.getLeastSignificantBits) - nbt.setLong(HostUUIDMost, entity.getPersistentID.getMostSignificantBits) + nbt.putBoolean(HostIsEntityTag, true) + nbt.putString(DimensionIDTag, entity.world.dimension.location.toString) + nbt.putLong(HostUUIDLeast, entity.getUUID.getLeastSignificantBits) + nbt.putLong(HostUUIDMost, entity.getUUID.getMostSignificantBits) case Some(tileEntity: TileEntity) => - nbt.setBoolean(HostIsEntityTag, false) - nbt.setInteger(DimensionIDTag, tileEntity.getWorld.provider.getDimension) - nbt.setInteger(HostXTag, tileEntity.getPos.getX) - nbt.setInteger(HostYTag, tileEntity.getPos.getY) - nbt.setInteger(HostZTag, tileEntity.getPos.getZ) + nbt.putBoolean(HostIsEntityTag, false) + nbt.putString(DimensionIDTag, tileEntity.getLevel.dimension.location.toString) + nbt.putInt(HostXTag, tileEntity.getBlockPos.getX) + nbt.putInt(HostYTag, tileEntity.getBlockPos.getY) + nbt.putInt(HostZTag, tileEntity.getBlockPos.getZ) case _ => // Welp! } merchant.get match { case Some(entity: Entity) => - nbt.setLong(MerchantUUIDLeastTag, entity.getPersistentID.getLeastSignificantBits) - nbt.setLong(MerchantUUIDMostTag, entity.getPersistentID.getMostSignificantBits) + nbt.putLong(MerchantUUIDLeastTag, entity.getUUID.getLeastSignificantBits) + nbt.putLong(MerchantUUIDMostTag, entity.getUUID.getMostSignificantBits) case _ => } - nbt.setInteger(RecipeID, recipeID) - nbt.setInteger(MerchantID, merchantID) + nbt.putInt(RecipeID, recipeID) + nbt.putInt(MerchantID, merchantID) } - private def loadEntity(nbt: NBTTagCompound, uuid: UUID): Option[Entity] = { - val dimension = nbt.getInteger(DimensionIDTag) - val world = DimensionManager.getWorld(dimension) + private def loadEntity(nbt: CompoundNBT, uuid: UUID): Option[Entity] = { + val dimension = new ResourceLocation(nbt.getString(DimensionIDTag)) + val dimKey = RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension) + val world = ServerLifecycleHooks.getCurrentServer.getLevel(dimKey) - world.loadedEntityList.find { - case entity: Entity if entity.getPersistentID == uuid => true - case _ => false - } + Option(world.getEntity(uuid)) } - private def loadHostEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = { + private def loadHostEntity(nbt: CompoundNBT): Option[EnvironmentHost] = { loadEntity(nbt, new UUID(nbt.getLong(HostUUIDMost), nbt.getLong(HostUUIDLeast))) match { case Some(entity: Entity with li.cil.oc.api.internal.Agent) => Option(entity: EnvironmentHost) case _ => None } } - private def loadHostTileEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = { - val dimension = nbt.getInteger(DimensionIDTag) - val world = DimensionManager.getWorld(dimension) + private def loadHostTileEntity(nbt: CompoundNBT): Option[EnvironmentHost] = { + val dimension = new ResourceLocation(nbt.getString(DimensionIDTag)) + val dimKey = RegistryKey.create(Registry.DIMENSION_REGISTRY, dimension) + val world = ServerLifecycleHooks.getCurrentServer.getLevel(dimKey) - val x = nbt.getInteger(HostXTag) - val y = nbt.getInteger(HostYTag) - val z = nbt.getInteger(HostZTag) + val x = nbt.getInt(HostXTag) + val y = nbt.getInt(HostYTag) + val z = nbt.getInt(HostZTag) - world.getTileEntity(new BlockPos(x, y, z)) match { + world.getBlockEntity(new BlockPos(x, y, z)) match { case robotProxy: li.cil.oc.common.tileentity.RobotProxy => Option(robotProxy.robot) case agent: li.cil.oc.api.internal.Agent => Option(agent) case null => None diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala b/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala index 0925a5e3a0..9209c877f1 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala @@ -12,12 +12,12 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList -import net.minecraft.util.EnumFacing -import net.minecraft.world.WorldServer +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT +import net.minecraft.util.Direction +import net.minecraft.world.server.ServerWorld import scala.collection.convert.WrapAsJava._ @@ -40,8 +40,8 @@ class UpgradeBarcodeReader(val host: EnvironmentHost) extends AbstractManagedEnv super.onMessage(message) if (message.name == "tablet.use") message.source.host match { case machine: api.machine.Machine => (machine.host, message.data) match { - case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => - host.world.getTileEntity(blockPos) match { + case (tablet: internal.Tablet, Array(nbt: CompoundNBT, stack: ItemStack, player: PlayerEntity, blockPos: BlockPosition, side: Direction, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => + host.world.getBlockEntity(blockPos) match { case analyzable: Analyzable => processNodes(analyzable.onAnalyze(player, side, hitX.toFloat, hitY.toFloat, hitZ.toFloat), nbt) case host: SidedEnvironment => @@ -56,25 +56,25 @@ class UpgradeBarcodeReader(val host: EnvironmentHost) extends AbstractManagedEnv } } - private def processNodes(nodes: Array[Node], nbt: NBTTagCompound): Unit = if (nodes != null) { - val readerNBT = new NBTTagList() + private def processNodes(nodes: Array[Node], nbt: CompoundNBT): Unit = if (nodes != null) { + val readerNBT = new ListNBT() for (node <- nodes if node != null) { - val nodeNBT = new NBTTagCompound() + val nodeNBT = new CompoundNBT() node match { case component: Component => - nodeNBT.setString("type", component.name) + nodeNBT.putString("type", component.name) case _ => } val address = node.address() if (address != null && !address.isEmpty) { - nodeNBT.setString("address", node.address()) + nodeNBT.putString("address", node.address()) } - readerNBT.appendTag(nodeNBT) + readerNBT.add(nodeNBT) } - nbt.setTag("analyzed", readerNBT) + nbt.put("analyzed", readerNBT) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala index 624ee4eb61..eea7fddc48 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala @@ -17,9 +17,10 @@ import li.cil.oc.api.network._ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.common.event.ChunkloaderUpgradeHandler -import net.minecraftforge.common.ForgeChunkManager -import net.minecraftforge.common.ForgeChunkManager.Ticket import net.minecraft.entity.Entity +import net.minecraft.util.math.ChunkPos +import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld import scala.collection.convert.WrapAsJava._ @@ -38,17 +39,19 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir override def getDeviceInfo: util.Map[String, String] = deviceInfo - var ticket: Option[Ticket] = None + var ticket: Option[ChunkPos] = None override val canUpdate = true override def update() { super.update() - if (host.world.getTotalWorldTime % Settings.get.tickFrequency == 0 && ticket.isDefined) { + if (host.world.getGameTime % Settings.get.tickFrequency == 0 && ticket.isDefined) { if (!node.tryChangeBuffer(-Settings.get.chunkloaderCost * Settings.get.tickFrequency)) { - ticket.foreach(ticket => try ForgeChunkManager.releaseTicket(ticket) catch { - case _: Throwable => // Ignored. - }) + host.world match { + case world: ServerWorld => { + ticket.foreach(pos => ChunkloaderUpgradeHandler.releaseTicket(world, node.address, pos)) + } + } ticket = None } else if (host.isInstanceOf[Entity]) // Robot move events are not fired for entities (drones) @@ -65,15 +68,15 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir override def onConnect(node: Node) { super.onConnect(node) if (node == this.node) { - val restoredTicket = ChunkloaderUpgradeHandler.restoredTickets.remove(node.address) + val restoredTicket = ChunkloaderUpgradeHandler.claimTicket(node.address) if (restoredTicket.isDefined) { if (!isDimensionAllowed) { - try ForgeChunkManager.releaseTicket(restoredTicket.get) catch { - case _: Throwable => // Ignored. + host.world match { + case world: ServerWorld => ChunkloaderUpgradeHandler.releaseTicket(world, node.address, restoredTicket.get) } - OpenComputers.log.info(s"Releasing chunk loader ticket at (${host.xPosition()}, ${host.yPosition()}, ${host.zPosition()}) in blacklisted dimension ${host.world().provider.getDimension}.") + OpenComputers.log.info(s"Releasing chunk loader ticket at (${host.xPosition()}, ${host.yPosition()}, ${host.zPosition()}) in blacklisted dimension ${host.world().dimension}.") } else { - OpenComputers.log.info(s"Reclaiming chunk loader ticket at (${host.xPosition()}, ${host.yPosition()}, ${host.zPosition()}) in dimension ${host.world().provider.getDimension}.") + OpenComputers.log.info(s"Reclaiming chunk loader ticket at (${host.xPosition()}, ${host.yPosition()}, ${host.zPosition()}) in dimension ${host.world().dimension}.") ticket = restoredTicket ChunkloaderUpgradeHandler.updateLoadedChunk(this) } @@ -87,8 +90,8 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir override def onDisconnect(node: Node) { super.onDisconnect(node) if (node == this.node) { - ticket.foreach(ticket => try ForgeChunkManager.releaseTicket(ticket) catch { - case _: Throwable => // Ignored. + ticket.foreach(pos => host.world match { + case world: ServerWorld => ChunkloaderUpgradeHandler.releaseTicket(world, node.address, pos) }) ticket = None } @@ -110,8 +113,8 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir ticket.isDefined } else if (!enabled && ticket.isDefined) { - ticket.foreach(ticket => try ForgeChunkManager.releaseTicket(ticket) catch { - case _: Throwable => // Ignored. + ticket.foreach(pos => host.world match { + case world: ServerWorld => ChunkloaderUpgradeHandler.releaseTicket(world, node.address, pos) }) ticket = None true @@ -120,8 +123,14 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir } } + @Deprecated private def isDimensionAllowed: Boolean = { - val id: Int = host.world().provider.getDimension + val id: Int = host.world().dimension match { + case World.OVERWORLD => 0 + case World.NETHER => -1 + case World.END => 1 + case _ => throw new Error("deprecated") + } val whitelist = Settings.get.chunkloadDimensionWhitelist val blacklist = Settings.get.chunkloadDimensionBlacklist if (!whitelist.isEmpty) { @@ -142,7 +151,8 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvir throw new Exception("this dimension is blacklisted") } } else { - ticket = Option(ForgeChunkManager.requestTicket(OpenComputers, host.world, ForgeChunkManager.Type.NORMAL)) + // This ticket is a lie, but ChunkloaderUpgradeHandler won't crash or load it. + ticket = Some(new ChunkPos(0, 0)) ChunkloaderUpgradeHandler.updateLoadedChunk(this) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala index cdae85f859..d4a0f67734 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala @@ -15,10 +15,12 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.crafting.IRecipeType +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory -import net.minecraft.inventory.{IInventory, InventoryCraftResult, SlotCrafting} -import net.minecraft.item.crafting.CraftingManager +import net.minecraft.inventory.{CraftResultInventory, IInventory} +import net.minecraft.inventory.container.Container +import net.minecraft.inventory.container.CraftingResultSlot import scala.collection.convert.WrapAsJava._ @@ -42,29 +44,30 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends Abs result(CraftingInventory.craft(count): _*) } - private object CraftingInventory extends inventory.InventoryCrafting(new inventory.Container { - override def canInteractWith(player: EntityPlayer) = true + private object CraftingInventory extends inventory.CraftingInventory(new Container(null, 0) { + override def stillValid(player: PlayerEntity) = true }, 3, 3) { def craft(wantedCount: Int): Seq[_] = { val player = host.player copyItemsFromHost(player.inventory) var countCrafted = 0 - val initialCraft = CraftingManager.findMatchingRecipe(CraftingInventory, host.world) - if (initialCraft != null) { + val manager = host.world.getRecipeManager + val initialCraft = manager.getRecipeFor(IRecipeType.CRAFTING, CraftingInventory: inventory.CraftingInventory, host.world) + if (initialCraft.isPresent) { def tryCraft() : Boolean = { - val craft = CraftingManager.findMatchingRecipe(CraftingInventory, host.world) - if (craft == null || craft != initialCraft) { + val craft = manager.getRecipeFor(IRecipeType.CRAFTING, CraftingInventory: inventory.CraftingInventory, host.world) + if (craft != initialCraft) { return false } - val craftResult = new InventoryCraftResult - val craftingSlot = new SlotCrafting(player, CraftingInventory, craftResult, 0, 0, 0) - val craftedResult = craft.getCraftingResult(this) - craftResult.setInventorySlotContents(0, craftedResult) - if (!craftingSlot.getHasStack) + val craftResult = new CraftResultInventory + val craftingSlot = new CraftingResultSlot(player, CraftingInventory, craftResult, 0, 0, 0) + val craftedResult = craft.get.assemble(this) + craftResult.setItem(0, craftedResult) + if (!craftingSlot.hasItem) return false - val stack = craftingSlot.decrStackSize(1) + val stack = craftingSlot.remove(1) countCrafted += stack.getCount max 1 val taken = craftingSlot.onTake(player, stack) copyItemsToHost(player.inventory) @@ -82,15 +85,15 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends Abs } def copyItemsFromHost(inventory: IInventory) { - for (slot <- 0 until getSizeInventory) { - val stack = inventory.getStackInSlot(toParentSlot(slot)) - setInventorySlotContents(slot, stack) + for (slot <- 0 until getContainerSize) { + val stack = inventory.getItem(toParentSlot(slot)) + setItem(slot, stack) } } def copyItemsToHost(inventory: IInventory) { - for (slot <- 0 until getSizeInventory) { - inventory.setInventorySlotContents(toParentSlot(slot), getStackInSlot(slot)) + for (slot <- 0 until getContainerSize) { + inventory.setItem(toParentSlot(slot), getItem(slot)) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala index 13d86017da..a46e2a1d3e 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala @@ -39,20 +39,20 @@ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment w override def getDeviceInfo: util.Map[String, String] = deviceInfo - override def size = data.getSizeInventory + override def size = data.getContainerSize - override def getStackInSlot(slot: Int) = StackOption(data.getStackInSlot(slot)).map(_.copy()).orEmpty + override def getStackInSlot(slot: Int) = StackOption(data.getItem(slot)).map(_.copy()).orEmpty - override def setStackInSlot(slot: Int, stack: ItemStack) = data.setInventorySlotContents(slot, stack) + override def setStackInSlot(slot: Int, stack: ItemStack) = data.setItem(slot, stack) override def findStackWithHash(needle: String) = indexOf(needle) @Callback(doc = "function(slot:number):table -- Get the representation of the item stack stored in the specified slot.") - def get(context: Context, args: Arguments): Array[AnyRef] = result(data.getStackInSlot(args.checkSlot(data, 0))) + def get(context: Context, args: Arguments): Array[AnyRef] = result(data.getItem(args.checkSlot(data, 0))) @Callback(doc = "function(slot:number):string -- Computes a hash value for the item stack in the specified slot.") def computeHash(context: Context, args: Arguments): Array[AnyRef] = { - data.getStackInSlot(args.checkSlot(data, 0)) match { + data.getItem(args.checkSlot(data, 0)) match { case stack: ItemStack => val hash = Hashing.sha256().hashBytes(ItemUtils.saveStack(stack)) result(hash.toString) @@ -66,19 +66,19 @@ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment w @Callback(doc = "function(slot:number):boolean -- Clears the specified slot. Returns true if there was something in the slot before.") def clear(context: Context, args: Arguments): Array[AnyRef] = { val slot = args.checkSlot(data, 0) - val nonEmpty = data.getStackInSlot(slot) != ItemStack.EMPTY // zero size stacks - data.setInventorySlotContents(slot, ItemStack.EMPTY) + val nonEmpty = data.getItem(slot) != ItemStack.EMPTY // zero size stacks + data.setItem(slot, ItemStack.EMPTY) result(nonEmpty) } @Callback(doc = "function(fromSlot:number, toSlot:number[, address:string]):boolean -- Copies an entry to another slot, optionally to another database. Returns true if something was overwritten.") def copy(context: Context, args: Arguments): Array[AnyRef] = { val fromSlot = args.checkSlot(data, 0) - val entry = data.getStackInSlot(fromSlot) + val entry = data.getItem(fromSlot) def set(inventory: IInventory) = { val toSlot = args.checkSlot(inventory, 1) - val nonEmpty = inventory.getStackInSlot(toSlot) != ItemStack.EMPTY // zero size stacks - inventory.setInventorySlotContents(toSlot, entry.copy()) + val nonEmpty = inventory.getItem(toSlot) != ItemStack.EMPTY // zero size stacks + inventory.setItem(toSlot, entry.copy()) result(nonEmpty) } if (args.count > 2) DatabaseAccess.withDatabase(node, args.checkString(2), database => set(database.data)) @@ -88,9 +88,9 @@ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment w @Callback(doc = "function(address:string):number -- Copies the data stored in this database to another database with the specified address.") def clone(context: Context, args: Arguments): Array[AnyRef] = { DatabaseAccess.withDatabase(node, args.checkString(0), database => { - val numberToCopy = math.min(data.getSizeInventory, database.data.getSizeInventory) + val numberToCopy = math.min(data.getContainerSize, database.data.getContainerSize) for (slot <- 0 until numberToCopy) { - database.data.setInventorySlotContents(slot, data.getStackInSlot(slot).copy()) + database.data.setItem(slot, data.getItem(slot).copy()) } context.pause(0.25) result(numberToCopy) @@ -98,7 +98,7 @@ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment w } private def indexOf(needle: String, offset: Int = 0): Int = { - for (slot <- 0 until data.getSizeInventory) data.getStackInSlot(slot) match { + for (slot <- 0 until data.getContainerSize) data.getItem(slot) match { case stack: ItemStack => val hash = Hashing.sha256().hashBytes(ItemUtils.saveStack(stack)) if (hash.toString == needle) return slot + offset diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala index 6f2c7f4546..6cd299c625 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala @@ -17,9 +17,9 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.UpgradeExperience import net.minecraft.enchantment.EnchantmentHelper -import net.minecraft.entity.item.EntityXPOrb -import net.minecraft.init.Items -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.entity.item.ExperienceOrbEntity +import net.minecraft.item.Items +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -55,10 +55,10 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A updateXpInfo() } val world = this.host.world - val pos = this.host.player.getPosition - val orb = new EntityXPOrb(world, pos.getX.toDouble + 0.5D, pos.getY.toDouble + 0.5D, pos.getZ.toDouble + 0.5D, value.toInt) - this.host.player.xpCooldown = 0 - orb.onCollideWithPlayer(this.host.player) + val pos = this.host.player.blockPosition + val orb = new ExperienceOrbEntity(world, pos.getX.toDouble + 0.5D, pos.getY.toDouble + 0.5D, pos.getZ.toDouble + 0.5D, value.toInt) + this.host.player.takeXpDelay = 0 + orb.playerTouch(this.host.player) } } @@ -84,25 +84,25 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A if (level >= MaxLevel) { return result(Unit, "max level") } - val stack = host.mainInventory.getStackInSlot(host.selectedSlot) + val stack = host.mainInventory.getItem(host.selectedSlot) if (stack.isEmpty) { return result(Unit, "no item") } var xp = 0 if (stack.getItem == Items.EXPERIENCE_BOTTLE) { - xp += 3 + host.world.rand.nextInt(5) + host.world.rand.nextInt(5) + xp += 3 + host.world.random.nextInt(5) + host.world.random.nextInt(5) } else { for ((enchantment, level) <- EnchantmentHelper.getEnchantments(stack)) { if (enchantment != null) { - xp += enchantment.getMinEnchantability(level) + xp += enchantment.getMinCost(level) } } if (xp <= 0) { return result(Unit, "could not extract experience from item") } } - val consumed = host.mainInventory().decrStackSize(host.selectedSlot, 1) + val consumed = host.mainInventory().removeItem(host.selectedSlot, 1) if (consumed.isEmpty) { return result(Unit, "could not consume item") } @@ -115,13 +115,13 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A case _ => } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) UpgradeExperience.setExperience(nbt, experience) } - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) experience = UpgradeExperience.getExperience(nbt) updateXpInfo() } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index bb0443f5c1..1e1f21ab3b 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -18,10 +18,11 @@ import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ -import net.minecraft.entity.item.EntityItem +import net.minecraft.entity.item.ItemEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntityFurnace +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.text.ITextComponent +import net.minecraftforge.common.ForgeHooks import scala.collection.convert.WrapAsJava._ @@ -50,15 +51,15 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab @Callback(doc = """function([count:number]):boolean -- Tries to insert fuel from the selected slot into the generator's queue.""") def insert(context: Context, args: Arguments): Array[AnyRef] = { val count = args.optInteger(0, 64) - val stack = host.mainInventory.getStackInSlot(host.selectedSlot) + val stack = host.mainInventory.getItem(host.selectedSlot) if (stack.isEmpty) return result(Unit, "selected slot is empty") - if (!TileEntityFurnace.isItemFuel(stack)) { + if (ForgeHooks.getBurnTime(stack, null) <= 0) { return result(Unit, "selected slot does not contain fuel") } - val container: ItemStack = stack.getItem.getContainerItem(stack) + val container: ItemStack = stack.getContainerItem() val inQueue: ItemStack = inventory match { case SomeStack(q) if q != null && q.getCount > 0 => - if (!q.isItemEqual(stack) || !ItemStack.areItemStackTagsEqual(q, stack)) { + if (!q.sameItem(stack) || !ItemStack.tagMatches(q, stack)) { return result(Unit, "different fuel type already queued") } q @@ -70,25 +71,25 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab } val previousSelectedFuel: ItemStack = stack.copy val insertLimit: Int = math.min(stack.getCount, math.min(space, count)) - val fuelToInsert: ItemStack = stack.splitStack(insertLimit) + val fuelToInsert: ItemStack = stack.split(insertLimit) // remove the fuel from the inventory if (stack.getCount == 0) { - host.mainInventory.setInventorySlotContents(host.selectedSlot, ItemStack.EMPTY) + host.mainInventory.setItem(host.selectedSlot, ItemStack.EMPTY) } else { - host.mainInventory.setInventorySlotContents(host.selectedSlot, stack) + host.mainInventory.setItem(host.selectedSlot, stack) } // add empty containers to inventory if (!container.isEmpty) { container.grow(fuelToInsert.getCount - 1) - if (!host.player.inventory.addItemStackToInventory(container)) { + if (!host.player.inventory.add(container)) { // no containers could be placed in inventory, give back the fuel - host.mainInventory.setInventorySlotContents(host.selectedSlot, previousSelectedFuel) + host.mainInventory.setItem(host.selectedSlot, previousSelectedFuel) return result(false, "no space in inventory for fuel containers") } else if (container.getCount > 0) { // not all the containers could be inserted in the inventory - host.player.entityDropItem(container.copy, -0.25f) + host.player.spawnAtLocation(container.copy, -0.25f) } } @@ -102,7 +103,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab @Callback(doc = """function():number -- Get the size of the item stack in the generator's queue.""") def count(context: Context, args: Arguments): Array[AnyRef] = { inventory match { - case SomeStack(stack) => result(stack.getCount, stack.getItem.getItemStackDisplayName(stack)) + case SomeStack(stack) => result(stack.getCount, stack.getItem.getName(stack)) case _ => result(0) } } @@ -120,12 +121,12 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab if (inQueue.isEmpty) { return result(false, "queue is empty") } - val previousSelectedItem: ItemStack = host.mainInventory.getStackInSlot(host.selectedSlot).copy - val emptyContainer: ItemStack = inQueue.getItem.getContainerItem(inQueue) match { + val previousSelectedItem: ItemStack = host.mainInventory.getItem(host.selectedSlot).copy + val emptyContainer: ItemStack = inQueue.getContainerItem match { case requiredContainer if !requiredContainer.isEmpty && requiredContainer.getCount > 0 => previousSelectedItem match { case slotItem: ItemStack if !slotItem.isEmpty && slotItem.getItem == requiredContainer.getItem && - ItemStack.areItemStackTagsEqual(slotItem, requiredContainer) => slotItem.copy + ItemStack.tagMatches(slotItem, requiredContainer) => slotItem.copy case _ => return result(false, "removing this fuel requires the appropriate container in the selected slot") } case _ => ItemStack.EMPTY // nothing to do, nothing required @@ -135,19 +136,19 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab // backup in case of failure val previousQueue = inQueue.copy - val forUser = inQueue.splitStack(removeLimit) + val forUser = inQueue.split(removeLimit) if (!emptyContainer.isEmpty) { - emptyContainer.splitStack(removeLimit) + emptyContainer.split(removeLimit) if (emptyContainer.isEmpty) { - host.mainInventory.setInventorySlotContents(host.selectedSlot, ItemStack.EMPTY) + host.mainInventory.setItem(host.selectedSlot, ItemStack.EMPTY) } else { - host.mainInventory.decrStackSize(host.selectedSlot, removeLimit) + host.mainInventory.removeItem(host.selectedSlot, removeLimit) } } - // addItemStackToInventory splits the input stack by reference - if (!host.player.inventory.addItemStackToInventory(forUser)) { + // add splits the input stack by reference + if (!host.player.inventory.add(forUser)) { // returns false if NO items were inserted - host.mainInventory.setInventorySlotContents(host.selectedSlot, previousSelectedItem) + host.mainInventory.setItem(host.selectedSlot, previousSelectedItem) inventory = StackOption(previousQueue) result (false, "no inventory space available for fuel") } else { @@ -166,7 +167,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab super.update() if (remainingTicks <= 0 && inventory.isDefined) { val stack = inventory.get - remainingTicks = TileEntityFurnace.getItemBurnTime(stack) + remainingTicks = ForgeHooks.getBurnTime(stack, null) if (remainingTicks > 0) { updateClient() stack.shrink(1) @@ -198,10 +199,10 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab inventory match { case SomeStack(stack) => val world = host.world - val entity = new EntityItem(world, host.xPosition, host.yPosition, host.zPosition, stack.copy()) - entity.motionY = 0.04 - entity.setPickupDelay(5) - world.spawnEntity(entity) + val entity = new ItemEntity(world, host.xPosition, host.yPosition, host.zPosition, stack.copy()) + entity.setDeltaMovement(entity.getDeltaMovement.add(0, 0.04, 0)) + entity.setPickUpDelay(5) + world.addFreshEntity(entity) inventory = EmptyStack case _ => } @@ -212,23 +213,23 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab private final val InventoryTag = "inventory" private final val RemainingTicksTag = "remainingTicks" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - inventory = StackOption(new ItemStack(nbt.getCompoundTag("inventory"))) - if (nbt.hasKey(InventoryTag)) { - inventory = StackOption(new ItemStack(nbt.getCompoundTag(InventoryTag))) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + inventory = StackOption(ItemStack.of(nbt.getCompound("inventory"))) + if (nbt.contains(InventoryTag)) { + inventory = StackOption(ItemStack.of(nbt.getCompound(InventoryTag))) } - remainingTicks = nbt.getInteger(RemainingTicksTag) + remainingTicks = nbt.getInt(RemainingTicksTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) inventory match { - case SomeStack(stack) => nbt.setNewCompoundTag(InventoryTag, stack.writeToNBT) + case SomeStack(stack) => nbt.setNewCompoundTag(InventoryTag, stack.save) case _ => } if (remainingTicks > 0) { - nbt.setInteger(RemainingTicksTag, remainingTicks) + nbt.putInt(RemainingTicksTag, remainingTicks) } } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala index 56ad8c71c5..c9974de64b 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala @@ -83,11 +83,11 @@ object UpgradeInventoryController { @Callback(doc = """function():boolean -- Swaps the equipped tool with the content of the currently selected inventory slot.""") def equip(context: Context, args: Arguments): Array[AnyRef] = { - if (inventory.getSizeInventory > 0) { - val equipped = host.getStackInSlot(0) - val selected = inventory.getStackInSlot(selectedSlot) - host.setInventorySlotContents(0, selected) - inventory.setInventorySlotContents(selectedSlot, equipped) + if (inventory.getContainerSize > 0) { + val equipped = host.getItem(0) + val selected = inventory.getItem(selectedSlot) + host.setItem(0, selected) + inventory.setItem(selectedSlot, equipped) result(true) } else result(false) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala index 02d58e1f7e..623e727ab3 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala @@ -21,9 +21,9 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLiving -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagString +import net.minecraft.entity.MobEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.StringNBT import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.WrapAsJava._ @@ -56,12 +56,12 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra if (leashedEntities.size >= MaxLeashedEntities) return result(Unit, "too many leashed entities") val side = args.checkSideAny(0) val nearBounds = position.bounds - val farBounds = nearBounds.offset(side.getFrontOffsetX * 2.0, side.getFrontOffsetY * 2.0, side.getFrontOffsetZ * 2.0) - val bounds = nearBounds.union(farBounds) - entitiesInBounds[EntityLiving](classOf[EntityLiving], bounds).find(_.canBeLeashedTo(fakePlayer)) match { + val farBounds = nearBounds.move(side.getStepX * 2.0, side.getStepY * 2.0, side.getStepZ * 2.0) + val bounds = nearBounds.minmax(farBounds) + entitiesInBounds[MobEntity](classOf[MobEntity], bounds).find(_.canBeLeashed(fakePlayer)) match { case Some(entity) => - entity.setLeashHolder(host, true) - leashedEntities += entity.getUniqueID + entity.setLeashedTo(host, true) + leashedEntities += entity.getUUID context.pause(0.1) result(true) case _ => result(Unit, "no unleashed entity") @@ -82,9 +82,9 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra } private def unleashAll() { - entitiesInBounds(classOf[EntityLiving], position.bounds.grow(5, 5, 5)).foreach(entity => { - if (leashedEntities.contains(entity.getUniqueID) && entity.getLeashHolder == host) { - entity.clearLeashed(true, false) + entitiesInBounds(classOf[MobEntity], position.bounds.inflate(5, 5, 5)).foreach(entity => { + if (leashedEntities.contains(entity.getUUID) && entity.getLeashHolder == host) { + entity.dropLeash(true, false) } }) leashedEntities.clear() @@ -92,18 +92,18 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra private final val LeashedEntitiesTag = "leashedEntities" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - leashedEntities ++= nbt.getTagList(LeashedEntitiesTag, NBT.TAG_STRING). - map((s: NBTTagString) => UUID.fromString(s.getString)) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + leashedEntities ++= nbt.getList(LeashedEntitiesTag, NBT.TAG_STRING). + map((s: StringNBT) => UUID.fromString(s.getAsString)) // Re-acquire leashed entities. Need to do this manually because leashed - // entities only remember their leashee if it's an EntityLivingBase... + // entities only remember their leashee if it's an LivingEntity... EventHandler.scheduleServer(() => { val foundEntities = mutable.Set.empty[UUID] - entitiesInBounds(classOf[EntityLiving], position.bounds.grow(5, 5, 5)).foreach(entity => { - if (leashedEntities.contains(entity.getUniqueID)) { - entity.setLeashHolder(host, true) - foundEntities += entity.getUniqueID + entitiesInBounds(classOf[MobEntity], position.bounds.inflate(5, 5, 5)).foreach(entity => { + if (leashedEntities.contains(entity.getUUID)) { + entity.setLeashedTo(host, true) + foundEntities += entity.getUUID } }) val missing = leashedEntities.diff(foundEntities) @@ -114,8 +114,8 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra }) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) nbt.setNewTagList(LeashedEntitiesTag, leashedEntities.map(_.toString)) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala index b64ded5a1a..6dbc6326c4 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala @@ -17,10 +17,10 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.driver.DriverBlock import li.cil.oc.api.prefab.AbstractManagedEnvironment -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraft.util.math.Vec3d +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d import scala.collection.convert.WrapAsJava._ @@ -29,7 +29,7 @@ import scala.collection.convert.WrapAsJava._ * * @author Sangar, Vexatos */ -class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: EnumFacing) extends AbstractManagedEnvironment with ChangeListener with DeviceInfo { +class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: Direction) extends AbstractManagedEnvironment with ChangeListener with DeviceInfo { override val node = api.Network.newNode(this, Visibility.None). withConnector(). create() @@ -57,9 +57,9 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En } private def updateBoundState() { - if (node != null && node.network != null && coord.world.exists(_.provider.getDimension == host.world.provider.getDimension) - && coord.toVec3.distanceTo(new Vec3d(host.xPosition, host.yPosition, host.zPosition)) <= Settings.get.mfuRange) { - host.world.getTileEntity(coord) match { + if (node != null && node.network != null && coord.world.exists(_.dimension == host.world.dimension) + && coord.toVec3.distanceTo(new Vector3d(host.xPosition, host.yPosition, host.zPosition)) <= Settings.get.mfuRange) { + host.world.getBlockEntity(coord) match { case env: TileEntity with api.network.Environment => otherEnv match { case Some(environment: TileEntity) => @@ -72,7 +72,7 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En otherDrv match { case Some((environment, driver)) => node.disconnect(environment.node) - environment.save(blockData.get.data) + environment.saveData(blockData.get.data) Option(environment.node).foreach(_.remove()) otherDrv = None case _ => // Nothing to do here. @@ -101,7 +101,7 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En val environment = newDriver.createEnvironment(world, coord.toBlockPos, dir) if (environment != null) { otherDrv = Some((environment, newDriver)) - blockData = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) + blockData = Some(new BlockData(environment.getClass.getName, new CompoundNBT())) node.connect(environment.node) } } // else: the more things change, the more they stay the same. @@ -112,10 +112,10 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En otherDrv = Some((environment, newDriver)) blockData match { case Some(data) if data.name == environment.getClass.getName => - environment.load(data.data) + environment.loadData(data.data) case _ => } - blockData = Some(new BlockData(environment.getClass.getName, new NBTTagCompound())) + blockData = Some(new BlockData(environment.getClass.getName, new CompoundNBT())) node.connect(environment.node) } } @@ -123,7 +123,7 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En case Some((environment, driver)) => // We had something there, but it's gone now... node.disconnect(environment.node) - environment.save(blockData.get.data) + environment.saveData(blockData.get.data) Option(environment.node).foreach(_.remove()) otherDrv = None case _ => // Nothing before, nothing now. @@ -143,7 +143,7 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En otherDrv match { case Some((environment, driver)) => node.disconnect(environment.node) - environment.save(blockData.get.data) + environment.saveData(blockData.get.data) Option(environment.node).foreach(_.remove()) otherDrv = None case _ => // Nothing to do here. @@ -158,9 +158,9 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En case Some((env, drv)) if env.canUpdate => env.update() case _ => // No driver } - if (host.world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (host.world.getGameTime % Settings.get.tickFrequency == 0) { if (!node.tryChangeBuffer(-Settings.get.mfuCost * Settings.get.tickFrequency - * coord.toVec3.distanceTo(new Vec3d(host.xPosition, host.yPosition, host.zPosition)))) { + * coord.toVec3.distanceTo(new Vector3d(host.xPosition, host.yPosition, host.zPosition)))) { disconnect() } } @@ -191,30 +191,30 @@ class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: En } } - override def load(nbt: NBTTagCompound) { - super.load(nbt) - Option(nbt.getCompoundTag(Settings.namespace + "adapter.block")) match { - case Some(blockNbt: NBTTagCompound) => - if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) { - blockData = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data"))) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + Option(nbt.getCompound(Settings.namespace + "adapter.block")) match { + case Some(blockNbt: CompoundNBT) => + if (blockNbt.contains("name") && blockNbt.contains("data")) { + blockData = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompound("data"))) } case _ => // Invalid tag } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - val blockNbt = new NBTTagCompound() + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + val blockNbt = new CompoundNBT() blockData.foreach({ data => - otherDrv.foreach(_._1.save(data.data)) - blockNbt.setString("name", data.name) - blockNbt.setTag("data", data.data) + otherDrv.foreach(_._1.saveData(data.data)) + blockNbt.putString("name", data.name) + blockNbt.put("data", data.data) }) - nbt.setTag(Settings.namespace + "adapter.block", blockNbt) + nbt.put(Settings.namespace + "adapter.block", blockNbt) } // ----------------------------------------------------------------------- // - private class BlockData(val name: String, val data: NBTTagCompound) + private class BlockData(val name: String, val data: CompoundNBT) } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index cbe4eee21c..04310bc9f5 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -22,10 +22,10 @@ import li.cil.oc.common.item.data.NavigationUpgradeData import li.cil.oc.common.Tier import li.cil.oc.server.network.Waypoints import li.cil.oc.util.BlockPosition -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumFacing +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.Direction import scala.collection.convert.WrapAsJava._ @@ -53,8 +53,8 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra def getPosition(context: Context, args: Arguments): Array[AnyRef] = { val info = data.mapData(host.world) val size = data.getSize(host.world) - val relativeX = host.xPosition - info.xCenter - val relativeZ = host.zPosition - info.zCenter + val relativeX = host.xPosition - info.x + val relativeZ = host.zPosition - info.z if (math.abs(relativeX) <= size / 2 && math.abs(relativeZ) <= size / 2) result(relativeX, host.yPosition, relativeZ) @@ -78,7 +78,7 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra val positionVec = position.toVec3 val rangeSq = range * range val waypoints = Waypoints.findWaypoints(position, range). - filter(waypoint => waypoint.getDistanceSq(positionVec.x, positionVec.y, positionVec.z) <= rangeSq) + filter(waypoint => positionVec.distanceToSqr(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) <= rangeSq) result(waypoints.map(waypoint => { val delta = waypoint.position.offset(waypoint.facing).toVec3.subtract(positionVec) Map( @@ -94,11 +94,11 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra super.onMessage(message) if (message.name == "tablet.use") message.source.host match { case machine: api.machine.Machine => (machine.host, message.data) match { - case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => + case (tablet: internal.Tablet, Array(nbt: CompoundNBT, stack: ItemStack, player: PlayerEntity, blockPos: BlockPosition, side: Direction, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => val info = data.mapData(host.world) - nbt.setInteger("posX", blockPos.x - info.xCenter) - nbt.setInteger("posY", blockPos.y) - nbt.setInteger("posZ", blockPos.z - info.zCenter) + nbt.putInt("posX", blockPos.x - info.x) + nbt.putInt("posY", blockPos.y) + nbt.putInt("posZ", blockPos.z - info.z) case _ => // Ignore. } case _ => // Ignore. @@ -107,13 +107,13 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { - super.load(nbt) - data.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + data.loadData(nbt) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - data.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + data.saveData(nbt) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala index a1ac268d18..4a64029547 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala @@ -16,34 +16,36 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ -import net.minecraft.block.BlockPistonBase -import net.minecraft.init.SoundEvents -import net.minecraft.util.{EnumFacing, SoundCategory} +import net.minecraft.block.Blocks +import net.minecraft.block.PistonBlock +import net.minecraft.util.SoundEvents +import net.minecraft.util.{Direction, SoundCategory} +import net.minecraft.util.math.BlockPos import li.cil.oc.server.{PacketSender => ServerPacketSender} -import net.minecraft.block.material.EnumPushReaction +import net.minecraft.block.material.PushReaction import scala.collection.convert.WrapAsJava._ protected object PistonTraits { trait ExtendAware { val host: EnvironmentHost - def pushOrigin(side: EnumFacing): BlockPosition = BlockPosition(host) - def pushDirection(args: Arguments, index: Int): EnumFacing + def pushOrigin(side: Direction): BlockPosition = BlockPosition(host) + def pushDirection(args: Arguments, index: Int): Direction } trait DroneLike extends ExtendAware { - def pushDirection(args: Arguments, index: Int): EnumFacing = args.optSideAny(index, EnumFacing.SOUTH) + def pushDirection(args: Arguments, index: Int): Direction = args.optSideAny(index, Direction.SOUTH) } trait RotatableLike extends ExtendAware { val rotatable: internal.Rotatable with EnvironmentHost - def pushDirection(args: Arguments, index: Int): EnumFacing = rotatable.toGlobal(args.optSideForAction(index, EnumFacing.SOUTH)) + def pushDirection(args: Arguments, index: Int): Direction = rotatable.toGlobal(args.optSideForAction(index, Direction.SOUTH)) } trait TabletLike extends ExtendAware { val tablet: internal.Tablet - override def pushOrigin(side: EnumFacing): BlockPosition = - if (side == EnumFacing.DOWN && tablet.player.getEyeHeight > 1) super.pushOrigin(side).offset(EnumFacing.DOWN) + override def pushOrigin(side: Direction): BlockPosition = + if (side == Direction.DOWN && tablet.player.getEyeHeight > 1) super.pushOrigin(side).offset(Direction.DOWN) else super.pushOrigin(side) } } @@ -68,10 +70,10 @@ abstract class UpgradePiston(val host: EnvironmentHost) extends AbstractManagedE @Callback(doc = """function():boolean -- Returns true if the piston is sticky, i.e. it can also pull.""") def isSticky(context: Context, args: Arguments): Array[AnyRef] = result(isSticky) - protected def doPistonAction(context: Context, side: EnumFacing, extending: Boolean): Array[AnyRef] = { - val sound = if (extending) SoundEvents.BLOCK_PISTON_EXTEND.getRegistryName else SoundEvents.BLOCK_PISTON_CONTRACT.getRegistryName + protected def doPistonAction(context: Context, side: Direction, extending: Boolean): Array[AnyRef] = { + val sound = if (extending) SoundEvents.PISTON_EXTEND.getRegistryName else SoundEvents.PISTON_CONTRACT.getRegistryName val hostPos = pushOrigin(side).toBlockPos - val piston = new BlockPistonBase(isSticky) + val piston = (if (isSticky) Blocks.STICKY_PISTON else Blocks.PISTON).asInstanceOf[PistonBlock] if (!extending) { if (!isSticky) { @@ -79,18 +81,18 @@ abstract class UpgradePiston(val host: EnvironmentHost) extends AbstractManagedE throw new NoSuchMethodError("piston is not sticky. does not have pull") } // make sure that any obstruction block has breaking mobility - val innerBlockPos = hostPos.offset(side) + val innerBlockPos = hostPos.relative(side): BlockPos val innerBlockState = host.world.getBlockState(innerBlockPos) if (innerBlockState != null) { if (!innerBlockState.getBlock.isAir(innerBlockState, host.world, innerBlockPos)) { - if (innerBlockState.getMobilityFlag != EnumPushReaction.DESTROY) { + if (innerBlockState.getPistonPushReaction != PushReaction.DESTROY) { return result(false, "path is obstructed") } } } } - if (piston.doMove(host.world, hostPos, side, extending)) { + if (piston.moveBlocks(host.world, hostPos, side, extending)) { // send piston extend sound to clients host.synchronized(ServerPacketSender.sendSound( host.world, hostPos.getX, hostPos.getY, hostPos.getZ, diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala index 3b70e3057a..7a256f9364 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala @@ -16,17 +16,17 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntitySign -import net.minecraft.util.EnumFacing -import net.minecraft.util.text.TextComponentString -import net.minecraft.world.WorldServer +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tileentity.SignTileEntity +import net.minecraft.util.Direction +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.event.world.BlockEvent -import net.minecraftforge.fml.common.eventhandler.Event +import net.minecraftforge.eventbus.api.Event import scala.collection.convert.WrapAsJava._ @@ -42,19 +42,19 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { def host: EnvironmentHost - protected def getValue(tileEntity: Option[TileEntitySign]): Array[AnyRef] = { + protected def getValue(tileEntity: Option[SignTileEntity]): Array[AnyRef] = { tileEntity match { - case Some(sign) => result(sign.signText.map(_.getUnformattedText).mkString("\n")) + case Some(sign) => result(sign.messages.map(_.getString).mkString("\n")) case _ => result(Unit, "no sign") } } - protected def setValue(tileEntity: Option[TileEntitySign], text: String): Array[AnyRef] = { + protected def setValue(tileEntity: Option[SignTileEntity], text: String): Array[AnyRef] = { tileEntity match { case Some(sign) => val player = host match { case robot: internal.Robot => robot.player - case _ => FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) + case _ => FakePlayerFactory.get(host.world.asInstanceOf[ServerWorld], Settings.get.fakePlayerProfile) } val lines = text.lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line).toArray @@ -63,32 +63,32 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { return result(Unit, "not allowed") } - lines.map(line => new TextComponentString(line)).copyToArray(sign.signText) - host.world.notifyBlockUpdate(sign.getPos) + lines.map(line => new StringTextComponent(line)).copyToArray(sign.messages) + host.world.notifyBlockUpdate(sign.getBlockPos) MinecraftForge.EVENT_BUS.post(new SignChangeEvent.Post(sign, lines)) - result(sign.signText.mkString("\n")) + result(sign.messages.mkString("\n")) case _ => result(Unit, "no sign") } } - protected def findSign(side: EnumFacing) = { + protected def findSign(side: Direction) = { val hostPos = BlockPosition(host) - host.world.getTileEntity(hostPos) match { - case sign: TileEntitySign => Option(sign) - case _ => host.world.getTileEntity(hostPos.offset(side)) match { - case sign: TileEntitySign => Option(sign) + host.world.getBlockEntity(hostPos) match { + case sign: SignTileEntity => Option(sign) + case _ => host.world.getBlockEntity(hostPos.offset(side)) match { + case sign: SignTileEntity => Option(sign) case _ => None } } } - private def canChangeSign(player: EntityPlayer, tileEntity: TileEntitySign, lines: Array[String]): Boolean = { - if (!host.world.isBlockModifiable(player, tileEntity.getPos)) { + private def canChangeSign(player: PlayerEntity, tileEntity: SignTileEntity, lines: Array[String]): Boolean = { + if (!host.world.mayInteract(player, tileEntity.getBlockPos)) { return false } - val event = new BlockEvent.BreakEvent(host.world, tileEntity.getPos, tileEntity.getWorld.getBlockState(tileEntity.getPos), player) + val event = new BlockEvent.BreakEvent(host.world, tileEntity.getBlockPos, tileEntity.getLevel.getBlockState(tileEntity.getBlockPos), player) MinecraftForge.EVENT_BUS.post(event) if (event.isCanceled || event.getResult == Event.Result.DENY) { return false @@ -103,10 +103,10 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { super.onMessage(message) if (message.name == "tablet.use") message.source.host match { case machine: api.machine.Machine => (machine.host, message.data) match { - case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => - host.world.getTileEntity(blockPos) match { - case sign: TileEntitySign => - nbt.setString("signText", sign.signText.map(_.getUnformattedText).mkString("\n")) + case (tablet: internal.Tablet, Array(nbt: CompoundNBT, stack: ItemStack, player: PlayerEntity, blockPos: BlockPosition, side: Direction, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) => + host.world.getBlockEntity(blockPos) match { + case sign: SignTileEntity => + nbt.putString("signText", sign.messages.map(_.getString).mkString("\n")) case _ => } case _ => // Ignore. diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala index 65a2dd8ce0..a3e2c7f6f8 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala @@ -13,7 +13,9 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.world.World +import net.minecraft.world.biome.Biome.RainType import scala.collection.convert.WrapAsJava._ @@ -53,10 +55,10 @@ class UpgradeSolarGenerator(val host: EnvironmentHost) extends AbstractManagedEn } private def isSunVisible = { - val blockPos = BlockPosition(host).offset(EnumFacing.UP) - host.world.isDaytime && - (!host.world.provider.isNether) && - host.world.canBlockSeeSky(blockPos.toBlockPos) && - (!host.world.getBiome(blockPos.toBlockPos).canRain || (!host.world.isRaining && !host.world.isThundering)) + val blockPos = BlockPosition(host).offset(Direction.UP) + host.world.isDay && + (host.world.dimension != World.NETHER) && + host.world.canSeeSkyFromBelowWater(blockPos.toBlockPos) && + (host.world.getBiome(blockPos.toBlockPos).getPrecipitation == RainType.NONE || (!host.world.isRaining && !host.world.isThundering)) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala index fec4a7b176..91cae7f751 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala @@ -11,10 +11,11 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraftforge.fluids.FluidStack -import net.minecraftforge.fluids.FluidTank import net.minecraftforge.fluids.IFluidTank +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction +import net.minecraftforge.fluids.capability.templates.FluidTank import scala.collection.convert.WrapAsJava._ @@ -35,13 +36,13 @@ class UpgradeTank(val owner: EnvironmentHost, val capacity: Int) extends Abstrac val tank = new FluidTank(capacity) - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) tank.readFromNBT(nbt) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) tank.writeToNBT(nbt) } @@ -53,20 +54,28 @@ class UpgradeTank(val owner: EnvironmentHost, val capacity: Int) extends Abstrac override def getCapacity = tank.getCapacity - override def getInfo = tank.getInfo + override def isFluidValid(stack: FluidStack) = tank.isFluidValid(stack) - override def fill(stack: FluidStack, doFill: Boolean) = { - val amount = tank.fill(stack, doFill) - if (doFill && amount > 0) { + override def fill(stack: FluidStack, action: FluidAction) = { + val amount = tank.fill(stack, action) + if (action.execute && amount > 0) { node.sendToVisible("computer.signal", "tank_changed", Int.box(tankIndex), Int.box(amount)) } amount } - override def drain(maxDrain: Int, doDrain: Boolean) = { - val amount = tank.drain(maxDrain, doDrain) - if (doDrain && amount != null && amount.amount > 0) { - node.sendToVisible("computer.signal", "tank_changed", Int.box(tankIndex), Int.box(-amount.amount)) + override def drain(stack: FluidStack, action: FluidAction) = { + val amount = tank.drain(stack, action) + if (action.execute && amount != null && amount.getAmount > 0) { + node.sendToVisible("computer.signal", "tank_changed", Int.box(tankIndex), Int.box(-amount.getAmount)) + } + amount + } + + override def drain(maxDrain: Int, action: FluidAction) = { + val amount = tank.drain(maxDrain, action) + if (action.execute && amount != null && amount.getAmount > 0) { + node.sendToVisible("computer.signal", "tank_changed", Int.box(tankIndex), Int.box(-amount.getAmount)) } amount } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala index 18cb4bed50..4bd6472d58 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala @@ -18,8 +18,8 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.util.math.BlockPos import scala.collection.convert.WrapAsJava._ @@ -45,22 +45,22 @@ object UpgradeTractorBeam { protected def position: BlockPosition - protected def collectItem(item: EntityItem): Unit + protected def collectItem(item: ItemEntity): Unit private def world = position.world.get @Callback(doc = """function():boolean -- Tries to pick up a random item in the robots' vicinity.""") def suck(context: Context, args: Arguments): Array[AnyRef] = { - val items = world.getEntitiesWithinAABB(classOf[EntityItem], position.bounds.grow(pickupRadius, pickupRadius, pickupRadius)) - .filter(item => item.isEntityAlive && !item.cannotPickup) + val items = world.getEntitiesOfClass(classOf[ItemEntity], position.bounds.inflate(pickupRadius, pickupRadius, pickupRadius)) + .filter(item => item.isAlive && !item.hasPickUpDelay) if (items.nonEmpty) { - val item = items(world.rand.nextInt(items.size)) + val item = items(world.random.nextInt(items.size)) val stack = item.getItem val size = stack.getCount collectItem(item) - if (stack.getCount < size || item.isDead) { + if (stack.getCount < size || !item.isAlive) { context.pause(Settings.get.suckDelay) - world.playEvent(2003, new BlockPos(math.floor(item.posX).toInt, math.floor(item.posY).toInt, math.floor(item.posZ).toInt), 0) + world.levelEvent(2003, new BlockPos(math.floor(item.getX).toInt, math.floor(item.getY).toInt, math.floor(item.getZ).toInt), 0) return result(true) } } @@ -68,20 +68,20 @@ object UpgradeTractorBeam { } } - class Player(val owner: EnvironmentHost, val player: () => EntityPlayer) extends Common { + class Player(val owner: EnvironmentHost, val player: () => PlayerEntity) extends Common { override protected def position = BlockPosition(owner) - override protected def collectItem(item: EntityItem) = item.onCollideWithPlayer(player()) + override protected def collectItem(item: ItemEntity) = item.playerTouch(player()) } class Drone(val owner: internal.Agent) extends Common { override protected def position = BlockPosition(owner) - override protected def collectItem(item: EntityItem) = { + override protected def collectItem(item: ItemEntity) = { InventoryUtils.insertIntoInventory(item.getItem, owner.mainInventory, None, 64, simulate = false, Some(insertionSlots)) } - private def insertionSlots = (owner.selectedSlot until owner.mainInventory.getSizeInventory) ++ (0 until owner.selectedSlot) + private def insertionSlots = (owner.selectedSlot until owner.mainInventory.getContainerSize) ++ (0 until owner.selectedSlot) } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala index f85de66f35..e2f1010370 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala @@ -18,8 +18,8 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.util.BlockPosition import net.minecraft.entity.Entity -import net.minecraft.entity.IMerchant -import net.minecraft.util.math.Vec3d +import net.minecraft.entity.merchant.IMerchant +import net.minecraft.util.math.vector.Vector3d import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -43,22 +43,22 @@ class UpgradeTrading(val host: EnvironmentHost) extends AbstractManagedEnvironme def maxRange = Settings.get.tradingRange - def isInRange(entity: Entity) = new Vec3d(entity.posX, entity.posY, entity.posZ).distanceTo(position.toVec3) <= maxRange + def isInRange(entity: Entity) = new Vector3d(entity.getX, entity.getY, entity.getZ).distanceTo(position.toVec3) <= maxRange @Callback(doc = "function():table -- Returns a table of trades in range as userdata objects.") def getTrades(context: Context, args: Arguments): Array[AnyRef] = { - val merchants = entitiesInBounds[Entity](classOf[Entity], position.bounds.grow(maxRange, maxRange, maxRange)). + val merchants = entitiesInBounds[Entity](classOf[Entity], position.bounds.inflate(maxRange, maxRange, maxRange)). filter(isInRange). - collect { case merchant: IMerchant => merchant } + collect { case merchant: Entity with IMerchant => merchant } var nextId = 1 val idMap = mutable.Map[UUID, Int]() - for (id: UUID <- merchants.collect { case merchant: IMerchant => merchant.getPersistentID }.sorted) { + for (id: UUID <- merchants.collect { case merchant: IMerchant => merchant.getUUID }.sorted) { idMap.put(id, nextId) nextId += 1 } // sorting the result is not necessary, but will help the merchant trades line up nicely by merchant - result(merchants.sortBy(m => m.getPersistentID).flatMap(merchant => merchant.getRecipes(null).indices.map(index => { - new Trade(this, merchant, index, idMap(merchant.getPersistentID)) + result(merchants.sortBy(m => m.getUUID).flatMap(merchant => merchant.getOffers.indices.map(index => { + new Trade(this, merchant, index, idMap(merchant.getUUID)) }))) } } diff --git a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala index 837ef6b684..f606906290 100644 --- a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala @@ -17,7 +17,7 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.language.implicitConversions @@ -100,7 +100,7 @@ abstract class WirelessNetworkCard(host: EnvironmentHost) extends NetworkCard(ho override def update() { super.update() - if (world.getTotalWorldTime % 20 == 0) { + if (world.getGameTime % 20 == 0) { api.Network.updateWirelessNetwork(this) } } @@ -114,7 +114,7 @@ abstract class WirelessNetworkCard(host: EnvironmentHost) extends NetworkCard(ho override def onDisconnect(node: Node) { super.onDisconnect(node) - if (node == this.node || !world.isBlockLoaded(position)) { + if (node == this.node || !world.isLoaded(position)) { api.Network.leaveWirelessNetwork(this) } } @@ -123,16 +123,16 @@ abstract class WirelessNetworkCard(host: EnvironmentHost) extends NetworkCard(ho private final val StrengthTag = "strength" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - if (nbt.hasKey(StrengthTag)) { + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + if (nbt.contains(StrengthTag)) { strength = nbt.getDouble(StrengthTag) max 0 min maxWirelessRange } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setDouble(StrengthTag, strength) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putDouble(StrengthTag, strength) } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala index c8aae3d386..f22f063f66 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala @@ -10,13 +10,14 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.StackOption._ import net.minecraft.item.ItemStack -import net.minecraftforge.oredict.OreDictionary + +import scala.collection.convert.WrapAsScala._ trait InventoryAnalytics extends InventoryAware with NetworkAware { @Callback(doc = """function([slot:number]):table -- Get a description of the stack in the specified slot or the selected slot.""") def getStackInInternalSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val slot = optSlot(args, 0) - result(inventory.getStackInSlot(slot)) + result(inventory.getItem(slot)) } else result(Unit, "not enabled in config") @@ -24,7 +25,7 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware { def isEquivalentTo(context: Context, args: Arguments): Array[AnyRef] = { val slot = args.checkSlot(inventory, 0) result((stackInSlot(selectedSlot), stackInSlot(slot)) match { - case (SomeStack(stackA), SomeStack(stackB)) => OreDictionary.getOreIDs(stackA).intersect(OreDictionary.getOreIDs(stackB)).nonEmpty + case (SomeStack(stackA), SomeStack(stackB)) => stackA.getItem.getTags.intersect(stackB.getItem.getTags).nonEmpty case (EmptyStack, EmptyStack) => true case _ => false }) @@ -34,7 +35,7 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware { def storeInternal(context: Context, args: Arguments): Array[AnyRef] = { val localSlot = args.checkSlot(inventory, 0) val dbAddress = args.checkString(1) - val localStack = inventory.getStackInSlot(localSlot) + val localStack = inventory.getItem(localSlot) DatabaseAccess.withDatabase(node, dbAddress, database => { val dbSlot = args.checkSlot(database.data, 2) val nonEmpty = database.getStackInSlot(dbSlot) != ItemStack.EMPTY // zero size stacks! @@ -47,7 +48,7 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware { def compareToDatabase(context: Context, args: Arguments): Array[AnyRef] = { val localSlot = args.checkSlot(inventory, 0) val dbAddress = args.checkString(1) - val localStack = inventory.getStackInSlot(localSlot) + val localStack = inventory.getItem(localSlot) DatabaseAccess.withDatabase(node, dbAddress, database => { val dbSlot = args.checkSlot(database.data, 2) val dbStack = database.getStackInSlot(dbSlot) diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala index 37dc8a43d8..22235ac0ed 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala @@ -3,13 +3,13 @@ package li.cil.oc.server.component.traits import li.cil.oc.api.machine.Arguments import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.StackOption -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import scala.collection.immutable trait InventoryAware { - def fakePlayer: EntityPlayer + def fakePlayer: PlayerEntity def inventory: IInventory @@ -17,7 +17,7 @@ trait InventoryAware { def selectedSlot_=(value: Int): Unit - def insertionSlots: immutable.IndexedSeq[Int] = (selectedSlot until inventory.getSizeInventory) ++ (0 until selectedSlot) + def insertionSlots: immutable.IndexedSeq[Int] = (selectedSlot until inventory.getContainerSize) ++ (0 until selectedSlot) // ----------------------------------------------------------------------- // @@ -25,5 +25,5 @@ trait InventoryAware { if (args.count > 0 && args.checkAny(0) != null) args.checkSlot(inventory, 0) else selectedSlot - protected def stackInSlot(slot: Int): StackOption = StackOption(inventory.getStackInSlot(slot)) + protected def stackInSlot(slot: Int): StackOption = StackOption(inventory.getItem(slot)) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala index 3a083fbe00..57011f1819 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala @@ -11,7 +11,7 @@ import net.minecraft.item.ItemStack trait InventoryControl extends InventoryAware { @Callback(doc = "function():number -- The size of this device's internal inventory.") - def inventorySize(context: Context, args: Arguments): Array[AnyRef] = result(inventory.getSizeInventory) + def inventorySize(context: Context, args: Arguments): Array[AnyRef] = result(inventory.getContainerSize) @Callback(doc = "function([slot:number]):number -- Get the currently selected slot; set the selected slot if specified.") def select(context: Context, args: Arguments): Array[AnyRef] = { @@ -35,8 +35,8 @@ trait InventoryControl extends InventoryAware { def space(context: Context, args: Arguments): Array[AnyRef] = { val slot = optSlot(args, 0) result(stackInSlot(slot) match { - case SomeStack(stack) => math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize) - stack.getCount - case _ => inventory.getInventoryStackLimit + case SomeStack(stack) => math.min(inventory.getMaxStackSize, stack.getMaxStackSize) - stack.getCount + case _ => inventory.getMaxStackSize }) } @@ -60,28 +60,28 @@ trait InventoryControl extends InventoryAware { else result((stackInSlot(selectedSlot), stackInSlot(slot)) match { case (SomeStack(from), SomeStack(to)) => if (InventoryUtils.haveSameItemType(from, to, checkNBT = true)) { - val space = math.min(inventory.getInventoryStackLimit, to.getMaxStackSize) - to.getCount + val space = math.min(inventory.getMaxStackSize, to.getMaxStackSize) - to.getCount val amount = math.min(count, math.min(space, from.getCount)) if (amount > 0) { from.shrink(amount) to.grow(amount) assert(from.getCount >= 0) if (from.getCount == 0) { - inventory.setInventorySlotContents(selectedSlot, ItemStack.EMPTY) + inventory.setItem(selectedSlot, ItemStack.EMPTY) } - inventory.markDirty() + inventory.setChanged() true } else false } else if (count >= from.getCount) { - inventory.setInventorySlotContents(slot, from) - inventory.setInventorySlotContents(selectedSlot, to) + inventory.setItem(slot, from) + inventory.setItem(selectedSlot, to) true } else false case (SomeStack(from), EmptyStack) => - inventory.setInventorySlotContents(slot, inventory.decrStackSize(selectedSlot, count)) + inventory.setItem(slot, inventory.removeItem(selectedSlot, count)) true case _ => false }) diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala index 28efa67168..bcfed8a7f8 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala @@ -8,28 +8,28 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ResultWrapper.result import li.cil.oc.util.StackOption._ -import net.minecraft.entity.item.EntityItem -import net.minecraft.item.ItemBlock +import net.minecraft.entity.item.ItemEntity +import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.entity.item.ItemTossEvent -import net.minecraftforge.fml.common.eventhandler.Event.Result +import net.minecraftforge.eventbus.api.Event.Result import scala.collection.convert.WrapAsScala._ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRestricted { - @Callback(doc = "function(side:number[, fuzzy:boolean=false]):boolean -- Compare the block on the specified side with the one in the selected slot. Returns true if equal.") + @Callback(doc = "function(side:number):boolean -- Compare the block on the specified side with the one in the selected slot. Returns true if equal.") def compare(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSideForAction(args, 0) stackInSlot(selectedSlot) match { case SomeStack(stack) => Option(stack.getItem) match { - case Some(item: ItemBlock) => + case Some(item: BlockItem) => val blockPos = position.offset(side).toBlockPos val state = world.getBlockState(blockPos) val idMatches = item.getBlock == state.getBlock - val subTypeMatches = args.optBoolean(1, false) || !item.getHasSubtypes || item.getMetadata(stack.getItemDamage) == state.getBlock.getMetaFromState(state) - return result(idMatches && subTypeMatches) + args.optBoolean(1, false) // TODO + return result(idMatches) case _ => } case _ => @@ -41,7 +41,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest def drop(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) val count = args.optItemCount(1) - val stack = inventory.getStackInSlot(selectedSlot) + val stack = inventory.getItem(selectedSlot) if (!stack.isEmpty && stack.getCount > 0) { val blockPos = position.offset(facing) InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match { @@ -52,16 +52,16 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest } else if (stack.getCount == 0) { // Dropped whole stack. - inventory.setInventorySlotContents(selectedSlot, ItemStack.EMPTY) + inventory.setItem(selectedSlot, ItemStack.EMPTY) } else { // Dropped partial stack. - inventory.markDirty() + inventory.setChanged() } case _ => // No inventory to drop into, drop into the world. - val dropped = inventory.decrStackSize(selectedSlot, count) - val validator = (item: EntityItem) => { + val dropped = inventory.removeItem(selectedSlot, count) + val validator = (item: ItemEntity) => { val event = new ItemTossEvent(item, fakePlayer) val canceled = MinecraftForge.EVENT_BUS.post(event) val denied = event.hasResult && event.getResult == Result.DENY @@ -69,7 +69,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest } if (!dropped.isEmpty) { if (InventoryUtils.spawnStackInWorld(position, dropped, Some(facing), Some(validator)) == null) - fakePlayer.inventory.addItemStackToInventory(dropped) + fakePlayer.inventory.add(dropped) } } @@ -84,14 +84,14 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest * @param facing items to suck from * @return the number of items sucked */ - def suckFromItems(facing: EnumFacing): Int = { - for (entity <- suckableItems(facing) if !entity.isDead && !entity.cannotPickup) { + def suckFromItems(facing: Direction): Int = { + for (entity <- suckableItems(facing) if entity.isAlive && !entity.hasPickUpDelay) { val stack = entity.getItem val size = stack.getCount onSuckCollect(entity) if (stack.getCount < size) return size - stack.getCount - else if (entity.isDead) + else if (!entity.isAlive) return size } 0 @@ -119,7 +119,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest } } - protected def suckableItems(side: EnumFacing) = entitiesOnSide(classOf[EntityItem], side) + protected def suckableItems(side: Direction) = entitiesOnSide(classOf[ItemEntity], side) - protected def onSuckCollect(entity: EntityItem): Unit = entity.onCollideWithPlayer(fakePlayer) + protected def onSuckCollect(entity: ItemEntity): Unit = entity.playerTouch(fakePlayer) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala index 60914b32fb..69b782664c 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala @@ -9,7 +9,7 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.items.IItemHandler trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideRestricted { @@ -18,7 +18,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR val facing = checkSideForAction(args, 0) val count = args.optItemCount(2) val fromSide = args.optSideAny(3, facing.getOpposite) - val stack = inventory.getStackInSlot(selectedSlot) + val stack = inventory.getItem(selectedSlot) if (!stack.isEmpty && stack.getCount > 0) { withInventory(position.offset(facing), fromSide, inventory => { val slot = args.checkSlot(inventory, 1) @@ -28,11 +28,11 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR } else if (stack.getCount == 0) { // Dropped whole stack. - this.inventory.setInventorySlotContents(selectedSlot, ItemStack.EMPTY) + this.inventory.setItem(selectedSlot, ItemStack.EMPTY) } else { // Dropped partial stack. - this.inventory.markDirty() + this.inventory.setChanged() } context.pause(Settings.get.dropDelay) @@ -59,7 +59,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR }) } - private def withInventory(blockPos: BlockPosition, fromSide: EnumFacing, f: IItemHandler => Array[AnyRef]) = + private def withInventory(blockPos: BlockPosition, fromSide: Direction, f: IItemHandler => Array[AnyRef]) = InventoryUtils.inventoryAt(blockPos, fromSide) match { case Some(inventory) if mayInteract(blockPos, fromSide) => f(inventory) case _ => result(Unit, "no inventory") diff --git a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala index 42026cd360..172e51e05e 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala @@ -35,7 +35,7 @@ trait ItemInventoryControl extends InventoryAware { } private def withItemInventory(slot: Int, f: IItemHandler => Array[AnyRef]): Array[AnyRef] = { - inventory.getStackInSlot(slot) match { + inventory.getItem(slot) match { case stack: ItemStack => api.Driver.itemHandlerFor(stack, fakePlayer) match { case inventory: IItemHandler => f(inventory) case _ => result(0, "no item inventory") diff --git a/src/main/scala/li/cil/oc/server/component/traits/SideRestricted.scala b/src/main/scala/li/cil/oc/server/component/traits/SideRestricted.scala index 1201e35221..5cc79c7cd7 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/SideRestricted.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/SideRestricted.scala @@ -1,8 +1,8 @@ package li.cil.oc.server.component.traits import li.cil.oc.api.machine.Arguments -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction trait SideRestricted { - protected def checkSideForAction(args: Arguments, n: Int): EnumFacing + protected def checkSideForAction(args: Arguments, n: Int): Direction } diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala index 33815f2e94..34e2add739 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala @@ -5,6 +5,7 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper.result +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction trait TankControl extends TankAware { @Callback(doc = "function():number -- The number of tanks installed in the device.") @@ -24,7 +25,7 @@ trait TankControl extends TankAware { if (args.count > 0 && args.checkAny(0) != null) args.checkTank(tank, 0) else selectedTank result(fluidInTank(index) match { - case Some(fluid) => fluid.amount + case Some(fluid) => fluid.getAmount case _ => 0 }) } @@ -59,17 +60,17 @@ trait TankControl extends TankAware { } else (getTank(selectedTank), getTank(index)) match { case (Some(from), Some(to)) => - val drained = from.drain(count, false) - val transferred = to.fill(drained, true) + val drained = from.drain(count, FluidAction.SIMULATE) + val transferred = to.fill(drained, FluidAction.EXECUTE) if (transferred > 0) { - from.drain(transferred, true) + from.drain(transferred, FluidAction.EXECUTE) result(true) } else if (count >= from.getFluidAmount && to.getCapacity >= from.getFluidAmount && from.getCapacity >= to.getFluidAmount) { // Swap. - val tmp = to.drain(to.getFluidAmount, true) - to.fill(from.drain(from.getFluidAmount, true), true) - from.fill(tmp, true) + val tmp = to.drain(to.getFluidAmount, FluidAction.EXECUTE) + to.fill(from.drain(from.getFluidAmount, FluidAction.EXECUTE), FluidAction.EXECUTE) + from.fill(tmp, FluidAction.EXECUTE) result(true) } else result(Unit, "incompatible or no fluid") diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala index 96571deea0..8b205e2e12 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala @@ -10,11 +10,13 @@ import li.cil.oc.util.FluidUtils import li.cil.oc.util.InventoryUtils import net.minecraft.item.ItemStack import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction +import net.minecraftforge.fluids.capability.CapabilityFluidHandler trait TankInventoryControl extends WorldAware with InventoryAware with TankAware { @Callback(doc = """function([slot:number]):number -- Get the amount of fluid in the tank item in the specified slot or the selected slot.""") def getTankLevelInSlot(context: Context, args: Arguments): Array[AnyRef] = - withFluidInfo(optSlot(args, 0), (fluid, _) => result(fluid.fold(0)(_.amount))) + withFluidInfo(optSlot(args, 0), (fluid, _) => result(fluid.fold(0)(_.getAmount))) @Callback(doc = """function([slot:number]):number -- Get the capacity of the tank item in the specified slot of the robot or the selected slot.""") def getTankCapacityInSlot(context: Context, args: Arguments): Array[AnyRef] = @@ -36,15 +38,15 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware def drain(context: Context, args: Arguments): Array[AnyRef] = { val amount = args.optFluidCount(0) Option(tank.getFluidTank(selectedTank)) match { - case Some(into) => inventory.getStackInSlot(selectedSlot) match { + case Some(into) => inventory.getItem(selectedSlot) match { case stack: ItemStack => Option(FluidUtils.fluidHandlerOf(stack)) match { case Some(handler) => - val drained = handler.drain(amount, false) - val transferred = into.fill(drained, true) + val drained = handler.drain(amount, FluidAction.SIMULATE) + val transferred = into.fill(drained, FluidAction.EXECUTE) if (transferred > 0) { - handler.drain(transferred, true) - inventory.setInventorySlotContents(selectedSlot, handler.getContainer) + handler.drain(transferred, FluidAction.EXECUTE) + inventory.setItem(selectedSlot, handler.getContainer) result(true, transferred) } else result(Unit, "incompatible or no fluid") @@ -60,15 +62,15 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware def fill(context: Context, args: Arguments): Array[AnyRef] = { val amount = args.optFluidCount(0) Option(tank.getFluidTank(selectedTank)) match { - case Some(from) => inventory.getStackInSlot(selectedSlot) match { + case Some(from) => inventory.getItem(selectedSlot) match { case stack: ItemStack => Option(FluidUtils.fluidHandlerOf(stack)) match { case Some(handler) => - val drained = from.drain(amount, false) - val transferred = handler.fill(drained, true) + val drained = from.drain(amount, FluidAction.SIMULATE) + val transferred = handler.fill(drained, FluidAction.EXECUTE) if (transferred > 0) { - from.drain(transferred, true) - inventory.setInventorySlotContents(selectedSlot, handler.getContainer) + from.drain(transferred, FluidAction.EXECUTE) + inventory.setItem(selectedSlot, handler.getContainer) result(true, transferred) } else result(Unit, "incompatible or no fluid") @@ -82,13 +84,12 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware private def withFluidInfo(slot: Int, f: (Option[FluidStack], Int) => Array[AnyRef]) = { def fluidInfo(stack: ItemStack) = Option(FluidUtils.fluidHandlerOf(stack)) match { - case Some(handler) if handler.getTankProperties.length > 0 => - val props = handler.getTankProperties()(0) - Option((Option(props.getContents), props.getCapacity)) + case Some(handler) if handler.getTanks > 0 => + Option((Option(handler.getFluidInTank(0)), handler.getTankCapacity(0))) case _ => None } - inventory.getStackInSlot(slot) match { + inventory.getItem(slot) match { case stack: ItemStack => fluidInfo(stack) match { case Some((fluid, capacity)) => f(fluid, capacity) case _ => result(Unit, "item is not a fluid container") diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala index 521eab40ca..44ffdec6b9 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala @@ -7,7 +7,7 @@ import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.FluidUtils import li.cil.oc.util.ResultWrapper.result import net.minecraftforge.fluids.FluidStack -import net.minecraftforge.fluids.capability.IFluidTankProperties +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction trait TankWorldControl extends TankAware with WorldAware with SideRestricted { @Callback(doc = "function(side:number [, tank:number]):boolean -- Compare the fluid in the selected tank with the fluid in the specified tank on the specified side. Returns true if equal.") @@ -17,8 +17,8 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { case Some(stack) => FluidUtils.fluidHandlerAt(position.offset(side), side.getOpposite) match { case Some(handler) => args.optTankProperties(handler, 1, null) match { - case properties: IFluidTankProperties => result(stack.isFluidEqual(properties.getContents)) - case _ => result(Option(handler.getTankProperties).exists(_.exists(other => stack.isFluidEqual(other.getContents)))) + case properties: TankProperties => result(stack.isFluidEqual(properties.contents)) + case _ => result((0 until handler.getTanks).map(handler.getFluidInTank).exists(stack.isFluidEqual)) } case _ => result(false) } @@ -39,14 +39,14 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { case Some(handler) => tank.getFluid match { case stack: FluidStack => - val drained = handler.drain(new FluidStack(stack, amount), true) - if ((drained != null && drained.amount > 0) || amount == 0) { - val filled = tank.fill(drained, true) + val drained = handler.drain(new FluidStack(stack, amount), FluidAction.EXECUTE) + if ((drained != null && drained.getAmount > 0) || amount == 0) { + val filled = tank.fill(drained, FluidAction.EXECUTE) result(true, filled) } else result(Unit, "incompatible or no fluid") case _ => - val transferred = tank.fill(handler.drain(amount, true), true) + val transferred = tank.fill(handler.drain(amount, FluidAction.EXECUTE), FluidAction.EXECUTE) result(transferred > 0, transferred) } case _ => result(Unit, "incompatible or no fluid") @@ -69,9 +69,9 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { case Some(handler) => tank.getFluid match { case stack: FluidStack => - val filled = handler.fill(new FluidStack(stack, amount), true) + val filled = handler.fill(new FluidStack(stack, amount), FluidAction.EXECUTE) if (filled > 0 || amount == 0) { - tank.drain(filled, true) + tank.drain(filled, FluidAction.EXECUTE) result(true, filled) } else result(Unit, "incompatible or no fluid") diff --git a/src/main/scala/li/cil/oc/server/component/traits/WakeMessageAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WakeMessageAware.scala index fe1255c8a1..40a2a87402 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WakeMessageAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WakeMessageAware.scala @@ -6,7 +6,7 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network.{EnvironmentHost, Packet} import li.cil.oc.server.component._ -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT trait WakeMessageAware extends traits.NetworkAware { private final val WakeMessageTag = "wakeMessage" @@ -60,15 +60,15 @@ trait WakeMessageAware extends traits.NetworkAware { } } - def loadWakeMessage(nbt: NBTTagCompound): Unit = { - if (nbt.hasKey(WakeMessageTag)) { + def loadWakeMessage(nbt: CompoundNBT): Unit = { + if (nbt.contains(WakeMessageTag)) { wakeMessage = Option(nbt.getString(WakeMessageTag)) } wakeMessageFuzzy = nbt.getBoolean(WakeMessageFuzzyTag) } - def saveWakeMessage(nbt: NBTTagCompound): Unit = { - wakeMessage.foreach(nbt.setString(WakeMessageTag, _)) - nbt.setBoolean(WakeMessageFuzzyTag, wakeMessageFuzzy) + def saveWakeMessage(nbt: CompoundNBT): Unit = { + wakeMessage.foreach(nbt.putString(WakeMessageTag, _)) + nbt.putBoolean(WakeMessageFuzzyTag, wakeMessageFuzzy) } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala index eaf94a5f47..0a05d47268 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala @@ -6,38 +6,41 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.item.EntityMinecart -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.util.EnumFacing -import net.minecraft.util.EnumHand +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.item.minecart.MinecartEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.Direction +import net.minecraft.util.Hand import net.minecraft.util.math.AxisAlignedBB -import net.minecraft.world.WorldServer +import net.minecraft.util.math.BlockRayTraceResult +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.event.entity.player.PlayerInteractEvent import net.minecraftforge.event.world.BlockEvent -import net.minecraftforge.fluids.FluidRegistry -import net.minecraftforge.fml.common.eventhandler.Event.Result +import net.minecraftforge.eventbus.api.Event.Result +import net.minecraftforge.fluids.IFluidBlock import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.wrapper.InvWrapper +import scala.collection.convert.WrapAsScala._ + trait WorldAware { def position: BlockPosition def world = position.world.get - def fakePlayer: EntityPlayer = { - val player = FakePlayerFactory.get(world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile) - player.posX = position.x + 0.5 - player.posY = position.y + 0.5 - player.posZ = position.z + 0.5 + def fakePlayer: PlayerEntity = { + val player = FakePlayerFactory.get(world.asInstanceOf[ServerWorld], Settings.get.fakePlayerProfile) + player.setPos(position.x + 0.5, position.y + 0.5, position.z + 0.5) player } - def mayInteract(blockPos: BlockPosition, face: EnumFacing): Boolean = { + def mayInteract(blockPos: BlockPosition, face: Direction): Boolean = { try { - val event = new PlayerInteractEvent.RightClickBlock(fakePlayer, EnumHand.MAIN_HAND, blockPos.toBlockPos, face, null) + val trace = new BlockRayTraceResult(fakePlayer.position, face, blockPos.toBlockPos, false) + val event = new PlayerInteractEvent.RightClickBlock(fakePlayer, Hand.MAIN_HAND, blockPos.toBlockPos, trace) MinecraftForge.EVENT_BUS.post(event) !event.isCanceled && event.getUseBlock != Result.DENY } catch { @@ -47,50 +50,51 @@ trait WorldAware { } } - def mayInteract(blockPos: BlockPosition, side: EnumFacing, inventory: IItemHandler): Boolean = mayInteract(blockPos, side) && (inventory match { - case inv: InvWrapper if inv.getInv != null => inv.getInv.isUsableByPlayer(fakePlayer) + def mayInteract(blockPos: BlockPosition, side: Direction, inventory: IItemHandler): Boolean = mayInteract(blockPos, side) && (inventory match { + case inv: InvWrapper if inv.getInv != null => inv.getInv.stillValid(fakePlayer) case _ => true }) def entitiesInBounds[Type <: Entity](clazz: Class[Type], bounds: AxisAlignedBB) = { - world.getEntitiesWithinAABB(clazz, bounds) + world.getEntitiesOfClass(clazz, bounds) } def entitiesInBlock[Type <: Entity](clazz: Class[Type], blockPos: BlockPosition) = { entitiesInBounds(clazz, blockPos.bounds) } - def entitiesOnSide[Type <: Entity](clazz: Class[Type], side: EnumFacing) = { + def entitiesOnSide[Type <: Entity](clazz: Class[Type], side: Direction) = { entitiesInBlock(clazz, position.offset(side)) } - def closestEntity[Type <: Entity](clazz: Class[Type], side: EnumFacing) = { + def closestEntity[Type <: Entity](clazz: Class[Type], side: Direction) = { val blockPos = position.offset(side) - Option(world.findNearestEntityWithinAABB(clazz, blockPos.bounds, fakePlayer)) + val candidates = world.getEntitiesOfClass(clazz, blockPos.bounds, null) + if (!candidates.isEmpty) Some(candidates.minBy(e => fakePlayer.distanceToSqr(e))) else None } - def blockContent(side: EnumFacing) = { + def blockContent(side: Direction) = { closestEntity[Entity](classOf[Entity], side) match { - case Some(_@(_: EntityLivingBase | _: EntityMinecart)) => + case Some(_@(_: LivingEntity | _: MinecartEntity)) => (true, "entity") case _ => val blockPos = position.offset(side) - val block = world.getBlock(blockPos) - val metadata = world.getBlockMetadata(blockPos) - if (block.isAir(blockPos)) { + val state = world.getBlockState(blockPos.toBlockPos) + val block = state.getBlock + if (block.isAir(state, world, blockPos.toBlockPos)) { (false, "air") } - else if (FluidRegistry.lookupFluidForBlock(block) != null) { - val event = new BlockEvent.BreakEvent(world, blockPos.toBlockPos, metadata, fakePlayer) + else if (!block.isInstanceOf[IFluidBlock]) { + val event = new BlockEvent.BreakEvent(world, blockPos.toBlockPos, state, fakePlayer) MinecraftForge.EVENT_BUS.post(event) (event.isCanceled, "liquid") } else if (block.isReplaceable(blockPos)) { - val event = new BlockEvent.BreakEvent(world, blockPos.toBlockPos, metadata, fakePlayer) + val event = new BlockEvent.BreakEvent(world, blockPos.toBlockPos, state, fakePlayer) MinecraftForge.EVENT_BUS.post(event) (event.isCanceled, "replaceable") } - else if (block.getCollisionBoundingBoxFromPool(blockPos) == null) { + else if (state.getCollisionShape(world, blockPos.toBlockPos, ISelectionContext.empty).isEmpty) { (true, "passable") } else { diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala index ee46da2f1c..40720b6633 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala @@ -12,9 +12,10 @@ import li.cil.oc.util.ExtendedArguments._ import net.minecraft.block.Block import li.cil.oc.util.StackOption import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.items.IItemHandler -import net.minecraftforge.oredict.OreDictionary + +import scala.collection.convert.WrapAsScala._ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware { @Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the device.""") @@ -68,12 +69,12 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ val stackB = inventory.getStackInSlot(args.checkSlot(inventory, 2)) result(stackA == stackB || (!stackA.isEmpty && !stackB.isEmpty && - OreDictionary.getOreIDs(stackA).intersect(OreDictionary.getOreIDs(stackB)).nonEmpty)) + stackA.getItem.getTags.intersect(stackB.getItem.getTags).nonEmpty)) }) } @Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the inventory on the specified side of the device.""") - def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { + def getItem(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) } @@ -122,7 +123,7 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) } - private def withInventory(side: EnumFacing, f: IItemHandler => Array[AnyRef]) = + private def withInventory(side: Direction, f: IItemHandler => Array[AnyRef]) = InventoryUtils.inventoryAt(position.offset(side), side.getOpposite) match { case Some(inventory) if mayInteract(position.offset(side), side.getOpposite, inventory) => f(inventory) case _ => result(Unit, "no inventory") diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala index e2edd63b8e..c8f720f1e5 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.machine.Context import li.cil.oc.server.component.result import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.FluidUtils -import net.minecraftforge.fluids.capability.IFluidTankProperties trait WorldTankAnalytics extends WorldAware with SideRestricted { @Callback(doc = """function(side:number [, tank:number]):number -- Get the amount of fluid in the tank on the specified side.""") @@ -16,8 +15,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { FluidUtils.fluidHandlerAt(position.offset(facing), facing.getOpposite) match { case Some(handler) => args.optTankProperties(handler, 1, null) match { - case properties: IFluidTankProperties => result(Option(properties.getContents).fold(0)(_.amount)) - case _ => result(handler.getTankProperties.map(info => Option(info.getContents).fold(0)(_.amount)).sum) + case properties: TankProperties => result(Option(properties.contents).fold(0)(_.getAmount)) + case _ => result((0 until handler.getTanks).map(i => Option(handler.getFluidInTank(i)).fold(0)(_.getAmount)).sum) } case _ => result(Unit, "no tank") } @@ -28,8 +27,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { val facing = checkSideForAction(args, 0) FluidUtils.fluidHandlerAt(position.offset(facing), facing.getOpposite) match { case Some(handler) => args.optTankProperties(handler, 1, null) match { - case properties: IFluidTankProperties => result(properties.getCapacity) - case _ => result(handler.getTankProperties.map(_.getCapacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) + case properties: TankProperties => result(properties.capacity) + case _ => result((0 until handler.getTanks).map(handler.getTankCapacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) } case _ => result(Unit, "no tank") } @@ -40,8 +39,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { val facing = checkSideForAction(args, 0) FluidUtils.fluidHandlerAt(position.offset(facing), facing.getOpposite) match { case Some(handler) => args.optTankProperties(handler, 1, null) match { - case properties: IFluidTankProperties => result(properties) - case _ => result(handler.getTankProperties) + case properties: TankProperties => result(properties) + case _ => result((0 until handler.getTanks).map(i => new TankProperties(handler.getTankCapacity(i), handler.getFluidInTank(i))).toArray) } case _ => result(Unit, "no tank") } diff --git a/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala b/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala index b7be2887c7..fe7c0fe68f 100644 --- a/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala +++ b/src/main/scala/li/cil/oc/server/driver/CompoundBlockDriver.scala @@ -9,12 +9,12 @@ import net.minecraft.inventory.IInventory import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World class CompoundBlockDriver(val sidedBlocks: Array[DriverBlock]) extends DriverBlock { - override def createEnvironment(world: World, pos: BlockPos, side: EnumFacing): CompoundBlockEnvironment = { + override def createEnvironment(world: World, pos: BlockPos, side: Direction): CompoundBlockEnvironment = { val list = sidedBlocks.map { driver => Option(driver.createEnvironment(world, pos, side)) match { case Some(environment) => (driver.getClass.getName, environment) @@ -25,7 +25,7 @@ class CompoundBlockDriver(val sidedBlocks: Array[DriverBlock]) extends DriverBlo else new CompoundBlockEnvironment(cleanName(tryGetName(world, pos, list.map(_._2))), list: _*) } - override def worksWith(world: World, pos: BlockPos, side: EnumFacing): Boolean = sidedBlocks.forall(_.worksWith(world, pos, side)) + override def worksWith(world: World, pos: BlockPos, side: Direction): Boolean = sidedBlocks.forall(_.worksWith(world, pos, side)) override def equals(obj: Any): Boolean = obj match { case multi: CompoundBlockDriver if multi.sidedBlocks.length == sidedBlocks.length => sidedBlocks.intersect(multi.sidedBlocks).length == sidedBlocks.length @@ -40,26 +40,21 @@ class CompoundBlockDriver(val sidedBlocks: Array[DriverBlock]) extends DriverBlo case Some(named) => return named.preferredName case _ => // No preferred name. } - try world.getTileEntity(pos) match { - case inventory: IInventory if !Strings.isNullOrEmpty(inventory.getName) => return inventory.getName.stripPrefix("container.") - } catch { - case _: Throwable => - } try { val block = world.getBlockState(pos).getBlock - val stack = if (Item.getItemFromBlock(block) != null) { - Some(new ItemStack(block, 1, block.damageDropped(world.getBlockState(pos)))) + val stack = if (block.asItem() != null) { + Some(new ItemStack(block, 1)) } else None if (stack.isDefined) { - return stack.get.getUnlocalizedName.stripPrefix("tile.") + return stack.get.getDescriptionId.stripPrefix("tile.") } } catch { case _: Throwable => } - try world.getTileEntity(pos) match { + try world.getBlockEntity(pos) match { case tileEntity: TileEntity => - return TileEntity.getKey(tileEntity.getClass).getResourcePath + return tileEntity.getType.getRegistryName.getPath } catch { case _: Throwable => } diff --git a/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala b/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala index 10ea6510c1..41ea4b17e7 100644 --- a/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala +++ b/src/main/scala/li/cil/oc/server/driver/CompoundBlockEnvironment.scala @@ -7,7 +7,7 @@ import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.api.network._ import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT class CompoundBlockEnvironment(val name: String, val environments: (String, ManagedEnvironment)*) extends ManagedEnvironment { // Block drivers with visibility < network usually won't make much sense, @@ -54,14 +54,14 @@ class CompoundBlockEnvironment(val name: String, val environments: (String, Mana private final val TypeHashTag = "typeHash" - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { // Ignore existing data if the underlying type is different. - if (nbt.hasKey(TypeHashTag) && nbt.getLong(TypeHashTag) != typeHash) return - node.load(nbt) + if (nbt.contains(TypeHashTag) && nbt.getLong(TypeHashTag) != typeHash) return + node.loadData(nbt) for ((driver, environment) <- environments) { - if (nbt.hasKey(driver)) { + if (nbt.contains(driver)) { try { - environment.load(nbt.getCompoundTag(driver)) + environment.loadData(nbt.getCompound(driver)) } catch { case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$driver') threw an error while loading.", e) } @@ -69,12 +69,12 @@ class CompoundBlockEnvironment(val name: String, val environments: (String, Mana } } - override def save(nbt: NBTTagCompound) { - nbt.setLong(TypeHashTag, typeHash) - node.save(nbt) + override def saveData(nbt: CompoundNBT) { + nbt.putLong(TypeHashTag, typeHash) + node.saveData(nbt) for ((driver, environment) <- environments) { try { - nbt.setNewCompoundTag(driver, environment.save) + nbt.setNewCompoundTag(driver, environment.saveData) } catch { case e: Throwable => OpenComputers.log.warn(s"A block component of type '${environment.getClass.getName}' (provided by driver '$driver') threw an error while saving.", e) } diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index 4a8e21c4bc..6ddfdb161d 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -13,10 +13,10 @@ import li.cil.oc.api.driver.item.HostAware import li.cil.oc.api.machine.Value import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.util.InventoryUtils -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraftforge.items.CapabilityItemHandler @@ -98,7 +98,7 @@ private[oc] object Registry extends api.detail.DriverAPI { } } - override def driverFor(world: World, pos: BlockPos, side: EnumFacing): DriverBlock = + override def driverFor(world: World, pos: BlockPos, side: Direction): DriverBlock = sidedBlocks.filter(_.worksWith(world, pos, side)) match { case sidedDrivers if sidedDrivers.nonEmpty => new CompoundBlockDriver(sidedDrivers.toArray) case _ => null @@ -129,20 +129,18 @@ private[oc] object Registry extends api.detail.DriverAPI { override def environmentsFor(stack: ItemStack): util.Set[Class[_]] = environmentProviders.map(_.getEnvironment(stack)).filter(_ != null).toSet[Class[_]] - override def itemHandlerFor(stack: ItemStack, player: EntityPlayer): IItemHandler = { + override def itemHandlerFor(stack: ItemStack, player: PlayerEntity): IItemHandler = { inventoryProviders.find(provider => provider.worksWith(stack, player)). map(provider => InventoryUtils.asItemHandler(provider.getInventory(stack, player))). getOrElse { - if(stack.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) - stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) - else null + stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).orElse(null) } } override def itemDrivers: util.List[DriverItem] = items.toSeq def blacklistHost(stack: ItemStack, host: Class[_]) { - blacklist.find(_._1.isItemEqual(stack)) match { + blacklist.find(_._1.sameItem(stack)) match { case Some((_, hosts)) => hosts += host case _ => blacklist.append((stack, mutable.Set(host))) } diff --git a/src/main/scala/li/cil/oc/server/fs/Buffered.scala b/src/main/scala/li/cil/oc/server/fs/Buffered.scala index 0a9443922a..7a513e4ee8 100644 --- a/src/main/scala/li/cil/oc/server/fs/Buffered.scala +++ b/src/main/scala/li/cil/oc/server/fs/Buffered.scala @@ -12,7 +12,7 @@ import li.cil.oc.OpenComputers import li.cil.oc.api.fs.Mode import li.cil.oc.util.ThreadPoolFactory import li.cil.oc.util.SafeThreadPool -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import org.apache.commons.io.FileUtils import scala.collection.mutable @@ -48,7 +48,7 @@ trait Buffered extends OutputStreamFileSystem { private var saving: Option[Future[_]] = None - override def load(nbt: NBTTagCompound): Unit = { + override def loadData(nbt: CompoundNBT): Unit = { saving.foreach(f => try { f.get(120L, TimeUnit.SECONDS) } catch { @@ -56,10 +56,10 @@ trait Buffered extends OutputStreamFileSystem { case e: CancellationException => // NO-OP }) loadFiles(nbt) - super.load(nbt) + super.loadData(nbt) } - private def loadFiles(nbt: NBTTagCompound): Unit = this.synchronized { + private def loadFiles(nbt: CompoundNBT): Unit = this.synchronized { def recurse(path: String, directory: io.File) { makeDirectory(path) for (child <- directory.listFiles() if FileSystem.isValidFilename(child.getName)) { @@ -101,8 +101,8 @@ trait Buffered extends OutputStreamFileSystem { else recurse("", fileRoot) } - override def save(nbt: NBTTagCompound): Unit = { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = { + super.saveData(nbt) saving = Buffered.fileSaveHandler.withPool(_.submit(new Runnable { override def run(): Unit = saveFiles() })) diff --git a/src/main/scala/li/cil/oc/server/fs/Capacity.scala b/src/main/scala/li/cil/oc/server/fs/Capacity.scala index 257e6bf029..54b606414a 100644 --- a/src/main/scala/li/cil/oc/server/fs/Capacity.scala +++ b/src/main/scala/li/cil/oc/server/fs/Capacity.scala @@ -4,7 +4,7 @@ import java.io import li.cil.oc.Settings import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT trait Capacity extends OutputStreamFileSystem { private var used = computeSize("/") @@ -52,10 +52,10 @@ trait Capacity extends OutputStreamFileSystem { // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { try { ignoreCapacity = true - super.load(nbt) + super.loadData(nbt) } finally { ignoreCapacity = false } @@ -63,11 +63,11 @@ trait Capacity extends OutputStreamFileSystem { used = computeSize("/") } - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) // For the tooltip. - nbt.setLong("capacity.used", used) + nbt.putLong("capacity.used", used) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala index d8e4553536..7e5f25d40f 100644 --- a/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala @@ -7,7 +7,7 @@ import li.cil.oc.api import li.cil.oc.api.fs.Handle import li.cil.oc.api.fs.Mode import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable @@ -82,15 +82,15 @@ class CompositeReadOnlyFileSystem(factories: mutable.LinkedHashMap[String, Calla // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { for ((name, fs) <- parts) { - fs.load(nbt.getCompoundTag(name)) + fs.loadData(nbt.getCompound(name)) } } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { for ((name, fs) <- parts) { - nbt.setNewCompoundTag(name, fs.save) + nbt.setNewCompoundTag(name, fs.saveData) } } diff --git a/src/main/scala/li/cil/oc/server/fs/FileOutputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileOutputStreamFileSystem.scala index a909e1c6ca..83b1ea7a5e 100644 --- a/src/main/scala/li/cil/oc/server/fs/FileOutputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileOutputStreamFileSystem.scala @@ -4,7 +4,7 @@ import java.io import java.io.RandomAccessFile import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT trait FileOutputStreamFileSystem extends FileInputStreamFileSystem with OutputStreamFileSystem { override def spaceTotal = -1 @@ -34,8 +34,8 @@ trait FileOutputStreamFileSystem extends FileInputStreamFileSystem with OutputSt // ----------------------------------------------------------------------- // - override def save(nbt: NBTTagCompound) { - super.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) root.mkdirs() root.setLastModified(System.currentTimeMillis()) } diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index ef3451bf9e..977e799837 100755 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -15,16 +15,18 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.common.DimensionManager +import net.minecraft.nbt.CompoundNBT +import net.minecraft.world.storage.FolderName +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.util.Try object FileSystem extends api.detail.FileSystemAPI { lazy val isCaseInsensitive: Boolean = Settings.get.forceCaseInsensitive || (try { val uuid = UUID.randomUUID().toString - val lowerCase = new io.File(DimensionManager.getCurrentSaveRootDirectory, uuid + "oc_rox") - val upperCase = new io.File(DimensionManager.getCurrentSaveRootDirectory, uuid + "OC_ROX") + val saveDir = ServerLifecycleHooks.getCurrentServer.getWorldPath(FolderName.ROOT).toFile + val lowerCase = new io.File(saveDir, uuid + "oc_rox") + val upperCase = new io.File(saveDir, uuid + "OC_ROX") // This should NEVER happen but could also lead to VERY weird bugs, so we // make sure the files don't exist. lowerCase.exists() && lowerCase.delete() @@ -100,7 +102,7 @@ object FileSystem extends api.detail.FileSystemAPI { } override def fromSaveDirectory(root: String, capacity: Long, buffered: Boolean): Capacity = { - val path = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + root) + val path = ServerLifecycleHooks.getCurrentServer.getWorldPath(new FolderName(Settings.savePath + root)).toFile if (!path.isDirectory) { path.delete() } @@ -116,10 +118,10 @@ object FileSystem extends api.detail.FileSystemAPI { Delegator.subItem(fsStack) match { case Some(drive: FileSystemLike) => { val data = li.cil.oc.integration.opencomputers.Item.dataTag(fsStack) - if (data.hasKey("node")) { - val nodeData = data.getCompoundTag("node") - if (nodeData.hasKey("address")) { - nodeData.removeTag("address") + if (data.contains("node")) { + val nodeData = data.getCompound("node") + if (nodeData.contains("address")) { + nodeData.remove("address") return true } } @@ -167,11 +169,11 @@ object FileSystem extends api.detail.FileSystemAPI { private final val LabelTag = Settings.namespace + "fs.label" - override def load(nbt: NBTTagCompound) {} + override def loadData(nbt: CompoundNBT) {} - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { if (label != null) { - nbt.setString(LabelTag, label) + nbt.putString(LabelTag, label) } } } diff --git a/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala index 67a554ef9f..11a986482c 100644 --- a/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala @@ -7,8 +7,8 @@ import java.nio.channels.ReadableByteChannel import li.cil.oc.api import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT import net.minecraftforge.common.util.Constants.NBT import scala.collection.mutable @@ -58,10 +58,10 @@ trait InputStreamFileSystem extends api.fs.FileSystem { private final val PathTag = "path" private final val PositionTag = "position" - override def load(nbt: NBTTagCompound) { - val handlesNbt = nbt.getTagList(InputTag, NBT.TAG_COMPOUND) - (0 until handlesNbt.tagCount).map(handlesNbt.getCompoundTagAt).foreach(handleNbt => { - val handle = handleNbt.getInteger(HandleTag) + override def loadData(nbt: CompoundNBT) { + val handlesNbt = nbt.getList(InputTag, NBT.TAG_COMPOUND) + (0 until handlesNbt.size).map(handlesNbt.getCompound).foreach(handleNbt => { + val handle = handleNbt.getInt(HandleTag) val path = handleNbt.getString(PathTag) val position = handleNbt.getLong(PositionTag) openInputChannel(path) match { @@ -74,17 +74,17 @@ trait InputStreamFileSystem extends api.fs.FileSystem { }) } - override def save(nbt: NBTTagCompound) = this.synchronized { - val handlesNbt = new NBTTagList() + override def saveData(nbt: CompoundNBT): Unit = this.synchronized { + val handlesNbt = new ListNBT() for (file <- handles.values) { assert(file.channel.isOpen) - val handleNbt = new NBTTagCompound() - handleNbt.setInteger(HandleTag, file.handle) - handleNbt.setString(PathTag, file.path) - handleNbt.setLong(PositionTag, file.position) - handlesNbt.appendTag(handleNbt) + val handleNbt = new CompoundNBT() + handleNbt.putInt(HandleTag, file.handle) + handleNbt.putString(PathTag, file.path) + handleNbt.putLong(PositionTag, file.position) + handlesNbt.add(handleNbt) } - nbt.setTag(InputTag, handlesNbt) + nbt.put(InputTag, handlesNbt) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/fs/OutputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/OutputStreamFileSystem.scala index 4877aaa4d4..d0b7bbc7b7 100644 --- a/src/main/scala/li/cil/oc/server/fs/OutputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/OutputStreamFileSystem.scala @@ -5,8 +5,8 @@ import java.io.IOException import li.cil.oc.api import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT import net.minecraftforge.common.util.Constants.NBT import scala.collection.mutable @@ -50,12 +50,12 @@ trait OutputStreamFileSystem extends InputStreamFileSystem { private final val HandleTag = "handle" private final val PathTag = "path" - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) - val handlesNbt = nbt.getTagList(OutputTag, NBT.TAG_COMPOUND) - (0 until handlesNbt.tagCount).map(handlesNbt.getCompoundTagAt).foreach(handleNbt => { - val handle = handleNbt.getInteger(HandleTag) + val handlesNbt = nbt.getList(OutputTag, NBT.TAG_COMPOUND) + (0 until handlesNbt.size).map(handlesNbt.getCompound).foreach(handleNbt => { + val handle = handleNbt.getInt(HandleTag) val path = handleNbt.getString(PathTag) openOutputHandle(handle, path, Mode.Append) match { case Some(fileHandle) => handles += handle -> fileHandle @@ -64,18 +64,18 @@ trait OutputStreamFileSystem extends InputStreamFileSystem { }) } - override def save(nbt: NBTTagCompound) = this.synchronized { - super.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = this.synchronized { + super.saveData(nbt) - val handlesNbt = new NBTTagList() + val handlesNbt = new ListNBT() for (file <- handles.values) { assert(!file.isClosed) - val handleNbt = new NBTTagCompound() - handleNbt.setInteger(HandleTag, file.handle) - handleNbt.setString(PathTag, file.path) - handlesNbt.appendTag(handleNbt) + val handleNbt = new CompoundNBT() + handleNbt.putInt(HandleTag, file.handle) + handleNbt.putString(PathTag, file.path) + handlesNbt.add(handleNbt) } - nbt.setTag(OutputTag, handlesNbt) + nbt.put(OutputTag, handlesNbt) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/fs/ReadOnlyWrapper.scala b/src/main/scala/li/cil/oc/server/fs/ReadOnlyWrapper.scala index 2c4a2dde51..9ead60b6ed 100644 --- a/src/main/scala/li/cil/oc/server/fs/ReadOnlyWrapper.scala +++ b/src/main/scala/li/cil/oc/server/fs/ReadOnlyWrapper.scala @@ -4,7 +4,7 @@ import java.io.FileNotFoundException import li.cil.oc.api import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT private class ReadOnlyWrapper(val fileSystem: api.fs.FileSystem) extends api.fs.FileSystem { override def isReadOnly = true @@ -41,7 +41,7 @@ private class ReadOnlyWrapper(val fileSystem: api.fs.FileSystem) extends api.fs. override def close() = fileSystem.close() - override def load(nbt: NBTTagCompound) = fileSystem.load(nbt) + override def loadData(nbt: CompoundNBT): Unit = fileSystem.loadData(nbt) - override def save(nbt: NBTTagCompound) = fileSystem.save(nbt) + override def saveData(nbt: CompoundNBT): Unit = fileSystem.saveData(nbt) } diff --git a/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala index 02a63fdcb3..24f8578ea5 100644 --- a/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala @@ -4,8 +4,8 @@ import java.io import java.io.FileNotFoundException import li.cil.oc.api.fs.Mode -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.ListNBT import net.minecraftforge.common.util.Constants.NBT import scala.collection.mutable @@ -123,14 +123,14 @@ trait VirtualFileSystem extends OutputStreamFileSystem { // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) = { - if (!this.isInstanceOf[Buffered]) root.load(nbt) - super.load(nbt) // Last to ensure streams can be re-opened. + override def loadData(nbt: CompoundNBT) { + if (!this.isInstanceOf[Buffered]) root.loadData(nbt) + super.loadData(nbt) // Last to ensure streams can be re-opened. } - override def save(nbt: NBTTagCompound) = { - super.save(nbt) // First to allow flushing. - if (!this.isInstanceOf[Buffered]) root.save(nbt) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) // First to allow flushing. + if (!this.isInstanceOf[Buffered]) root.saveData(nbt) } // ----------------------------------------------------------------------- // @@ -146,13 +146,13 @@ trait VirtualFileSystem extends OutputStreamFileSystem { var lastModified = System.currentTimeMillis() - def load(nbt: NBTTagCompound) { - if (nbt.hasKey("lastModified")) + def loadData(nbt: CompoundNBT) { + if (nbt.contains("lastModified")) lastModified = nbt.getLong("lastModified") } - def save(nbt: NBTTagCompound) { - nbt.setLong("lastModified", lastModified) + def saveData(nbt: CompoundNBT) { + nbt.putLong("lastModified", lastModified) } def get(path: Iterable[String]): Option[VirtualObject] = @@ -185,15 +185,15 @@ trait VirtualFileSystem extends OutputStreamFileSystem { handle } - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) data.clear() data ++= nbt.getByteArray("data") } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setByteArray("data", data.toArray) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putByteArray("data", data.toArray) } override def canDelete = handle.isEmpty @@ -245,29 +245,29 @@ trait VirtualFileSystem extends OutputStreamFileSystem { private final val IsDirectoryTag = "isDirectory" private final val NameTag = "name" - override def load(nbt: NBTTagCompound) { - super.load(nbt) - val childrenNbt = nbt.getTagList(ChildrenTag, NBT.TAG_COMPOUND) - (0 until childrenNbt.tagCount).map(childrenNbt.getCompoundTagAt).foreach(childNbt => { + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + val childrenNbt = nbt.getList(ChildrenTag, NBT.TAG_COMPOUND) + (0 until childrenNbt.size).map(childrenNbt.getCompound).foreach(childNbt => { val child = if (childNbt.getBoolean(IsDirectoryTag)) new VirtualDirectory else new VirtualFile - child.load(childNbt) + child.loadData(childNbt) children += childNbt.getString(NameTag) -> child }) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - val childrenNbt = new NBTTagList() + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + val childrenNbt = new ListNBT() for ((childName, child) <- children) { - val childNbt = new NBTTagCompound() - childNbt.setBoolean(IsDirectoryTag, child.isDirectory) - childNbt.setString(NameTag, childName) - child.save(childNbt) - childrenNbt.appendTag(childNbt) + val childNbt = new CompoundNBT() + childNbt.putBoolean(IsDirectoryTag, child.isDirectory) + childNbt.putString(NameTag, childName) + child.saveData(childNbt) + childrenNbt.add(childNbt) } - nbt.setTag(ChildrenTag, childrenNbt) + nbt.put(ChildrenTag, childrenNbt) } override def get(path: Iterable[String]) = diff --git a/src/main/scala/li/cil/oc/server/machine/ArchitectureAPI.scala b/src/main/scala/li/cil/oc/server/machine/ArchitectureAPI.scala index 656ab58152..141b9717d2 100644 --- a/src/main/scala/li/cil/oc/server/machine/ArchitectureAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/ArchitectureAPI.scala @@ -1,7 +1,7 @@ package li.cil.oc.server.machine import li.cil.oc.api -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT abstract class ArchitectureAPI(val machine: api.machine.Machine) { protected def node = machine.node @@ -10,7 +10,7 @@ abstract class ArchitectureAPI(val machine: api.machine.Machine) { def initialize(): Unit - def load(nbt: NBTTagCompound) {} + def loadData(nbt: CompoundNBT) {} - def save(nbt: NBTTagCompound) {} + def saveData(nbt: CompoundNBT) {} } diff --git a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala index f5873a7fc7..cdb1f4ced5 100644 --- a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala +++ b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala @@ -7,8 +7,9 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.util.ItemUtils import net.minecraft.item.Item import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import net.minecraft.util.ResourceLocation +import net.minecraftforge.registries.ForgeRegistries import scala.collection.convert.WrapAsJava._ import scala.collection.mutable @@ -219,11 +220,12 @@ class ArgumentsImpl(val args: Seq[AnyRef]) extends Arguments { case _ => value.getClass.getSimpleName } - private def makeStack(name: String, damage: Int, tag: Option[NBTTagCompound]) = { - Item.REGISTRY.getObject(new ResourceLocation(name)) match { + private def makeStack(name: String, damage: Int, tag: Option[CompoundNBT]) = { + ForgeRegistries.ITEMS.getValue(new ResourceLocation(name)) match { case item: Item => - val stack = new ItemStack(item, 1, damage) - tag.foreach(stack.setTagCompound) + val stack = new ItemStack(item, 1) + stack.setDamageValue(damage) + tag.foreach(stack.setTag) stack case _ => throw new IllegalArgumentException("invalid item stack") } diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 40b707cc72..cc91c7e81a 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -38,12 +38,12 @@ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ResultWrapper.result import li.cil.oc.util.ThreadPoolFactory import net.minecraft.client.Minecraft -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt._ import net.minecraft.server.integrated.IntegratedServer import net.minecraftforge.common.util.Constants.NBT -import net.minecraftforge.fml.common.FMLCommonHandler +import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.Array.canBuildFrom import scala.collection.convert.WrapAsJava._ @@ -192,10 +192,11 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac override def canInteract(player: String): Boolean = !Settings.get.canComputersBeOwned || _users.synchronized(_users.isEmpty || _users.contains(player)) || - FMLCommonHandler.instance.getMinecraftServerInstance.isSinglePlayer || { - val config = FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList - val entity = config.getPlayerByUsername(player) - entity != null && config.canSendCommands(entity.getGameProfile) + ServerLifecycleHooks.getCurrentServer == null || + ServerLifecycleHooks.getCurrentServer.isSingleplayer || { + val config = ServerLifecycleHooks.getCurrentServer.getPlayerList + val entity = config.getPlayerByName(player) + entity != null && config.isOp(entity.getGameProfile) } override def isRunning: Boolean = state.synchronized(state.top != Machine.State.Stopped && state.top != Machine.State.Stopping) @@ -319,7 +320,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac case arg: java.lang.Number => Double.box(arg.doubleValue) case arg: java.lang.String => arg case arg: Array[Byte] => arg - case arg: NBTTagCompound => arg + case arg: CompoundNBT => arg case arg => OpenComputers.log.warn("Trying to push signal with an unsupported argument of type " + arg.getClass.getName) null @@ -409,7 +410,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac throw new Exception("user exists") if (name.length > Settings.get.maxUsernameLength) throw new Exception("username too long") - if (!FMLCommonHandler.instance.getMinecraftServerInstance.getOnlinePlayerNames.contains(name)) + if (!ServerLifecycleHooks.getCurrentServer.getPlayerNames.contains(name)) throw new Exception("player must be online") _users.synchronized { @@ -509,7 +510,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac } // Update world time for time() and uptime(). - worldTime = host.world.getWorldTime + worldTime = host.world.getDayTime uptime += 1 if (remainIdle > 0) { @@ -520,7 +521,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac callBudget = maxCallBudget // Make sure we have enough power. - if (host.world.getTotalWorldTime % Settings.get.tickFrequency == 0) { + if (host.world.getGameTime % Settings.get.tickFrequency == 0) { state.synchronized(state.top match { case Machine.State.Paused | Machine.State.Restarting | @@ -538,7 +539,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac } // Avoid spamming user list across the network. - if (host.world.getTotalWorldTime % 20 == 0 && usersChanged) { + if (host.world.getGameTime % 20 == 0 && usersChanged) { val list = _users.synchronized { usersChanged = false users @@ -631,8 +632,8 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac message.data match { case Array(name: String, args@_*) if message.name == "computer.signal" => signal(name, Seq(message.source.address) ++ args: _*) - case Array(player: EntityPlayer, name: String, args@_*) if message.name == "computer.checked_signal" => - if (canInteract(player.getName)) + case Array(player: PlayerEntity, name: String, args@_*) if message.name == "computer.checked_signal" => + if (canInteract(player.getName.getString)) signal(name, Seq(message.source.address) ++ args: _*) case _ => if (message.name == "computer.start" && !isPaused) start() @@ -741,55 +742,55 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac private final val CPUTimeTag = "cpuTime" private final val RemainingPauseTag = "remainingPause" - override def load(nbt: NBTTagCompound): Unit = Machine.this.synchronized(state.synchronized { + override def loadData(nbt: CompoundNBT): Unit = Machine.this.synchronized(state.synchronized { assert(state.top == Machine.State.Stopped || state.top == Machine.State.Paused) close() state.clear() - super.load(nbt) + super.loadData(nbt) state.pushAll(nbt.getIntArray(StateTag).reverseMap(Machine.State(_))) - nbt.getTagList(UsersTag, NBT.TAG_STRING).foreach((tag: NBTTagString) => _users += tag.getString) - if (nbt.hasKey(MessageTag)) { + nbt.getList(UsersTag, NBT.TAG_STRING).foreach((tag: StringNBT) => _users += tag.getAsString) + if (nbt.contains(MessageTag)) { message = Some(nbt.getString(MessageTag)) } - _components ++= nbt.getTagList(ComponentsTag, NBT.TAG_COMPOUND).map((tag: NBTTagCompound) => + _components ++= nbt.getList(ComponentsTag, NBT.TAG_COMPOUND).map((tag: CompoundNBT) => tag.getString(AddressTag) -> tag.getString(NameTag)) tmp.foreach(fs => { - if (nbt.hasKey(TmpTag)) fs.load(nbt.getCompoundTag(TmpTag)) - else fs.load(SaveHandler.loadNBT(nbt, tmpPath)) + if (nbt.contains(TmpTag)) fs.loadData(nbt.getCompound(TmpTag)) + else fs.loadData(SaveHandler.loadNBT(nbt, tmpPath)) }) if (state.nonEmpty && isRunning && init()) try { - architecture.load(nbt) + architecture.loadData(nbt) - signals ++= nbt.getTagList(SignalsTag, NBT.TAG_COMPOUND).map((signalNbt: NBTTagCompound) => { - val argsNbt = signalNbt.getCompoundTag(ArgsTag) - val argsLength = argsNbt.getInteger(LengthTag) + signals ++= nbt.getList(SignalsTag, NBT.TAG_COMPOUND).map((signalNbt: CompoundNBT) => { + val argsNbt = signalNbt.getCompound(ArgsTag) + val argsLength = argsNbt.getInt(LengthTag) new Machine.Signal(signalNbt.getString(NameTag), - (0 until argsLength).map(ArgPrefixTag + _).map(argsNbt.getTag).map { - case tag: NBTTagByte if tag.getByte == -1 => null - case tag: NBTTagByte => Boolean.box(tag.getByte == 1) - case tag: NBTTagLong => Long.box(tag.getLong) - case tag: NBTTagDouble => Double.box(tag.getDouble) - case tag: NBTTagString => tag.getString - case tag: NBTTagByteArray => tag.getByteArray - case tag: NBTTagList => + (0 until argsLength).map(ArgPrefixTag + _).map(argsNbt.get).map { + case tag: ByteNBT if tag.getAsByte == -1 => null + case tag: ByteNBT => Boolean.box(tag.getAsByte == 1) + case tag: LongNBT => Long.box(tag.getAsLong) + case tag: DoubleNBT => Double.box(tag.getAsDouble) + case tag: StringNBT => tag.getAsString + case tag: ByteArrayNBT => tag.getAsByteArray + case tag: ListNBT => val data = mutable.Map.empty[String, String] - for (i <- 0 until tag.tagCount by 2) { - data += tag.getStringTagAt(i) -> tag.getStringTagAt(i + 1) + for (i <- 0 until tag.size by 2) { + data += tag.getString(i) -> tag.getString(i + 1) } data - case tag: NBTTagCompound => tag + case tag: CompoundNBT => tag case _ => null }.toArray[AnyRef]) }) uptime = nbt.getLong(UptimeTag) cpuTotal = nbt.getLong(CPUTimeTag) - remainingPause = nbt.getInteger(RemainingPauseTag) + remainingPause = nbt.getInt(RemainingPauseTag) // Delay execution for a second to allow the world around us to settle. if (state.top != Machine.State.Restarting) { @@ -810,7 +811,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac } }) - override def save(nbt: NBTTagCompound): Unit = Machine.this.synchronized(state.synchronized { + override def saveData(nbt: CompoundNBT): Unit = Machine.this.synchronized(state.synchronized { // The lock on 'this' should guarantee that this never happens regularly. // If something other than regular saving tries to save while we are executing code, // e.g. SpongeForge saving during robot.move due to block changes being captured, @@ -824,60 +825,60 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac // Make sure we don't continue running until everything has saved. pause(0.05) - super.save(nbt) + super.saveData(nbt) // Make sure the component list is up-to-date. processAddedComponents() - nbt.setIntArray(StateTag, state.map(_.id).toArray) + nbt.putIntArray(StateTag, state.map(_.id).toArray) nbt.setNewTagList(UsersTag, _users) - message.foreach(nbt.setString(MessageTag, _)) + message.foreach(nbt.putString(MessageTag, _)) - val componentsNbt = new NBTTagList() + val componentsNbt = new ListNBT() for ((address, name) <- _components) { - val componentNbt = new NBTTagCompound() - componentNbt.setString(AddressTag, address) - componentNbt.setString(NameTag, name) - componentsNbt.appendTag(componentNbt) + val componentNbt = new CompoundNBT() + componentNbt.putString(AddressTag, address) + componentNbt.putString(NameTag, name) + componentsNbt.add(componentNbt) } - nbt.setTag(ComponentsTag, componentsNbt) + nbt.put(ComponentsTag, componentsNbt) - tmp.foreach(fs => SaveHandler.scheduleSave(host, nbt, tmpPath, fs.save _)) + tmp.foreach(fs => SaveHandler.scheduleSave(host, nbt, tmpPath, fs.saveData _)) if (state.top != Machine.State.Stopped) try { - architecture.save(nbt) + architecture.saveData(nbt) - val signalsNbt = new NBTTagList() + val signalsNbt = new ListNBT() for (s <- signals.iterator) { - val signalNbt = new NBTTagCompound() - signalNbt.setString(NameTag, s.name) + val signalNbt = new CompoundNBT() + signalNbt.putString(NameTag, s.name) signalNbt.setNewCompoundTag(ArgsTag, args => { - args.setInteger(LengthTag, s.args.length) + args.putInt(LengthTag, s.args.length) s.args.zipWithIndex.foreach { - case (null, i) => args.setByte(ArgPrefixTag + i, -1) - case (arg: java.lang.Boolean, i) => args.setByte(ArgPrefixTag + i, if (arg) 1 else 0) - case (arg: java.lang.Long, i) => args.setLong(ArgPrefixTag + i, arg) - case (arg: java.lang.Double, i) => args.setDouble(ArgPrefixTag + i, arg) - case (arg: String, i) => args.setString(ArgPrefixTag + i, arg) - case (arg: Array[Byte], i) => args.setByteArray(ArgPrefixTag + i, arg) + case (null, i) => args.putByte(ArgPrefixTag + i, -1) + case (arg: java.lang.Boolean, i) => args.putByte(ArgPrefixTag + i, if (arg) 1 else 0) + case (arg: java.lang.Long, i) => args.putLong(ArgPrefixTag + i, arg) + case (arg: java.lang.Double, i) => args.putDouble(ArgPrefixTag + i, arg) + case (arg: String, i) => args.putString(ArgPrefixTag + i, arg) + case (arg: Array[Byte], i) => args.putByteArray(ArgPrefixTag + i, arg) case (arg: Map[_, _], i) => - val list = new NBTTagList() + val list = new ListNBT() for ((key, value) <- arg) { list.append(key.toString) list.append(value.toString) } - args.setTag(ArgPrefixTag + i, list) - case (arg: NBTTagCompound, i) => args.setTag(ArgPrefixTag + i, arg) - case (_, i) => args.setByte(ArgPrefixTag + i, -1) + args.put(ArgPrefixTag + i, list) + case (arg: CompoundNBT, i) => args.put(ArgPrefixTag + i, arg) + case (_, i) => args.putByte(ArgPrefixTag + i, -1) } }) - signalsNbt.appendTag(signalNbt) + signalsNbt.add(signalNbt) } - nbt.setTag(SignalsTag, signalsNbt) + nbt.put(SignalsTag, signalsNbt) - nbt.setLong(UptimeTag, uptime) - nbt.setLong(CPUTimeTag, cpuTotal) - nbt.setInteger(RemainingPauseTag, remainingPause) + nbt.putLong(UptimeTag, uptime) + nbt.putLong(CPUTimeTag, cpuTotal) + nbt.putInt(RemainingPauseTag, remainingPause) } catch { case t: Throwable => @@ -966,8 +967,8 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac result } - private def isGamePaused = FMLCommonHandler.instance.getMinecraftServerInstance != null && !FMLCommonHandler.instance.getMinecraftServerInstance.isDedicatedServer && (FMLCommonHandler.instance.getMinecraftServerInstance match { - case integrated: IntegratedServer => Minecraft.getMinecraft.isGamePaused + private def isGamePaused = ServerLifecycleHooks.getCurrentServer != null && !ServerLifecycleHooks.getCurrentServer.isDedicatedServer && (ServerLifecycleHooks.getCurrentServer match { + case integrated: IntegratedServer => Minecraft.getInstance.isPaused case _ => false }) diff --git a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala index f87d313d1d..fab5552439 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala @@ -16,7 +16,7 @@ import li.cil.oc.server.machine.Machine import li.cil.oc.util.ExtendedLuaState.extendLuaState import li.cil.repack.com.naef.jnlua._ import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsScala._ @@ -340,7 +340,7 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A @Deprecated private def state = machine.asInstanceOf[Machine].state - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { if (!machine.isRunning) return // Unlimit memory use while unpersisting. @@ -368,10 +368,10 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A } } - kernelMemory = (nbt.getInteger("kernelMemory") * ramScale).toInt + kernelMemory = (nbt.getInt("kernelMemory") * ramScale).toInt for (api <- apis) { - api.load(nbt) + api.loadData(nbt) } try lua.gc(LuaState.GcAction.COLLECT, 0) catch { @@ -387,7 +387,7 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A recomputeMemory(machine.host.internalComponents) } - override def save(nbt: NBTTagCompound) { + override def saveData(nbt: CompoundNBT) { // Unlimit memory while persisting. if (Settings.get.limitMemory) { lua.setTotalMemory(Integer.MAX_VALUE) @@ -406,10 +406,10 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A SaveHandler.scheduleSave(machine.host, nbt, machine.node.address + "_stack", persistence.persist(2)) } - nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt) + nbt.putInt("kernelMemory", math.ceil(kernelMemory / ramScale).toInt) for (api <- apis) { - api.save(nbt) + api.saveData(nbt) } try lua.gc(LuaState.GcAction.COLLECT, 0) catch { @@ -420,10 +420,10 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A } catch { case e: LuaRuntimeException => OpenComputers.log.warn(s"Could not persist computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}).\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat "))) - nbt.removeTag("state") + nbt.remove("state") case e: LuaGcMetamethodException => OpenComputers.log.warn(s"Could not persist computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}).\n${e.toString}") - nbt.removeTag("state") + nbt.remove("state") } // Limit memory again. diff --git a/src/main/scala/li/cil/oc/server/machine/luac/PersistenceAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/PersistenceAPI.scala index a47f5a95af..ccdc45d940 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/PersistenceAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/PersistenceAPI.scala @@ -5,7 +5,7 @@ import java.util.UUID import li.cil.oc.Settings import li.cil.oc.util.ExtendedLuaState._ import li.cil.repack.com.naef.jnlua.LuaState -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.mutable @@ -90,16 +90,16 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { } } - override def load(nbt: NBTTagCompound) { - super.load(nbt) - if (nbt.hasKey("persistKey")) { + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + if (nbt.contains("persistKey")) { persistKey = nbt.getString("persistKey") } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setString("persistKey", persistKey) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putString("persistKey", persistKey) } def configure() { diff --git a/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala index 3d24fe65e0..a52030c71d 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala @@ -12,7 +12,7 @@ import li.cil.oc.server.driver.Registry import li.cil.oc.server.machine.ArgumentsImpl import li.cil.oc.util.ExtendedLuaState.extendLuaState import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsScala._ @@ -21,10 +21,10 @@ class UserdataAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { lua.newTable() lua.pushScalaFunction(lua => { - val nbt = new NBTTagCompound() + val nbt = new CompoundNBT() val persistable = lua.toJavaObjectRaw(1).asInstanceOf[Persistable] lua.pushString(persistable.getClass.getName) - persistable.save(nbt) + persistable.saveData(nbt) val baos = new ByteArrayOutputStream() val dos = new DataOutputStream(baos) CompressedStreamTools.write(nbt, dos) @@ -42,7 +42,7 @@ class UserdataAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { val bais = new ByteArrayInputStream(data) val dis = new DataInputStream(bais) val nbt = CompressedStreamTools.read(dis) - persistable.load(nbt) + persistable.loadData(nbt) lua.pushJavaObjectRaw(persistable) 1 } diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala index 2c8b2388b7..2dac043a50 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala @@ -17,7 +17,7 @@ import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2._ import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsScala._ @@ -250,12 +250,12 @@ class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { if (machine.isRunning) { machine.stop() machine.start() } } - override def save(nbt: NBTTagCompound) {} + override def saveData(nbt: CompoundNBT) {} } diff --git a/src/main/scala/li/cil/oc/server/network/Component.scala b/src/main/scala/li/cil/oc/server/network/Component.scala index 086888ef59..1a347a5e46 100644 --- a/src/main/scala/li/cil/oc/server/network/Component.scala +++ b/src/main/scala/li/cil/oc/server/network/Component.scala @@ -13,7 +13,7 @@ import li.cil.oc.server.machine.Callbacks.ComponentCallback import li.cil.oc.server.machine.Callbacks.PeripheralCallback import li.cil.oc.server.machine.Machine import li.cil.oc.util.SideTracker -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -118,16 +118,16 @@ trait Component extends network.Component with Node { // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { - super.load(nbt) - if (nbt.hasKey(NodeData.VisibilityTag)) { - _visibility = Visibility.values()(nbt.getInteger(NodeData.VisibilityTag)) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) + if (nbt.contains(NodeData.VisibilityTag)) { + _visibility = Visibility.values()(nbt.getInt(NodeData.VisibilityTag)) } } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setInteger(NodeData.VisibilityTag, _visibility.ordinal()) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putInt(NodeData.VisibilityTag, _visibility.ordinal()) } override def toString = super.toString + s"@$name" diff --git a/src/main/scala/li/cil/oc/server/network/Connector.scala b/src/main/scala/li/cil/oc/server/network/Connector.scala index 8b905f74dd..485315f875 100644 --- a/src/main/scala/li/cil/oc/server/network/Connector.scala +++ b/src/main/scala/li/cil/oc/server/network/Connector.scala @@ -4,7 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.api.network import li.cil.oc.api.network.{Node => ImmutableNode} import li.cil.oc.common.item.data.NodeData -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT trait Connector extends network.Connector with Node { var localBufferSize = 0.0 @@ -119,13 +119,13 @@ trait Connector extends network.Connector with Node { // ----------------------------------------------------------------------- // - override def load(nbt: NBTTagCompound) { - super.load(nbt) + override def loadData(nbt: CompoundNBT) { + super.loadData(nbt) localBuffer = nbt.getDouble(NodeData.BufferTag) } - override def save(nbt: NBTTagCompound) { - super.save(nbt) - nbt.setDouble(NodeData.BufferTag, math.min(localBuffer, localBufferSize)) + override def saveData(nbt: CompoundNBT) { + super.saveData(nbt) + nbt.putDouble(NodeData.BufferTag, math.min(localBuffer, localBufferSize)) } } diff --git a/src/main/scala/li/cil/oc/server/network/Network.scala b/src/main/scala/li/cil/oc/server/network/Network.scala index 3ab2c46055..29226e96aa 100644 --- a/src/main/scala/li/cil/oc/server/network/Network.scala +++ b/src/main/scala/li/cil/oc/server/network/Network.scala @@ -11,12 +11,14 @@ import li.cil.oc.common.tileentity import li.cil.oc.server.network.{Node => MutableNode} import li.cil.oc.util.Color import li.cil.oc.util.SideTracker -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.DyeColor import net.minecraft.nbt._ import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.RegistryKey import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader +import net.minecraft.world.World import scala.collection.JavaConverters._ import scala.collection.mutable @@ -442,14 +444,14 @@ private class Network private(private val data: mutable.Map[String, Network.Vert } object Network extends api.detail.NetworkAPI { - override def joinOrCreateNetwork(world: IBlockAccess, pos: BlockPos): Unit = { - val tileEntity = world.getTileEntity(pos) - if (tileEntity != null && !tileEntity.isInvalid && tileEntity.getWorld != null && !tileEntity.getWorld.isRemote) { - for (side <- EnumFacing.values) { - val npos = tileEntity.getPos.offset(side) - if (tileEntity.getWorld.isBlockLoaded(npos)) { + override def joinOrCreateNetwork(world: IBlockReader, pos: BlockPos): Unit = { + val tileEntity = world.getBlockEntity(pos) + if (tileEntity != null && !tileEntity.isRemoved && tileEntity.getLevel != null && !tileEntity.getLevel.isClientSide) { + for (side <- Direction.values) { + val npos = tileEntity.getBlockPos.relative(side) + if (tileEntity.getLevel.isLoaded(npos)) { val localNode = getNetworkNode(tileEntity, side) - val neighborTileEntity = tileEntity.getWorld.getTileEntity(npos) + val neighborTileEntity = tileEntity.getLevel.getBlockEntity(npos) val neighborNode = getNetworkNode(neighborTileEntity, side.getOpposite) localNode match { case Some(node: MutableNode) => @@ -473,8 +475,8 @@ object Network extends api.detail.NetworkAPI { override def joinOrCreateNetwork(tileEntity: TileEntity): Unit = { if (tileEntity != null) { - val world = tileEntity.getWorld - val pos = tileEntity.getPos + val world = tileEntity.getLevel + val pos = tileEntity.getBlockPos if (world != null && pos != null) { joinOrCreateNetwork(world, pos) } @@ -487,15 +489,15 @@ object Network extends api.detail.NetworkAPI { case _ => } - def getNetworkNode(tileEntity: TileEntity, side: EnumFacing): Option[ImmutableNode] = { + def getNetworkNode(tileEntity: TileEntity, side: Direction): Option[ImmutableNode] = { if (tileEntity != null) { - if (tileEntity.hasCapability(Capabilities.SidedEnvironmentCapability, side)) { - val host = tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side) + if (tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side).isPresent) { + val host = tileEntity.getCapability(Capabilities.SidedEnvironmentCapability, side).orElse(null) if (host != null) return Option(host.sidedNode(side)) } - if (tileEntity.hasCapability(Capabilities.EnvironmentCapability, side)) { - val host = tileEntity.getCapability(Capabilities.EnvironmentCapability, side) + if (tileEntity.getCapability(Capabilities.EnvironmentCapability, side).isPresent) { + val host = tileEntity.getCapability(Capabilities.EnvironmentCapability, side).orElse(null) if (host != null) return Option(host.node) } } @@ -505,21 +507,21 @@ object Network extends api.detail.NetworkAPI { private def getConnectionColor(tileEntity: TileEntity): Int = { if (tileEntity != null) { - if (tileEntity.hasCapability(Capabilities.ColoredCapability, null)) { - val colored = tileEntity.getCapability(Capabilities.ColoredCapability, null) + if (tileEntity.getCapability(Capabilities.ColoredCapability, null).isPresent) { + val colored = tileEntity.getCapability(Capabilities.ColoredCapability, null).orElse(null) if (colored != null && colored.controlsConnectivity) return colored.getColor } } - Color.rgbValues(EnumDyeColor.SILVER) + Color.rgbValues(DyeColor.LIGHT_GRAY) } private def canConnectBasedOnColor(te1: TileEntity, te2: TileEntity) = { val (c1, c2) = (getConnectionColor(te1), getConnectionColor(te2)) - c1 == c2 || c1 == Color.rgbValues(EnumDyeColor.SILVER) || c2 == Color.rgbValues(EnumDyeColor.SILVER) + c1 == c2 || c1 == Color.rgbValues(DyeColor.LIGHT_GRAY) || c2 == Color.rgbValues(DyeColor.LIGHT_GRAY) } - private def canConnectFromSideIM(tileEntity: TileEntity, side: EnumFacing) = + private def canConnectFromSideIM(tileEntity: TileEntity, side: Direction) = tileEntity match { case im: tileentity.traits.ImmibisMicroblock => im.ImmibisMicroblocks_isSideOpen(side.ordinal) case _ => true @@ -539,7 +541,7 @@ object Network extends api.detail.NetworkAPI { WirelessNetwork.remove(endpoint) } - override def leaveWirelessNetwork(endpoint: WirelessEndpoint, dimension: Int) { + override def leaveWirelessNetwork(endpoint: WirelessEndpoint, dimension: RegistryKey[World]) { WirelessNetwork.remove(endpoint, dimension) } @@ -565,21 +567,21 @@ object Network extends api.detail.NetworkAPI { packet } - override def newPacket(nbt: NBTTagCompound) = { + override def newPacket(nbt: CompoundNBT) = { val source = nbt.getString("source") val destination = - if (nbt.hasKey("dest")) null + if (nbt.contains("dest")) null else nbt.getString("dest") - val port = nbt.getInteger("port") - val ttl = nbt.getInteger("ttl") - val data = (for (i <- 0 until nbt.getInteger("dataLength")) yield { - if (nbt.hasKey("data" + i)) { - nbt.getTag("data" + i) match { - case tag: NBTTagByte => Boolean.box(tag.getByte == 1) - case tag: NBTTagInt => Int.box(tag.getInt) - case tag: NBTTagDouble => Double.box(tag.getDouble) - case tag: NBTTagString => tag.getString: AnyRef - case tag: NBTTagByteArray => tag.getByteArray + val port = nbt.getInt("port") + val ttl = nbt.getInt("ttl") + val data = (for (i <- 0 until nbt.getInt("dataLength")) yield { + if (nbt.contains("data" + i)) { + nbt.get("data" + i) match { + case tag: ByteNBT => Boolean.box(tag.getAsByte == 1) + case tag: IntNBT => Int.box(tag.getAsInt) + case tag: DoubleNBT => Double.box(tag.getAsDouble) + case tag: StringNBT => tag.getAsString: AnyRef + case tag: ByteArrayNBT => tag.getAsByteArray } } else null @@ -722,21 +724,21 @@ object Network extends api.detail.NetworkAPI { override def hop() = new Packet(source, destination, port, data, ttl - 1) - override def save(nbt: NBTTagCompound) { - nbt.setString("source", source) + override def saveData(nbt: CompoundNBT) { + nbt.putString("source", source) if (destination != null && !destination.isEmpty) { - nbt.setString("dest", destination) + nbt.putString("dest", destination) } - nbt.setInteger("port", port) - nbt.setInteger("ttl", ttl) - nbt.setInteger("dataLength", data.length) + nbt.putInt("port", port) + nbt.putInt("ttl", ttl) + nbt.putInt("dataLength", data.length) for (i <- data.indices) data(i) match { case null | Unit | None => - case value: java.lang.Boolean => nbt.setBoolean("data" + i, value) - case value: java.lang.Integer => nbt.setInteger("data" + i, value) - case value: java.lang.Double => nbt.setDouble("data" + i, value) - case value: java.lang.String => nbt.setString("data" + i, value) - case value: Array[Byte] => nbt.setByteArray("data" + i, value) + case value: java.lang.Boolean => nbt.putBoolean("data" + i, value) + case value: java.lang.Integer => nbt.putInt("data" + i, value) + case value: java.lang.Double => nbt.putDouble("data" + i, value) + case value: java.lang.String => nbt.putString("data" + i, value) + case value: Array[Byte] => nbt.putByteArray("data" + i, value) case value => OpenComputers.log.warn("Unexpected type while saving network packet: " + value.getClass.getName) } } diff --git a/src/main/scala/li/cil/oc/server/network/Node.scala b/src/main/scala/li/cil/oc/server/network/Node.scala index 6bc30be3eb..82922574d8 100644 --- a/src/main/scala/li/cil/oc/server/network/Node.scala +++ b/src/main/scala/li/cil/oc/server/network/Node.scala @@ -6,7 +6,7 @@ import li.cil.oc.api import li.cil.oc.api.network.Environment import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.{Node => ImmutableNode} -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -71,8 +71,8 @@ trait Node extends ImmutableNode { // ----------------------------------------------------------------------- // - def load(nbt: NBTTagCompound) = { - if (nbt.hasKey("address")) { + def loadData(nbt: CompoundNBT) { + if (nbt.contains("address")) { val newAddress = nbt.getString("address") if (!Strings.isNullOrEmpty(newAddress) && newAddress != address) network match { case wrapper: Network.Wrapper => wrapper.network.remap(this, newAddress) @@ -81,9 +81,9 @@ trait Node extends ImmutableNode { } } - def save(nbt: NBTTagCompound) = { + def saveData(nbt: CompoundNBT) { if (address != null) { - nbt.setString("address", address) + nbt.putString("address", address) } } diff --git a/src/main/scala/li/cil/oc/server/network/Waypoints.scala b/src/main/scala/li/cil/oc/server/network/Waypoints.scala index 5ee18be67e..3c1d3fb317 100644 --- a/src/main/scala/li/cil/oc/server/network/Waypoints.scala +++ b/src/main/scala/li/cil/oc/server/network/Waypoints.scala @@ -4,44 +4,52 @@ import li.cil.oc.Settings import li.cil.oc.common.tileentity.Waypoint import li.cil.oc.util.BlockPosition import li.cil.oc.util.RTree +import net.minecraft.util.RegistryKey +import net.minecraft.world.World import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ import scala.collection.mutable object Waypoints { - val dimensions = mutable.Map.empty[Int, RTree[Waypoint]] + val dimensions = mutable.Map.empty[RegistryKey[World], RTree[Waypoint]] @SubscribeEvent def onWorldUnload(e: WorldEvent.Unload) { - if (!e.getWorld.isRemote) { - dimensions.remove(e.getWorld.provider.getDimension) + if (!e.getWorld.isClientSide) { + e.getWorld match { + case world: World => dimensions.remove(world.dimension) + case _ => + } } } @SubscribeEvent def onWorldLoad(e: WorldEvent.Load) { - if (!e.getWorld.isRemote) { - dimensions.remove(e.getWorld.provider.getDimension) + if (!e.getWorld.isClientSide) { + e.getWorld match { + case world: World => dimensions.remove(world.dimension) + case _ => + } } } // Safety clean up, in case some tile entities didn't properly leave the net. @SubscribeEvent - def onChunkUnload(e: ChunkEvent.Unload) { - e.getChunk.getTileEntityMap.values.foreach { + def onChunkUnloaded(e: ChunkEvent.Unload) { + e.getChunk.getBlockEntitiesPos.map(e.getChunk.getBlockEntity).foreach { case waypoint: Waypoint => remove(waypoint) case _ => } } - def add(waypoint: Waypoint): Unit = if (!waypoint.isInvalid && waypoint.world != null && !waypoint.world.isRemote) { + def add(waypoint: Waypoint): Unit = if (!waypoint.isRemoved && waypoint.world != null && !waypoint.world.isClientSide) { dimensions.getOrElseUpdate(dimension(waypoint), new RTree[Waypoint](Settings.get.rTreeMaxEntries)((waypoint) => (waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5))).add(waypoint) } - def remove(waypoint: Waypoint): Unit = if (waypoint.world != null && !waypoint.world.isRemote) { + def remove(waypoint: Waypoint): Unit = if (waypoint.world != null && !waypoint.world.isClientSide) { dimensions.get(dimension(waypoint)) match { case Some(set) => set.remove(waypoint) case _ => @@ -49,13 +57,13 @@ object Waypoints { } def findWaypoints(pos: BlockPosition, range: Double): Iterable[Waypoint] = { - dimensions.get(pos.world.get.provider.getDimension) match { + dimensions.get(pos.world.get.dimension) match { case Some(set) => - val bounds = pos.bounds.grow(range * 0.5, range * 0.5, range * 0.5) + val bounds = pos.bounds.inflate(range * 0.5, range * 0.5, range * 0.5) set.query((bounds.minX, bounds.minY, bounds.minZ), (bounds.maxX, bounds.maxY, bounds.maxZ)) case _ => Iterable.empty } } - private def dimension(waypoint: Waypoint) = waypoint.world.provider.getDimension + private def dimension(waypoint: Waypoint) = waypoint.world.dimension } diff --git a/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala b/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala index ad9b3707f9..61faeece03 100644 --- a/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala @@ -6,35 +6,39 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.RTree -import net.minecraft.util.math.Vec3d +import net.minecraft.util.RegistryKey +import net.minecraft.util.math.vector.Vector3d +import net.minecraft.world.World import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.eventbus.api.SubscribeEvent import scala.collection.convert.WrapAsScala._ import scala.collection.mutable object WirelessNetwork { - val dimensions = mutable.Map.empty[Int, RTree[WirelessEndpoint]] + val dimensions = mutable.Map.empty[RegistryKey[World], RTree[WirelessEndpoint]] @SubscribeEvent def onWorldUnload(e: WorldEvent.Unload) { - if (!e.getWorld.isRemote) { - dimensions.remove(e.getWorld.provider.getDimension) + if (!e.getWorld.isClientSide) e.getWorld match { + case world: World => dimensions.remove(world.dimension) + case _ => } } @SubscribeEvent def onWorldLoad(e: WorldEvent.Load) { - if (!e.getWorld.isRemote) { - dimensions.remove(e.getWorld.provider.getDimension) + if (!e.getWorld.isClientSide) e.getWorld match { + case world: World => dimensions.remove(world.dimension) + case _ => } } // Safety clean up, in case some tile entities didn't properly leave the net. @SubscribeEvent - def onChunkUnload(e: ChunkEvent.Unload) { - e.getChunk.getTileEntityMap.values.foreach { + def onChunkUnloaded(e: ChunkEvent.Unload) { + e.getChunk.getBlockEntitiesPos.map(e.getChunk.getBlockEntity).foreach { case endpoint: WirelessEndpoint => remove(endpoint) case _ => } @@ -62,7 +66,7 @@ object WirelessNetwork { } } - def remove(endpoint: WirelessEndpoint, dimension: Int) = { + def remove(endpoint: WirelessEndpoint, dimension: RegistryKey[World]) = { dimensions.get(dimension) match { case Some(set) => set.remove(endpoint) case _ => false @@ -91,7 +95,7 @@ object WirelessNetwork { } } - private def dimension(endpoint: WirelessEndpoint) = endpoint.world.provider.getDimension + private def dimension(endpoint: WirelessEndpoint) = endpoint.world.dimension private def offset(endpoint: WirelessEndpoint, value: Double) = (endpoint.x + 0.5 + value, endpoint.y + 0.5 + value, endpoint.z + 0.5 + value) @@ -117,8 +121,8 @@ object WirelessNetwork { // the message. val world = endpoint.world - val origin = new Vec3d(reference.x, reference.y, reference.z) - val target = new Vec3d(endpoint.x, endpoint.y, endpoint.z) + val origin = new Vector3d(reference.x, reference.y, reference.z) + val target = new Vector3d(endpoint.x, endpoint.y, endpoint.z) // Vector from reference endpoint (sender) to this one (receiver). val delta = subtract(target, origin) @@ -127,10 +131,10 @@ object WirelessNetwork { // Get the vectors that are orthogonal to the direction vector. val up = if (v.x == 0 && v.z == 0) { assert(v.y != 0) - new Vec3d(1, 0, 0) + new Vector3d(1, 0, 0) } else { - new Vec3d(0, 1, 0) + new Vector3d(0, 1, 0) } val side = crossProduct(v, up) val top = crossProduct(v, side) @@ -140,16 +144,16 @@ object WirelessNetwork { val samples = math.max(1, math.sqrt(gap).toInt) for (i <- 0 until samples) { - val rGap = world.rand.nextDouble() * gap + val rGap = world.random.nextDouble() * gap // Adding some jitter to avoid only tracking the perfect line between // two endpoints when they are diagonal to each other for example. - val rSide = world.rand.nextInt(3) - 1 - val rTop = world.rand.nextInt(3) - 1 + val rSide = world.random.nextInt(3) - 1 + val rTop = world.random.nextInt(3) - 1 val x = (origin.x + v.x * rGap + side.x * rSide + top.x * rTop).toInt val y = (origin.y + v.y * rGap + side.y * rSide + top.y * rTop).toInt val z = (origin.z + v.z * rGap + side.z * rSide + top.z * rTop).toInt val blockPos = BlockPosition(x, y, z, world) - if (world.isBlockLoaded(blockPos)) Option(world.getBlock(blockPos)) match { + if (world.isLoaded(blockPos)) Option(world.getBlock(blockPos)) match { case Some(block) => hardness += block.getBlockHardness(blockPos) case _ => } @@ -164,7 +168,7 @@ object WirelessNetwork { else true } - private def subtract(v1: Vec3d, v2: Vec3d) = new Vec3d(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) + private def subtract(v1: Vector3d, v2: Vector3d) = new Vector3d(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) - private def crossProduct(v1: Vec3d, v2: Vec3d) = new Vec3d(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x) + private def crossProduct(v1: Vector3d, v2: Vector3d) = new Vector3d(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x) } diff --git a/src/main/scala/li/cil/oc/util/Audio.scala b/src/main/scala/li/cil/oc/util/Audio.scala index 14e6cc969c..c6cc5fc585 100644 --- a/src/main/scala/li/cil/oc/util/Audio.scala +++ b/src/main/scala/li/cil/oc/util/Audio.scala @@ -5,19 +5,18 @@ import java.nio.ByteBuffer import li.cil.oc.OpenComputers import li.cil.oc.Settings import net.minecraft.client.Minecraft -import net.minecraft.client.audio.PositionedSoundRecord -import net.minecraft.init.SoundEvents +import net.minecraft.client.audio.SimpleSound +import net.minecraft.util.SoundEvents import net.minecraft.util.ResourceLocation import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvent import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.event.TickEvent.ClientTickEvent import org.lwjgl.BufferUtils -import org.lwjgl.openal.AL import org.lwjgl.openal.AL10 -import org.lwjgl.openal.OpenALException import scala.collection.mutable @@ -37,7 +36,7 @@ object Audio { private val sources = mutable.Set.empty[Source] - private def volume = Minecraft.getMinecraft.gameSettings.getSoundLevel(SoundCategory.BLOCKS) + private def volume = Minecraft.getInstance.options.getSoundSourceVolume(SoundCategory.BLOCKS) private var disableAudio = false @@ -46,8 +45,8 @@ object Audio { } def play(x: Float, y: Float, z: Float, pattern: String, frequencyInHz: Int = 1000, durationInMilliseconds: Int = 200): Unit = { - val mc = Minecraft.getMinecraft - val distanceBasedGain = math.max(0, 1 - mc.player.getDistance(x, y, z) / maxDistance).toFloat + val mc = Minecraft.getInstance + val distanceBasedGain = math.max(0, 1 - mc.player.position.distanceTo(new Vector3d(x, y, z)) / maxDistance).toFloat val gain = distanceBasedGain * volume if (gain <= 0 || amplitude <= 0) return @@ -61,54 +60,52 @@ object Audio { val clampedFrequency = ((frequencyInHz - 20) max 0 min 1980) / 1980f + 0.5f var delay = 0 for (ch <- pattern) { - val record = new PositionedSoundRecord(SoundEvents.BLOCK_NOTE_HARP, SoundCategory.BLOCKS, gain, clampedFrequency, new BlockPos(x, y, z)) - if (delay == 0) mc.getSoundHandler.playSound(record) - else mc.getSoundHandler.playDelayedSound(record, delay) + val record = new SimpleSound(SoundEvents.NOTE_BLOCK_HARP, SoundCategory.BLOCKS, gain, clampedFrequency, new BlockPos(x, y, z)) + if (delay == 0) mc.getSoundManager.play(record) + else mc.getSoundManager.playDelayed(record, delay) delay += ((if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds) * 20 / 1000) max 1 } } else { - if (AL.isCreated) { - val sampleCounts = pattern.toCharArray. - map(ch => if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds). - map(_ * sampleRate / 1000) - // 50ms pause between pattern parts. - val pauseSampleCount = 50 * sampleRate / 1000 - val data = BufferUtils.createByteBuffer(sampleCounts.sum + (sampleCounts.length - 1) * pauseSampleCount) - val step = frequencyInHz / sampleRate.toFloat - var offset = 0f - for (sampleCount <- sampleCounts) { - for (sample <- 0 until sampleCount) { - val angle = 2 * math.Pi * offset - val value = (math.signum(math.sin(angle)) * amplitude).toByte ^ 0x80 - offset += step - if (offset > 1) offset -= 1 - data.put(value.toByte) - } - if (data.hasRemaining) { - for (sample <- 0 until pauseSampleCount) { - data.put(127: Byte) - } - } + val sampleCounts = pattern.toCharArray. + map(ch => if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds). + map(_ * sampleRate / 1000) + // 50ms pause between pattern parts. + val pauseSampleCount = 50 * sampleRate / 1000 + val data = BufferUtils.createByteBuffer(sampleCounts.sum + (sampleCounts.length - 1) * pauseSampleCount) + val step = frequencyInHz / sampleRate.toFloat + var offset = 0f + for (sampleCount <- sampleCounts) { + for (sample <- 0 until sampleCount) { + val angle = 2 * math.Pi * offset + val value = (math.signum(math.sin(angle)) * amplitude).toByte ^ 0x80 + offset += step + if (offset > 1) offset -= 1 + data.put(value.toByte) } - data.rewind() - - // Watch out for sound cards running out of memory... this apparently - // really does happen. I'm assuming this is due to too many sounds being - // kept loaded, since from what I can see OC's releasing its audio - // memory as it should. - try sources.synchronized(sources += new Source(x, y, z, data, gain)) catch { - case e: LessUselessOpenALException => - if (e.errorCode == AL10.AL_OUT_OF_MEMORY) { - // Well... let's just stop here. - OpenComputers.log.info("Couldn't play computer speaker sound because your sound card ran out of memory. Either your sound card is just really low-end, or there are just too many sounds in use already by other mods. Disabling computer speakers to avoid spamming your log file now.") - disableAudio = true - } - else { - OpenComputers.log.warn("Error playing computer speaker sound.", e) - } + if (data.hasRemaining) { + for (sample <- 0 until pauseSampleCount) { + data.put(127: Byte) + } } } + data.rewind() + + // Watch out for sound cards running out of memory... this apparently + // really does happen. I'm assuming this is due to too many sounds being + // kept loaded, since from what I can see OC's releasing its audio + // memory as it should. + try sources.synchronized(sources += new Source(x, y, z, data, gain)) catch { + case e: OpenALException => + if (e.errorCode == AL10.AL_OUT_OF_MEMORY) { + // Well... let's just stop here. + OpenComputers.log.info("Couldn't play computer speaker sound because your sound card ran out of memory. Either your sound card is just really low-end, or there are just too many sounds in use already by other mods. Disabling computer speakers to avoid spamming your log file now.") + disableAudio = true + } + else { + OpenComputers.log.warn("Error playing computer speaker sound.", e) + } + } } } @@ -117,12 +114,10 @@ object Audio { sources.synchronized(sources --= sources.filter(_.checkFinished)) // Clear error stack. - if (AL.isCreated) { - try AL10.alGetError() catch { - case _: UnsatisfiedLinkError => - OpenComputers.log.warn("Negotiations with OpenAL broke down, disabling sounds.") - disableAudio = true - } + try AL10.alGetError() catch { + case _: UnsatisfiedLinkError => + OpenComputers.log.warn("Negotiations with OpenAL broke down, disabling sounds.") + disableAudio = true } } } @@ -178,13 +173,13 @@ object Audio { } // Having the error code in an accessible way is really cool, you know. - class LessUselessOpenALException(val errorCode: Int) extends OpenALException(errorCode) + class OpenALException(val errorCode: Int) extends RuntimeException // Custom implementation of Util.checkALError() that uses our custom exception. def checkALError(): Unit = { val errorCode = AL10.alGetError() if (errorCode != AL10.AL_NO_ERROR) { - throw new LessUselessOpenALException(errorCode) + throw new OpenALException(errorCode) } } diff --git a/src/main/scala/li/cil/oc/util/BlockPosition.scala b/src/main/scala/li/cil/oc/util/BlockPosition.scala index e7b120e8f7..04c9e295a9 100644 --- a/src/main/scala/li/cil/oc/util/BlockPosition.scala +++ b/src/main/scala/li/cil/oc/util/BlockPosition.scala @@ -3,10 +3,10 @@ package li.cil.oc.util import com.google.common.hash.Hashing import li.cil.oc.api.network.EnvironmentHost import net.minecraft.entity.Entity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.World class BlockPosition(val x: Int, val y: Int, val z: Int, val world: Option[World]) { @@ -17,22 +17,22 @@ class BlockPosition(val x: Int, val y: Int, val z: Int, val world: Option[World] world ) - def offset(direction: EnumFacing, n: Int) = new BlockPosition( - x + direction.getFrontOffsetX * n, - y + direction.getFrontOffsetY * n, - z + direction.getFrontOffsetZ * n, + def offset(direction: Direction, n: Int) = new BlockPosition( + x + direction.getStepX * n, + y + direction.getStepY * n, + z + direction.getStepZ * n, world ) - def offset(direction: EnumFacing): BlockPosition = offset(direction, 1) + def offset(direction: Direction): BlockPosition = offset(direction, 1) - def offset(x: Double, y: Double, z: Double) = new Vec3d(this.x + x, this.y + y, this.z + z) + def offset(x: Double, y: Double, z: Double) = new Vector3d(this.x + x, this.y + y, this.z + z) def bounds = new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1) def toBlockPos = new BlockPos(x, y, z) - def toVec3 = new Vec3d(x + 0.5, y + 0.5, z + 0.5) + def toVec3 = new Vector3d(x + 0.5, y + 0.5, z + 0.5) override def equals(obj: scala.Any) = obj match { case position: BlockPosition => position.x == x && position.y == y && position.z == z && position.world == world @@ -61,13 +61,13 @@ object BlockPosition { def apply(x: Double, y: Double, z: Double) = new BlockPosition(x, y, z, None) - def apply(v: Vec3d) = new BlockPosition(v.x, v.y, v.z, None) + def apply(v: Vector3d) = new BlockPosition(v.x, v.y, v.z, None) - def apply(v: Vec3d, world: World) = new BlockPosition(v.x, v.y, v.z, Option(world)) + def apply(v: Vector3d, world: World) = new BlockPosition(v.x, v.y, v.z, Option(world)) def apply(host: EnvironmentHost): BlockPosition = BlockPosition(host.xPosition, host.yPosition, host.zPosition, host.world) - def apply(entity: Entity): BlockPosition = BlockPosition(entity.posX, entity.posY, entity.posZ, entity.world) + def apply(entity: Entity): BlockPosition = BlockPosition(entity.getX, entity.getY, entity.getZ, entity.level) def apply(pos: BlockPos, world: World): BlockPosition = BlockPosition(pos.getX, pos.getY, pos.getZ, world) diff --git a/src/main/scala/li/cil/oc/util/Color.scala b/src/main/scala/li/cil/oc/util/Color.scala index 8d319ccb08..85b02125ad 100644 --- a/src/main/scala/li/cil/oc/util/Color.scala +++ b/src/main/scala/li/cil/oc/util/Color.scala @@ -1,29 +1,28 @@ package li.cil.oc.util -import net.minecraft.item.EnumDyeColor +import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack -import net.minecraftforge.oredict.OreDictionary import scala.collection.convert.WrapAsScala._ object Color { val rgbValues = Map( - EnumDyeColor.BLACK -> 0x444444, // 0x1E1B1B - EnumDyeColor.RED -> 0xB3312C, - EnumDyeColor.GREEN -> 0x339911, // 0x3B511A - EnumDyeColor.BROWN -> 0x51301A, - EnumDyeColor.BLUE -> 0x6666FF, // 0x253192 - EnumDyeColor.PURPLE -> 0x7B2FBE, - EnumDyeColor.CYAN -> 0x66FFFF, // 0x287697 - EnumDyeColor.SILVER -> 0xABABAB, - EnumDyeColor.GRAY -> 0x666666, // 0x434343 - EnumDyeColor.PINK -> 0xD88198, - EnumDyeColor.LIME -> 0x66FF66, // 0x41CD34 - EnumDyeColor.YELLOW -> 0xFFFF66, // 0xDECF2A - EnumDyeColor.LIGHT_BLUE -> 0xAAAAFF, // 0x6689D3 - EnumDyeColor.MAGENTA -> 0xC354CD, - EnumDyeColor.ORANGE -> 0xEB8844, - EnumDyeColor.WHITE -> 0xF0F0F0 + DyeColor.BLACK -> 0x444444, // 0x1E1B1B + DyeColor.RED -> 0xB3312C, + DyeColor.GREEN -> 0x339911, // 0x3B511A + DyeColor.BROWN -> 0x51301A, + DyeColor.BLUE -> 0x6666FF, // 0x253192 + DyeColor.PURPLE -> 0x7B2FBE, + DyeColor.CYAN -> 0x66FFFF, // 0x287697 + DyeColor.LIGHT_GRAY -> 0xABABAB, + DyeColor.GRAY -> 0x666666, // 0x434343 + DyeColor.PINK -> 0xD88198, + DyeColor.LIME -> 0x66FF66, // 0x41CD34 + DyeColor.YELLOW -> 0xFFFF66, // 0xDECF2A + DyeColor.LIGHT_BLUE -> 0xAAAAFF, // 0x6689D3 + DyeColor.MAGENTA -> 0xC354CD, + DyeColor.ORANGE -> 0xEB8844, + DyeColor.WHITE -> 0xF0F0F0 ) val dyes = Array( @@ -44,31 +43,32 @@ object Color { "dyeOrange", "dyeWhite") + @Deprecated val byOreName = Map( - "dyeBlack" -> EnumDyeColor.BLACK, - "dyeRed" -> EnumDyeColor.RED, - "dyeGreen" -> EnumDyeColor.GREEN, - "dyeBrown" -> EnumDyeColor.BROWN, - "dyeBlue" -> EnumDyeColor.BLUE, - "dyePurple" -> EnumDyeColor.PURPLE, - "dyeCyan" -> EnumDyeColor.CYAN, - "dyeLightGray" -> EnumDyeColor.SILVER, - "dyeGray" -> EnumDyeColor.GRAY, - "dyePink" -> EnumDyeColor.PINK, - "dyeLime" -> EnumDyeColor.LIME, - "dyeYellow" -> EnumDyeColor.YELLOW, - "dyeLightBlue" -> EnumDyeColor.LIGHT_BLUE, - "dyeMagenta" -> EnumDyeColor.MAGENTA, - "dyeOrange" -> EnumDyeColor.ORANGE, - "dyeWhite" -> EnumDyeColor.WHITE) + "dyeBlack" -> DyeColor.BLACK, + "dyeRed" -> DyeColor.RED, + "dyeGreen" -> DyeColor.GREEN, + "dyeBrown" -> DyeColor.BROWN, + "dyeBlue" -> DyeColor.BLUE, + "dyePurple" -> DyeColor.PURPLE, + "dyeCyan" -> DyeColor.CYAN, + "dyeLightGray" -> DyeColor.LIGHT_GRAY, + "dyeGray" -> DyeColor.GRAY, + "dyePink" -> DyeColor.PINK, + "dyeLime" -> DyeColor.LIME, + "dyeYellow" -> DyeColor.YELLOW, + "dyeLightBlue" -> DyeColor.LIGHT_BLUE, + "dyeMagenta" -> DyeColor.MAGENTA, + "dyeOrange" -> DyeColor.ORANGE, + "dyeWhite" -> DyeColor.WHITE) - val byTier = Array(EnumDyeColor.SILVER, EnumDyeColor.YELLOW, EnumDyeColor.CYAN, EnumDyeColor.MAGENTA) + private val byTag = DyeColor.values.map(col => (col.getTag.getName, col)).toMap - def byMeta(meta: EnumDyeColor) = byOreName(dyes(meta.getDyeDamage)) + val byTier = Array(DyeColor.LIGHT_GRAY, DyeColor.YELLOW, DyeColor.CYAN, DyeColor.MAGENTA) - def findDye(stack: ItemStack) = byOreName.keys.find(OreDictionary.getOres(_).exists(oreStack => OreDictionary.itemMatches(stack, oreStack, false))) + def findDye(stack: ItemStack) = byTag.keys.find(stack.getItem.getTags.contains) def isDye(stack: ItemStack) = findDye(stack).isDefined - def dyeColor(stack: ItemStack) = findDye(stack).fold(EnumDyeColor.MAGENTA)(byOreName(_)) + def dyeColor(stack: ItemStack) = findDye(stack).fold(DyeColor.MAGENTA)(byTag(_)) } diff --git a/src/main/scala/li/cil/oc/util/ExtendedAABB.scala b/src/main/scala/li/cil/oc/util/ExtendedAABB.scala index 8dda6a5a01..0c831ffe26 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedAABB.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedAABB.scala @@ -1,9 +1,9 @@ package li.cil.oc.util -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.vector.Vector3d import scala.language.implicitConversions @@ -23,9 +23,9 @@ object ExtendedAABB { bounds.maxZ + pos.getZ) } - def min = new Vec3d(bounds.minX, bounds.minY, bounds.minZ) + def minVec = new Vector3d(bounds.minX, bounds.minY, bounds.minZ) - def max = new Vec3d(bounds.maxX, bounds.maxY, bounds.maxZ) + def maxVec = new Vector3d(bounds.maxX, bounds.maxY, bounds.maxZ) def volume: Int = { val sx = ((bounds.maxX - bounds.minX) * 16).round.toInt @@ -41,18 +41,18 @@ object ExtendedAABB { sx * sy * 2 + sx * sz * 2 + sy * sz * 2 } - def rotateTowards(facing: EnumFacing) = rotateY(facing match { - case EnumFacing.WEST => 3 - case EnumFacing.NORTH => 2 - case EnumFacing.EAST => 1 + def rotateTowards(facing: Direction) = rotateY(facing match { + case Direction.WEST => 3 + case Direction.NORTH => 2 + case Direction.EAST => 1 case _ => 0 }) def rotateY(count: Int): AxisAlignedBB = { - var min = new Vec3d(bounds.minX - 0.5, bounds.minY - 0.5, bounds.minZ - 0.5) - var max = new Vec3d(bounds.maxX - 0.5, bounds.maxY - 0.5, bounds.maxZ - 0.5) - min = min.rotateYaw(count * Math.PI.toFloat * 0.5f) - max = max.rotateYaw(count * Math.PI.toFloat * 0.5f) + var min = new Vector3d(bounds.minX - 0.5, bounds.minY - 0.5, bounds.minZ - 0.5) + var max = new Vector3d(bounds.maxX - 0.5, bounds.maxY - 0.5, bounds.maxZ - 0.5) + min = min.yRot(count * Math.PI.toFloat * 0.5f) + max = max.yRot(count * Math.PI.toFloat * 0.5f) new AxisAlignedBB( (math.min(min.x + 0.5, max.x + 0.5) * 32).round / 32f, (math.min(min.y + 0.5, max.y + 0.5) * 32).round / 32f, diff --git a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala index df744224ac..8fbee2b42f 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala @@ -3,10 +3,10 @@ package li.cil.oc.util import li.cil.oc.api.internal.MultiTank import li.cil.oc.api.machine.Arguments import net.minecraft.inventory.IInventory -import net.minecraft.util.EnumFacing -import net.minecraftforge.fluids.Fluid +import net.minecraft.util.Direction +import net.minecraftforge.fluids.FluidAttributes +import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.capability.IFluidHandler -import net.minecraftforge.fluids.capability.IFluidTankProperties import net.minecraftforge.items.IItemHandler import scala.language.implicitConversions @@ -20,7 +20,7 @@ object ExtendedArguments { if (!isDefined(index) || !hasValue(index)) default else math.max(0, math.min(64, args.checkInteger(index))) - def optFluidCount(index: Int, default: Int = Fluid.BUCKET_VOLUME) = + def optFluidCount(index: Int, default: Int = FluidAttributes.BUCKET_VOLUME) = if (!isDefined(index) || !hasValue(index)) default else math.max(0, args.checkInteger(index)) @@ -51,53 +51,53 @@ object ExtendedArguments { def checkTankProperties(handler: IFluidHandler, n: Int) = { val tank = args.checkInteger(n) - 1 - if (tank < 0 || tank >= handler.getTankProperties.length) { + if (tank < 0 || tank >= handler.getTanks) { throw new IllegalArgumentException("invalid tank index") } - handler.getTankProperties()(tank) + new TankProperties(handler.getTankCapacity(tank), handler.getFluidInTank(tank)) } - def optTankProperties(handler: IFluidHandler, n: Int, default: IFluidTankProperties) = { + def optTankProperties(handler: IFluidHandler, n: Int, default: TankProperties) = { if (!isDefined(n)) default else checkTankProperties(handler, n) } - def checkSideAny(index: Int) = checkSide(index, EnumFacing.values: _*) + def checkSideAny(index: Int) = checkSide(index, Direction.values: _*) - def optSideAny(index: Int, default: EnumFacing) = + def optSideAny(index: Int, default: Direction) = if (!isDefined(index)) default else checkSideAny(index) - def checkSideExcept(index: Int, invalid: EnumFacing*) = checkSide(index, EnumFacing.values.filterNot(invalid.contains): _*) + def checkSideExcept(index: Int, invalid: Direction*) = checkSide(index, Direction.values.filterNot(invalid.contains): _*) - def optSideExcept(index: Int, default: EnumFacing, invalid: EnumFacing*) = + def optSideExcept(index: Int, default: Direction, invalid: Direction*) = if (!isDefined(index)) default else checkSideExcept(index, invalid: _*) - def checkSideForAction(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.UP, EnumFacing.DOWN) + def checkSideForAction(index: Int) = checkSide(index, Direction.SOUTH, Direction.UP, Direction.DOWN) - def optSideForAction(index: Int, default: EnumFacing) = + def optSideForAction(index: Int, default: Direction) = if (!isDefined(index)) default else checkSideForAction(index) - def checkSideForMovement(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.NORTH, EnumFacing.UP, EnumFacing.DOWN) + def checkSideForMovement(index: Int) = checkSide(index, Direction.SOUTH, Direction.NORTH, Direction.UP, Direction.DOWN) - def optSideForMovement(index: Int, default: EnumFacing) = + def optSideForMovement(index: Int, default: Direction) = if (!isDefined(index)) default else checkSideForMovement(index) - def checkSideForFace(index: Int, facing: EnumFacing) = checkSideExcept(index, facing.getOpposite) + def checkSideForFace(index: Int, facing: Direction) = checkSideExcept(index, facing.getOpposite) - def optSideForFace(index: Int, default: EnumFacing) = + def optSideForFace(index: Int, default: Direction) = if (!isDefined(index)) default else checkSideForAction(index) - private def checkSide(index: Int, allowed: EnumFacing*) = { + private def checkSide(index: Int, allowed: Direction*) = { val side = args.checkInteger(index) if (side < 0 || side > 5) { throw new IllegalArgumentException("invalid side") } - val direction = EnumFacing.getFront(side) + val direction = Direction.from3DDataValue(side) if (allowed.isEmpty || (allowed contains direction)) direction else throw new IllegalArgumentException("unsupported side") } @@ -107,4 +107,7 @@ object ExtendedArguments { private def hasValue(index: Int) = args.checkAny(index) != null } + @Deprecated + class TankProperties(val capacity: Int, val contents: FluidStack) + } diff --git a/src/main/scala/li/cil/oc/util/ExtendedBlock.scala b/src/main/scala/li/cil/oc/util/ExtendedBlock.scala index 6842bfa122..2358d51b21 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedBlock.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedBlock.scala @@ -1,8 +1,9 @@ package li.cil.oc.util import net.minecraft.block.Block -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.fluids.IFluidBlock +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction import scala.language.implicitConversions @@ -11,23 +12,23 @@ object ExtendedBlock { implicit def extendedBlock(block: Block): ExtendedBlock = new ExtendedBlock(block) class ExtendedBlock(val block: Block) { + @Deprecated def isAir(position: BlockPosition) = block.isAir(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) - def isReplaceable(position: BlockPosition) = block.isReplaceable(position.world.get, position.toBlockPos) + @Deprecated + def isReplaceable(position: BlockPosition) = block.defaultBlockState.getMaterial.isReplaceable - def getBlockHardness(position: BlockPosition) = block.getBlockHardness(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) + @Deprecated + def getBlockHardness(position: BlockPosition) = position.world.get.getBlockState(position.toBlockPos).getDestroySpeed(position.world.get, position.toBlockPos) - def getSelectedBoundingBoxFromPool(position: BlockPosition) = block.getSelectedBoundingBox(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) - - def getCollisionBoundingBoxFromPool(position: BlockPosition) = block.getCollisionBoundingBox(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) - - def getComparatorInputOverride(position: BlockPosition, side: EnumFacing) = block.getComparatorInputOverride(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) + @Deprecated + def getComparatorInputOverride(position: BlockPosition, side: Direction) = block.getAnalogOutputSignal(position.world.get.getBlockState(position.toBlockPos), position.world.get, position.toBlockPos) } implicit def extendedFluidBlock(block: IFluidBlock): ExtendedFluidBlock = new ExtendedFluidBlock(block) class ExtendedFluidBlock(val block: IFluidBlock) { - def drain(position: BlockPosition, doDrain: Boolean) = block.drain(position.world.get, position.toBlockPos, doDrain) + def drain(position: BlockPosition, action: FluidAction) = block.drain(position.world.get, position.toBlockPos, action) def canDrain(position: BlockPosition) = block.canDrain(position.world.get, position.toBlockPos) diff --git a/src/main/scala/li/cil/oc/util/ExtendedEnumFacing.scala b/src/main/scala/li/cil/oc/util/ExtendedEnumFacing.scala index 3c8b89f259..6e5ffea946 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedEnumFacing.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedEnumFacing.scala @@ -1,14 +1,14 @@ package li.cil.oc.util -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.language.implicitConversions object ExtendedEnumFacing { - implicit def extendedEnumFacing(facing: EnumFacing): ExtendedEnumFacing = new ExtendedEnumFacing(facing) + implicit def ExtendedEnumFacing(facing: Direction): ExtendedEnumFacing = new ExtendedEnumFacing(facing) - class ExtendedEnumFacing(val facing: EnumFacing) { - // Copy-pasta from old Forge's ForgeDirection, because MC's equivalent in EnumFacing is client side only \o/ + class ExtendedEnumFacing(val facing: Direction) { + // Copy-pasta from old Forge's ForgeDirection, because MC's equivalent in Direction is client side only \o/ private val ROTATION_MATRIX = Array( Array(0, 1, 4, 5, 3, 2, 6), Array(0, 1, 5, 4, 2, 3, 6), @@ -18,8 +18,8 @@ object ExtendedEnumFacing { Array(3, 2, 0, 1, 4, 5, 6), Array(0, 1, 2, 3, 4, 5, 6)) - def getRotation(axis: EnumFacing) = { - EnumFacing.getFront(ROTATION_MATRIX(axis.ordinal)(facing.ordinal)) + def getRotation(axis: Direction) = { + Direction.from3DDataValue(ROTATION_MATRIX(axis.ordinal)(facing.ordinal)) } } diff --git a/src/main/scala/li/cil/oc/util/ExtendedInventory.scala b/src/main/scala/li/cil/oc/util/ExtendedInventory.scala index 9cc44db3db..d404f0f2c7 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedInventory.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedInventory.scala @@ -11,11 +11,11 @@ object ExtendedInventory { implicit def extendedInventory(inventory: IInventory): ExtendedInventory = new ExtendedInventory(inventory) class ExtendedInventory(val inventory: IInventory) extends mutable.IndexedSeq[ItemStack] { - override def length = inventory.getSizeInventory + override def length = inventory.getContainerSize - override def update(idx: Int, elem: ItemStack) = inventory.setInventorySlotContents(idx, elem) + override def update(idx: Int, elem: ItemStack) = inventory.setItem(idx, elem) - override def apply(idx: Int) = inventory.getStackInSlot(idx) + override def apply(idx: Int) = inventory.getItem(idx) } } diff --git a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala index 3f604b48cc..8552837e04 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala @@ -3,7 +3,7 @@ package li.cil.oc.util import com.google.common.base.Charsets import net.minecraft.item.ItemStack import net.minecraft.nbt._ -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.WrapAsScala._ @@ -14,62 +14,62 @@ import scala.reflect.ClassTag object ExtendedNBT { - implicit def toNbt(value: Boolean): NBTTagByte = new NBTTagByte(if (value) 1 else 0) + implicit def toNbt(value: Boolean): ByteNBT = ByteNBT.valueOf(value) - implicit def toNbt(value: Byte): NBTTagByte = new NBTTagByte(value) + implicit def toNbt(value: Byte): ByteNBT = ByteNBT.valueOf(value) - implicit def toNbt(value: Short): NBTTagShort = new NBTTagShort(value) + implicit def toNbt(value: Short): ShortNBT = ShortNBT.valueOf(value) - implicit def toNbt(value: Int): NBTTagInt = new NBTTagInt(value) + implicit def toNbt(value: Int): IntNBT = IntNBT.valueOf(value) - implicit def toNbt(value: Long): NBTTagLong = new NBTTagLong(value) + implicit def toNbt(value: Long): LongNBT = LongNBT.valueOf(value) - implicit def toNbt(value: Float): NBTTagFloat = new NBTTagFloat(value) + implicit def toNbt(value: Float): FloatNBT = FloatNBT.valueOf(value) - implicit def toNbt(value: Double): NBTTagDouble = new NBTTagDouble(value) + implicit def toNbt(value: Double): DoubleNBT = DoubleNBT.valueOf(value) - implicit def toNbt(value: Array[Byte]): NBTTagByteArray = new NBTTagByteArray(value) + implicit def toNbt(value: Array[Byte]): ByteArrayNBT = new ByteArrayNBT(value) - implicit def toNbt(value: Array[Int]): NBTTagIntArray = new NBTTagIntArray(value) + implicit def toNbt(value: Array[Int]): IntArrayNBT = new IntArrayNBT(value) - implicit def toNbt(value: Array[Boolean]): NBTTagByteArray = new NBTTagByteArray(value.map(if (_) 1: Byte else 0: Byte)) + implicit def toNbt(value: Array[Boolean]): ByteArrayNBT = new ByteArrayNBT(value.map(if (_) 1: Byte else 0: Byte)) - implicit def toNbt(value: String): NBTTagString = new NBTTagString(value) + implicit def toNbt(value: String): StringNBT = StringNBT.valueOf(value) - implicit def toNbt(value: ItemStack): NBTTagCompound = { - val nbt = new NBTTagCompound() + implicit def toNbt(value: ItemStack): CompoundNBT = { + val nbt = new CompoundNBT() if (value != null) { - value.writeToNBT(nbt) + value.save(nbt) } nbt } - implicit def toNbt(value: NBTTagCompound => Unit): NBTTagCompound = { - val nbt = new NBTTagCompound() + implicit def toNbt(value: CompoundNBT => Unit): CompoundNBT = { + val nbt = new CompoundNBT() value(nbt) nbt } - implicit def toNbt(value: Map[String, _]): NBTTagCompound = { - val nbt = new NBTTagCompound() + implicit def toNbt(value: Map[String, _]): CompoundNBT = { + val nbt = new CompoundNBT() for ((key, value) <- value) value match { - case value: Boolean => nbt.setTag(key, value) - case value: Byte => nbt.setTag(key, value) - case value: Short => nbt.setTag(key, value) - case value: Int => nbt.setTag(key, value) - case value: Long => nbt.setTag(key, value) - case value: Float => nbt.setTag(key, value) - case value: Double => nbt.setTag(key, value) - case value: Array[Byte] => nbt.setTag(key, value) - case value: Array[Int] => nbt.setTag(key, value) - case value: String => nbt.setTag(key, value) - case value: ItemStack => nbt.setTag(key, value) + case value: Boolean => nbt.put(key, value) + case value: Byte => nbt.put(key, value) + case value: Short => nbt.put(key, value) + case value: Int => nbt.put(key, value) + case value: Long => nbt.put(key, value) + case value: Float => nbt.put(key, value) + case value: Double => nbt.put(key, value) + case value: Array[Byte] => nbt.put(key, value) + case value: Array[Int] => nbt.put(key, value) + case value: String => nbt.put(key, value) + case value: ItemStack => nbt.put(key, value) case _ => } nbt } - def typedMapToNbt(map: Map[_, _]): NBTBase = { + def typedMapToNbt(map: Map[_, _]): INBT = { def mapToList(value: Array[(_, _)]) = value.collect { // Ignore, can be stuff like the 'n' introduced by Lua's `pack`. case (k: Number, v) => k -> v @@ -93,64 +93,64 @@ object ExtendedNBT { val nbtValue = typeAndValue.get("value") nbtType match { case Some(n: Number) => n.intValue() match { - case NBT.TAG_BYTE => new NBTTagByte(nbtValue match { + case NBT.TAG_BYTE => ByteNBT.valueOf(nbtValue match { case Some(v: Number) => v.byteValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_SHORT => new NBTTagShort(nbtValue match { + case NBT.TAG_SHORT => ShortNBT.valueOf(nbtValue match { case Some(v: Number) => v.shortValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_INT => new NBTTagInt(nbtValue match { + case NBT.TAG_INT => IntNBT.valueOf(nbtValue match { case Some(v: Number) => v.intValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_LONG => new NBTTagLong(nbtValue match { + case NBT.TAG_LONG => LongNBT.valueOf(nbtValue match { case Some(v: Number) => v.longValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_FLOAT => new NBTTagFloat(nbtValue match { + case NBT.TAG_FLOAT => FloatNBT.valueOf(nbtValue match { case Some(v: Number) => v.floatValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_DOUBLE => new NBTTagDouble(nbtValue match { + case NBT.TAG_DOUBLE => DoubleNBT.valueOf(nbtValue match { case Some(v: Number) => v.doubleValue() case _ => throw new IllegalArgumentException("Illegal or missing value.") }) - case NBT.TAG_BYTE_ARRAY => new NBTTagByteArray(asList(nbtValue).map { + case NBT.TAG_BYTE_ARRAY => new ByteArrayNBT(asList(nbtValue).map { case n: Number => n.byteValue() case _ => throw new IllegalArgumentException("Illegal value.") }.toArray) - case NBT.TAG_STRING => new NBTTagString(nbtValue match { + case NBT.TAG_STRING => StringNBT.valueOf(nbtValue match { case Some(v: String) => v case Some(v: Array[Byte]) => new String(v, Charsets.UTF_8) case _ => throw new IllegalArgumentException("Illegal or missing value.") }) case NBT.TAG_LIST => - val list = new NBTTagList() - asList(nbtValue).map(v => asMap(Option(v))).foreach(v => list.appendTag(typedMapToNbt(v))) + val list = new ListNBT() + asList(nbtValue).map(v => asMap(Option(v))).foreach(v => list.add(typedMapToNbt(v))) list case NBT.TAG_COMPOUND => - val nbt = new NBTTagCompound() + val nbt = new CompoundNBT() val values = asMap[String](nbtValue) for ((name, entry) <- values) { - try nbt.setTag(name, typedMapToNbt(asMap[Any](Option(entry)))) catch { + try nbt.put(name, typedMapToNbt(asMap[Any](Option(entry)))) catch { case t: Throwable => throw new IllegalArgumentException(s"Error converting entry '$name': ${t.getMessage}") } } nbt case NBT.TAG_INT_ARRAY => - new NBTTagIntArray(asList(nbtValue).map { + new IntArrayNBT(asList(nbtValue).map { case n: Number => n.intValue() case _ => throw new IllegalArgumentException() }.toArray) @@ -162,123 +162,123 @@ object ExtendedNBT { } } - implicit def booleanIterableToNbt(value: Iterable[Boolean]): Iterable[NBTTagByte] = value.map(toNbt) + implicit def booleanIterableToNbt(value: Iterable[Boolean]): Iterable[ByteNBT] = value.map(toNbt) - implicit def byteIterableToNbt(value: Iterable[Byte]): Iterable[NBTTagByte] = value.map(toNbt) + implicit def byteIterableToNbt(value: Iterable[Byte]): Iterable[ByteNBT] = value.map(toNbt) - implicit def shortIterableToNbt(value: Iterable[Short]): Iterable[NBTTagShort] = value.map(toNbt) + implicit def shortIterableToNbt(value: Iterable[Short]): Iterable[ShortNBT] = value.map(toNbt) - implicit def intIterableToNbt(value: Iterable[Int]): Iterable[NBTTagInt] = value.map(toNbt) + implicit def intIterableToNbt(value: Iterable[Int]): Iterable[IntNBT] = value.map(toNbt) - implicit def intArrayIterableToNbt(value: Iterable[Array[Int]]): Iterable[NBTTagIntArray] = value.map(toNbt) + implicit def intArrayIterableToNbt(value: Iterable[Array[Int]]): Iterable[IntArrayNBT] = value.map(toNbt) - implicit def longIterableToNbt(value: Iterable[Long]): Iterable[NBTTagLong] = value.map(toNbt) + implicit def longIterableToNbt(value: Iterable[Long]): Iterable[LongNBT] = value.map(toNbt) - implicit def floatIterableToNbt(value: Iterable[Float]): Iterable[NBTTagFloat] = value.map(toNbt) + implicit def floatIterableToNbt(value: Iterable[Float]): Iterable[FloatNBT] = value.map(toNbt) - implicit def doubleIterableToNbt(value: Iterable[Double]): Iterable[NBTTagDouble] = value.map(toNbt) + implicit def doubleIterableToNbt(value: Iterable[Double]): Iterable[DoubleNBT] = value.map(toNbt) - implicit def byteArrayIterableToNbt(value: Iterable[Array[Byte]]): Iterable[NBTTagByteArray] = value.map(toNbt) + implicit def byteArrayIterableToNbt(value: Iterable[Array[Byte]]): Iterable[ByteArrayNBT] = value.map(toNbt) - implicit def stringIterableToNbt(value: Iterable[String]): Iterable[NBTTagString] = value.map(toNbt) + implicit def stringIterableToNbt(value: Iterable[String]): Iterable[StringNBT] = value.map(toNbt) - implicit def writableIterableToNbt(value: Iterable[NBTTagCompound => Unit]): Iterable[NBTTagCompound] = value.map(toNbt) + implicit def writableIterableToNbt(value: Iterable[CompoundNBT => Unit]): Iterable[CompoundNBT] = value.map(toNbt) - implicit def itemStackIterableToNbt(value: Iterable[ItemStack]): Iterable[NBTTagCompound] = value.map(toNbt) + implicit def itemStackIterableToNbt(value: Iterable[ItemStack]): Iterable[CompoundNBT] = value.map(toNbt) - implicit def extendNBTBase(nbt: NBTBase): ExtendedNBTBase = new ExtendedNBTBase(nbt) + implicit def extendINBT(nbt: INBT): ExtendedINBT = new ExtendedINBT(nbt) - implicit def extendNBTTagCompound(nbt: NBTTagCompound): ExtendedNBTTagCompound = new ExtendedNBTTagCompound(nbt) + implicit def extendCompoundNBT(nbt: CompoundNBT): ExtendedCompoundNBT = new ExtendedCompoundNBT(nbt) - implicit def extendNBTTagList(nbt: NBTTagList): ExtendedNBTTagList = new ExtendedNBTTagList(nbt) + implicit def extendListNBT(nbt: ListNBT): ExtendedListNBT = new ExtendedListNBT(nbt) - class ExtendedNBTBase(val nbt: NBTBase) { + class ExtendedINBT(val nbt: INBT) { def toTypedMap: Map[String, _] = Map("type" -> nbt.getId, "value" -> (nbt match { - case tag: NBTTagByte => tag.getByte - case tag: NBTTagShort => tag.getShort - case tag: NBTTagInt => tag.getInt - case tag: NBTTagLong => tag.getLong - case tag: NBTTagFloat => tag.getFloat - case tag: NBTTagDouble => tag.getDouble - case tag: NBTTagByteArray => tag.getByteArray - case tag: NBTTagString => tag.getString - case tag: NBTTagList => tag.map((entry: NBTBase) => entry.toTypedMap) - case tag: NBTTagCompound => tag.getKeySet.collect { - case key: String => key -> tag.getTag(key).toTypedMap + case tag: ByteNBT => tag.getAsByte + case tag: ShortNBT => tag.getAsShort + case tag: IntNBT => tag.getAsInt + case tag: LongNBT => tag.getAsLong + case tag: FloatNBT => tag.getAsFloat + case tag: DoubleNBT => tag.getAsDouble + case tag: ByteArrayNBT => tag.getAsByteArray + case tag: StringNBT => tag.getAsString + case tag: ListNBT => tag.map((entry: INBT) => entry.toTypedMap) + case tag: CompoundNBT => tag.getAllKeys.collect { + case key: String => key -> tag.get(key).toTypedMap }.toMap - case tag: NBTTagIntArray => tag.getIntArray + case tag: IntArrayNBT => tag.getAsIntArray case _ => throw new IllegalArgumentException() })) } - class ExtendedNBTTagCompound(val nbt: NBTTagCompound) { - def setNewCompoundTag(name: String, f: (NBTTagCompound) => Any) = { - val t = new NBTTagCompound() + class ExtendedCompoundNBT(val nbt: CompoundNBT) { + def setNewCompoundTag(name: String, f: (CompoundNBT) => Any) = { + val t = new CompoundNBT() f(t) - nbt.setTag(name, t) + nbt.put(name, t) nbt } - def setNewTagList(name: String, values: Iterable[NBTBase]) = { - val t = new NBTTagList() + def setNewTagList(name: String, values: Iterable[INBT]) = { + val t = new ListNBT() t.append(values) - nbt.setTag(name, t) + nbt.put(name, t) nbt } - def setNewTagList(name: String, values: NBTBase*): NBTTagCompound = setNewTagList(name, values) + def setNewTagList(name: String, values: INBT*): CompoundNBT = setNewTagList(name, values) def getDirection(name: String) = { nbt.getByte(name) match { - case id if id < 0 || id > EnumFacing.values.length => None - case id => Option(EnumFacing.getFront(id)) + case id if id < 0 || id > Direction.values.length => None + case id => Option(Direction.from3DDataValue(id)) } } - def setDirection(name: String, d: Option[EnumFacing]): Unit = { + def setDirection(name: String, d: Option[Direction]): Unit = { d match { - case Some(side) => nbt.setByte(name, side.ordinal.toByte) - case _ => nbt.setByte(name, -1: Byte) + case Some(side) => nbt.putByte(name, side.ordinal.toByte) + case _ => nbt.putByte(name, -1: Byte) } } def getBooleanArray(name: String) = nbt.getByteArray(name).map(_ == 1) - def setBooleanArray(name: String, value: Array[Boolean]) = nbt.setTag(name, toNbt(value)) + def setBooleanArray(name: String, value: Array[Boolean]) = nbt.put(name, toNbt(value)) } - class ExtendedNBTTagList(val nbt: NBTTagList) { - def appendNewCompoundTag(f: (NBTTagCompound) => Unit) { - val t = new NBTTagCompound() + class ExtendedListNBT(val nbt: ListNBT) { + def appendNewCompoundTag(f: (CompoundNBT) => Unit) { + val t = new CompoundNBT() f(t) - nbt.appendTag(t) + nbt.add(t) } - def append(values: Iterable[NBTBase]) { + def append(values: Iterable[INBT]) { for (value <- values) { - nbt.appendTag(value) + nbt.add(value) } } - def append(values: NBTBase*): Unit = append(values) + def append(values: INBT*): Unit = append(values) - def foreach[Tag <: NBTBase](f: Tag => Unit) { - val iterable = nbt.copy(): NBTTagList - while (iterable.tagCount > 0) { - f(iterable.removeTag(0).asInstanceOf[Tag]) + def foreach[Tag <: INBT](f: Tag => Unit) { + val iterable = nbt.copy(): ListNBT + while (iterable.size > 0) { + f((iterable.remove(0): INBT).asInstanceOf[Tag]) } } - def map[Tag <: NBTBase, Value](f: Tag => Value): IndexedSeq[Value] = { - val iterable = nbt.copy(): NBTTagList + def map[Tag <: INBT, Value](f: Tag => Value): IndexedSeq[Value] = { + val iterable = nbt.copy(): ListNBT val buffer = mutable.ArrayBuffer.empty[Value] - while (iterable.tagCount > 0) { - buffer += f(iterable.removeTag(0).asInstanceOf[Tag]) + while (iterable.size > 0) { + buffer += f((iterable.remove(0): INBT).asInstanceOf[Tag]) } buffer } - def toArray[Tag: ClassTag] = map((t: Tag) => t).toArray + def toTagArray[Tag: ClassTag] = map((t: Tag) => t).toArray } } diff --git a/src/main/scala/li/cil/oc/util/ExtendedWorld.scala b/src/main/scala/li/cil/oc/util/ExtendedWorld.scala index 01ce67ebe1..688537c95c 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedWorld.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedWorld.scala @@ -2,82 +2,97 @@ package li.cil.oc.util import li.cil.oc.api.network.EnvironmentHost import net.minecraft.block.Block -import net.minecraft.block.state.IBlockState -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.block.Blocks +import net.minecraft.block.BlockState +import net.minecraft.block.material.Material +import net.minecraft.entity.player.PlayerEntity import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockAccess +import net.minecraft.world.IBlockReader import net.minecraft.world.World import scala.language.implicitConversions object ExtendedWorld { - implicit def extendedBlockAccess(world: IBlockAccess): ExtendedBlockAccess = new ExtendedBlockAccess(world) + implicit def extendedBlockAccess(world: IBlockReader): ExtendedBlockAccess = new ExtendedBlockAccess(world) implicit def extendedWorld(world: World): ExtendedWorld = new ExtendedWorld(world) - class ExtendedBlockAccess(val world: IBlockAccess) { + class ExtendedBlockAccess(val world: IBlockReader) { def getBlock(position: BlockPosition) = world.getBlockState(position.toBlockPos).getBlock def getBlockMapColor(position: BlockPosition) = getBlockMetadata(position).getMapColor(world, position.toBlockPos) def getBlockMetadata(position: BlockPosition) = world.getBlockState(position.toBlockPos) - def getTileEntity(position: BlockPosition): TileEntity = world.getTileEntity(position.toBlockPos) + def getBlockEntity(position: BlockPosition): TileEntity = world.getBlockEntity(position.toBlockPos) - def getTileEntity(host: EnvironmentHost): TileEntity = getTileEntity(BlockPosition(host)) + def getBlockEntity(host: EnvironmentHost): TileEntity = getBlockEntity(BlockPosition(host)) - def isAirBlock(position: BlockPosition) = world.isAirBlock(position.toBlockPos) - - def getLightBrightnessForSkyBlocks(position: BlockPosition, minBrightness: Int) = world.getCombinedLight(position.toBlockPos, minBrightness) + def isAirBlock(position: BlockPosition) = { + val state = world.getBlockState(position.toBlockPos) + state.getBlock.isAir(state, world, position.toBlockPos) + } } class ExtendedWorld(override val world: World) extends ExtendedBlockAccess(world) { - def blockExists(position: BlockPosition) = world.isBlockLoaded(position.toBlockPos) + def blockExists(position: BlockPosition) = world.isLoaded(position.toBlockPos) def breakBlock(position: BlockPosition, drops: Boolean = true) = world.destroyBlock(position.toBlockPos, drops) - def destroyBlockInWorldPartially(entityId: Int, position: BlockPosition, progress: Int) = world.sendBlockBreakProgress(entityId, position.toBlockPos, progress) + def destroyBlockInWorldPartially(entityId: Int, position: BlockPosition, progress: Int) = world.destroyBlockProgress(entityId, position.toBlockPos, progress) - def extinguishFire(player: EntityPlayer, position: BlockPosition, side: EnumFacing) = world.extinguishFire(player, position.toBlockPos, side) + def extinguishFire(player: PlayerEntity, position: BlockPosition, side: Direction) = { + val pos = position.toBlockPos + val state = world.getBlockState(pos) + if (state.getMaterial == Material.FIRE) { + world.setBlock(pos, Blocks.AIR.defaultBlockState, 3) + true + } + else false + } - def getBlockHardness(position: BlockPosition) = getBlock(position).getBlockHardness(world.getBlockState(position.toBlockPos), world, position.toBlockPos) + def getBlockHardness(position: BlockPosition) = world.getBlockState(position.toBlockPos).getDestroySpeed(world, position.toBlockPos) def getBlockHarvestLevel(position: BlockPosition) = getBlock(position).getHarvestLevel(getBlockMetadata(position)) def getBlockHarvestTool(position: BlockPosition) = getBlock(position).getHarvestTool(getBlockMetadata(position)) - def computeRedstoneSignal(position: BlockPosition, side: EnumFacing) = math.max(world.isBlockProvidingPowerTo(position.offset(side), side), world.getIndirectPowerLevelTo(position.offset(side), side)) + def computeRedstoneSignal(position: BlockPosition, side: Direction) = math.max(world.isBlockProvidingPowerTo(position.offset(side), side), world.getIndirectPowerLevelTo(position.offset(side), side)) - def isBlockProvidingPowerTo(position: BlockPosition, side: EnumFacing) = world.getStrongPower(position.toBlockPos, side) + def isBlockProvidingPowerTo(position: BlockPosition, side: Direction) = world.getDirectSignal(position.toBlockPos, side) - def getIndirectPowerLevelTo(position: BlockPosition, side: EnumFacing) = world.getRedstonePower(position.toBlockPos, side) + def getIndirectPowerLevelTo(position: BlockPosition, side: Direction) = world.getSignal(position.toBlockPos, side) - def notifyBlockUpdate(pos: BlockPos): Unit = world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3) + def notifyBlockUpdate(pos: BlockPos): Unit = world.sendBlockUpdated(pos, world.getBlockState(pos), world.getBlockState(pos), 3) - def notifyBlockUpdate(position: BlockPosition): Unit = notifyBlockUpdate(position, world.getBlockState(position.toBlockPos), world.getBlockState(position.toBlockPos)) + def notifyBlockUpdate(position: BlockPosition): Unit = world.sendBlockUpdated(position.toBlockPos, world.getBlockState(position.toBlockPos), world.getBlockState(position.toBlockPos), 3) - def notifyBlockUpdate(position: BlockPosition, oldState: IBlockState, newState: IBlockState, flags: Int = 3): Unit = world.notifyBlockUpdate(position.toBlockPos, oldState, newState, flags) + def notifyBlockUpdate(position: BlockPosition, oldState: BlockState, newState: BlockState, flags: Int = 3): Unit = world.sendBlockUpdated(position.toBlockPos, oldState, newState, flags) def notifyBlockOfNeighborChange(position: BlockPosition, block: Block) = world.neighborChanged(position.toBlockPos, block, position.toBlockPos) - def notifyBlocksOfNeighborChange(position: BlockPosition, block: Block, updateObservers: Boolean) = world.notifyNeighborsOfStateChange(position.toBlockPos, block, updateObservers) - - def notifyBlocksOfNeighborChange(position: BlockPosition, block: Block, side: EnumFacing) = world.notifyNeighborsOfStateExcept(position.toBlockPos, block, side) + @Deprecated + def notifyBlocksOfNeighborChange(position: BlockPosition, block: Block, updateObservers: Boolean) = world.updateNeighborsAt(position.toBlockPos, block) - def playAuxSFX(id: Int, position: BlockPosition, data: Int) = world.playEvent(id, position.toBlockPos, data) + def notifyBlocksOfNeighborChange(position: BlockPosition, block: Block, side: Direction) = world.updateNeighborsAtExceptFromFacing(position.toBlockPos, block, side) - def setBlock(position: BlockPosition, block: Block) = world.setBlockState(position.toBlockPos, block.getDefaultState) + def playAuxSFX(id: Int, position: BlockPosition, data: Int) = world.levelEvent(id, position.toBlockPos, data) - def setBlock(position: BlockPosition, block: Block, metadata: Int, flag: Int) = world.setBlockState(position.toBlockPos, block.getStateFromMeta(metadata), flag) + def setBlock(position: BlockPosition, block: Block) = world.setBlockAndUpdate(position.toBlockPos, block.defaultBlockState) - def setBlockToAir(position: BlockPosition) = world.setBlockToAir(position.toBlockPos) + @Deprecated + def setBlock(position: BlockPosition, block: Block, metadata: Int, flag: Int) = { + val states = block.getStateDefinition.getPossibleStates + val state = if (metadata >= 0 && metadata < states.size) states.get(metadata) else block.defaultBlockState + world.setBlock(position.toBlockPos, state, flag) + } - def isSideSolid(position: BlockPosition, side: EnumFacing) = world.isSideSolid(position.toBlockPos, side) + def setBlockToAir(position: BlockPosition) = world.setBlockAndUpdate(position.toBlockPos, Blocks.AIR.defaultBlockState) - def isBlockLoaded(position: BlockPosition) = world.isBlockLoaded(position.toBlockPos) + def isLoaded(position: BlockPosition) = world.isLoaded(position.toBlockPos) } } diff --git a/src/main/scala/li/cil/oc/util/FluidUtils.scala b/src/main/scala/li/cil/oc/util/FluidUtils.scala index 2b50bf24e9..1b42e1a498 100644 --- a/src/main/scala/li/cil/oc/util/FluidUtils.scala +++ b/src/main/scala/li/cil/oc/util/FluidUtils.scala @@ -3,23 +3,19 @@ package li.cil.oc.util import li.cil.oc.util.ExtendedBlock._ import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block -import net.minecraft.block.BlockDynamicLiquid -import net.minecraft.block.BlockLiquid -import net.minecraft.block.BlockStaticLiquid -import net.minecraft.init.Blocks +import net.minecraft.block.FlowingFluidBlock +import net.minecraft.block.Blocks +import net.minecraft.fluid.Fluid import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing -import net.minecraftforge.fluids.Fluid -import net.minecraftforge.fluids.FluidRegistry +import net.minecraft.util.Direction +import net.minecraftforge.fluids.FluidAttributes import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.IFluidBlock -import net.minecraftforge.fluids.capability import net.minecraftforge.fluids.capability.CapabilityFluidHandler -import net.minecraftforge.fluids.capability.FluidTankProperties import net.minecraftforge.fluids.capability.IFluidHandler +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction import net.minecraftforge.fluids.capability.IFluidHandlerItem -import net.minecraftforge.fluids.capability.IFluidTankProperties object FluidUtils { /** @@ -27,12 +23,12 @@ object FluidUtils { *

    * This performs special handling for in-world liquids. */ - def fluidHandlerAt(position: BlockPosition, side: EnumFacing): Option[IFluidHandler] = position.world match { - case Some(world) if world.blockExists(position) => world.getTileEntity(position) match { + def fluidHandlerAt(position: BlockPosition, side: Direction): Option[IFluidHandler] = position.world match { + case Some(world) if world.blockExists(position) => world.getBlockEntity(position) match { case handler: IFluidHandler => Option(handler) - case t: TileEntity if t.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side) => - t.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side) match { - case handler: capability.IFluidHandler => Option(handler) + case t: TileEntity if t.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side).isPresent => + t.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side).orElse(null) match { + case handler: IFluidHandler => Option(handler) case _ => Option(new GenericBlockWrapper(position)) } case _ => Option(new GenericBlockWrapper(position)) @@ -41,8 +37,7 @@ object FluidUtils { } def fluidHandlerOf(stack: ItemStack): IFluidHandlerItem = Option(stack) match { - case Some(itemStack) if itemStack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null) => - itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null) + case Some(itemStack) => itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null).orElse(null) case _ => null } @@ -55,13 +50,13 @@ object FluidUtils { *

    * This returns true if some fluid was transferred. */ - def transferBetweenFluidHandlers(source: IFluidHandler, sink: IFluidHandler, limit: Int = Fluid.BUCKET_VOLUME): Int = { - val drained = source.drain(limit, false) + def transferBetweenFluidHandlers(source: IFluidHandler, sink: IFluidHandler, limit: Int = FluidAttributes.BUCKET_VOLUME): Int = { + val drained = source.drain(limit, FluidAction.SIMULATE) if (drained == null) { return 0 } - val filled = sink.fill(drained, false) - sink.fill(source.drain(filled, true), true) + val filled = sink.fill(drained, FluidAction.SIMULATE) + sink.fill(source.drain(filled, FluidAction.EXECUTE), FluidAction.EXECUTE) } /** @@ -71,39 +66,41 @@ object FluidUtils { * This uses the fluidHandlerAt method, and therefore handles special * cases such as fluid blocks. */ - def transferBetweenFluidHandlersAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sinkPos: BlockPosition, sinkSide: EnumFacing, limit: Int = Fluid.BUCKET_VOLUME): Int = + def transferBetweenFluidHandlersAt(sourcePos: BlockPosition, sourceSide: Direction, sinkPos: BlockPosition, sinkSide: Direction, limit: Int = FluidAttributes.BUCKET_VOLUME): Int = fluidHandlerAt(sourcePos, sourceSide).fold(0)(source => fluidHandlerAt(sinkPos, sinkSide).fold(0)(sink => transferBetweenFluidHandlers(source, sink, limit))) /** * Lookup fluid taking into account flowing liquid blocks... + * For legacy reasons, returns null when the block is not a fluid, not Fluids.EMPTY. */ - def lookupFluidForBlock(block: Block): Fluid = { - if (block == Blocks.FLOWING_LAVA) FluidRegistry.LAVA - else if (block == Blocks.FLOWING_WATER) FluidRegistry.WATER - else FluidRegistry.lookupFluidForBlock(block) + @Deprecated + def lookupFluidForBlock(block: Block): Fluid = block match { + case fluid: FlowingFluidBlock => fluid.getFluid + case _ => null } // ----------------------------------------------------------------------- // private class GenericBlockWrapper(position: BlockPosition) extends IFluidHandler { - def canDrain(fluid: Fluid): Boolean = currentWrapper.fold(false)(_.drain(new FluidStack(fluid, 1), false).amount > 0) + override def getTanks = currentWrapper.fold(0)(_.getTanks) + + override def getFluidInTank(tank: Int) = currentWrapper.fold(FluidStack.EMPTY)(_.getFluidInTank(tank)) - override def drain(resource: FluidStack, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(resource, doDrain)) + override def getTankCapacity(tank: Int) = currentWrapper.fold(0)(_.getTankCapacity(tank)) - override def drain(maxDrain: Int, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(maxDrain, doDrain)) + override def isFluidValid(tank: Int, fluid: FluidStack): Boolean = currentWrapper.fold(false)(_.isFluidValid(tank, fluid)) - def canFill(fluid: Fluid): Boolean = currentWrapper.fold(false)(_.fill(new FluidStack(fluid, 1), false) > 0) + override def drain(resource: FluidStack, action: FluidAction): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(resource, action)) - override def fill(resource: FluidStack, doFill: Boolean): Int = currentWrapper.fold(0)(_.fill(resource, doFill)) + override def drain(maxDrain: Int, action: FluidAction): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(maxDrain, action)) - override def getTankProperties: Array[IFluidTankProperties] = currentWrapper.fold(Array.empty[IFluidTankProperties])(_.getTankProperties) + override def fill(resource: FluidStack, action: FluidAction): Int = currentWrapper.fold(0)(_.fill(resource, action)) def currentWrapper: Option[IFluidHandler] = if (position.world.get.blockExists(position)) position.world.get.getBlock(position) match { case block: IFluidBlock => Option(new FluidBlockWrapper(position, block)) - case block: BlockStaticLiquid if lookupFluidForBlock(block) != null && isFullLiquidBlock => Option(new LiquidBlockWrapper(position, block)) - case block: BlockDynamicLiquid if lookupFluidForBlock(block) != null && isFullLiquidBlock => Option(new LiquidBlockWrapper(position, block)) + case block: FlowingFluidBlock if lookupFluidForBlock(block) != null && isFullLiquidBlock => Option(new LiquidBlockWrapper(position, block)) case block: Block if block.isAir(position) || block.isReplaceable(position) => Option(new AirBlockWrapper(position, block)) case _ => None } @@ -111,84 +108,92 @@ object FluidUtils { def isFullLiquidBlock: Boolean = { val state = position.world.get.getBlockState(position.toBlockPos) - state.getValue(BlockLiquid.LEVEL) == 0 + state.getValue(FlowingFluidBlock.LEVEL) == 0 } } private trait BlockWrapperBase extends IFluidHandler { - protected def uncheckedDrain(doDrain: Boolean): FluidStack + override def getTanks = 1 + + override def getTankCapacity(tank: Int) = FluidAttributes.BUCKET_VOLUME + + protected def uncheckedDrain(action: FluidAction): FluidStack - override def drain(resource: FluidStack, doDrain: Boolean): FluidStack = { - val drained = uncheckedDrain(false) - if (drained != null && (resource == null || (drained.getFluid == resource.getFluid && drained.amount <= resource.amount))) { - uncheckedDrain(doDrain) + override def drain(resource: FluidStack, action: FluidAction): FluidStack = { + val drained = uncheckedDrain(FluidAction.SIMULATE) + if (drained != null && (resource == null || (drained.getFluid == resource.getFluid && drained.getAmount <= resource.getAmount))) { + uncheckedDrain(action) } else null } - override def drain(maxDrain: Int, doDrain: Boolean): FluidStack = { - val drained = uncheckedDrain(false) - if (drained != null && drained.amount <= maxDrain) { - uncheckedDrain(doDrain) + override def drain(maxDrain: Int, action: FluidAction): FluidStack = { + val drained = uncheckedDrain(FluidAction.SIMULATE) + if (drained != null && drained.getAmount <= maxDrain) { + uncheckedDrain(action) } else null } - def canFill(fluid: Fluid): Boolean = false - - override def fill(resource: FluidStack, doFill: Boolean): Int = 0 + override def fill(resource: FluidStack, action: FluidAction): Int = 0 } + @Deprecated private class FluidBlockWrapper(val position: BlockPosition, val block: IFluidBlock) extends BlockWrapperBase { - final val AssumedCapacity = Fluid.BUCKET_VOLUME - - def canDrain(fluid: Fluid): Boolean = block.canDrain(position) + override def getFluidInTank(tank: Int) = block.drain(position, FluidAction.SIMULATE) - override def getTankProperties: Array[IFluidTankProperties] = Array(new FluidTankProperties(new FluidStack(block.getFluid, (block.getFilledPercentage(position) * AssumedCapacity).toInt), AssumedCapacity)) + override def isFluidValid(tank: Int, fluid: FluidStack): Boolean = block.getFluid.isSame(fluid.getFluid) && block.canDrain(position) - override protected def uncheckedDrain(doDrain: Boolean): FluidStack = block.drain(position, doDrain) + override protected def uncheckedDrain(action: FluidAction): FluidStack = block.drain(position, action) } - private class LiquidBlockWrapper(val position: BlockPosition, val block: BlockLiquid) extends BlockWrapperBase { + private class LiquidBlockWrapper(val position: BlockPosition, val block: FlowingFluidBlock) extends BlockWrapperBase { val fluid: Fluid = lookupFluidForBlock(block) - def canDrain(fluid: Fluid): Boolean = true + override def getFluidInTank(tank: Int) = if (isFullLiquidBlock) new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME) else FluidStack.EMPTY - override def getTankProperties: Array[IFluidTankProperties] = Array(new FluidTankProperties(new FluidStack(fluid, Fluid.BUCKET_VOLUME), Fluid.BUCKET_VOLUME)) + override def isFluidValid(tank: Int, fluid: FluidStack): Boolean = block.getFluid.isSame(fluid.getFluid) - override protected def uncheckedDrain(doDrain: Boolean): FluidStack = { - if (doDrain) { + override protected def uncheckedDrain(action: FluidAction): FluidStack = { + if (action.execute) { position.world.get.setBlockToAir(position) } - new FluidStack(fluid, Fluid.BUCKET_VOLUME) + if (isFullLiquidBlock) new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME) else FluidStack.EMPTY + } + + def isFullLiquidBlock: Boolean = { + val state = position.world.get.getBlockState(position.toBlockPos) + state.getValue(FlowingFluidBlock.LEVEL) == 0 } } private class AirBlockWrapper(val position: BlockPosition, val block: Block) extends IFluidHandler { - def canDrain(fluid: Fluid): Boolean = false + override def getTanks = 1 - override def drain(resource: FluidStack, doDrain: Boolean): FluidStack = null + override def getTankCapacity(tank: Int) = FluidAttributes.BUCKET_VOLUME - override def drain(maxDrain: Int, doDrain: Boolean): FluidStack = null + override def getFluidInTank(tank: Int) = FluidStack.EMPTY - def canFill(fluid: Fluid): Boolean = fluid.canBePlacedInWorld + override def drain(resource: FluidStack, action: FluidAction): FluidStack = FluidStack.EMPTY - override def fill(resource: FluidStack, doFill: Boolean): Int = { - if (resource != null && resource.getFluid.canBePlacedInWorld && resource.getFluid.getBlock != null && resource.amount >= 1000) { - if (doFill) { + override def drain(maxDrain: Int, action: FluidAction): FluidStack = FluidStack.EMPTY + + override def isFluidValid(tank: Int, fluid: FluidStack): Boolean = fluid.getFluid.defaultFluidState.createLegacyBlock != null + + override def fill(resource: FluidStack, action: FluidAction): Int = { + if (resource != null && resource.getFluid.defaultFluidState.createLegacyBlock != null && resource.getAmount >= FluidAttributes.BUCKET_VOLUME) { + if (action.execute) { val world = position.world.get if (!world.isAirBlock(position) && !world.containsAnyLiquid(position.bounds)) world.breakBlock(position) - world.setBlock(position, resource.getFluid.getBlock) + world.setBlockAndUpdate(position.toBlockPos, resource.getFluid.defaultFluidState.createLegacyBlock) // This fake neighbor update is required to get stills to start flowing. world.notifyBlockOfNeighborChange(position, world.getBlock(position)) } - Fluid.BUCKET_VOLUME + FluidAttributes.BUCKET_VOLUME } else 0 } - - override def getTankProperties: Array[IFluidTankProperties] = Array.empty } } diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 6b9595a134..79bb9928b5 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -1,16 +1,19 @@ package li.cil.oc.util +import java.util.Optional + import li.cil.oc.OpenComputers import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.StackOption._ import net.minecraft.entity.Entity -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction +import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.items.CapabilityItemHandler import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandlerModifiable @@ -21,7 +24,7 @@ import scala.collection.convert.WrapAsScala._ object InventoryUtils { - def asItemHandler(inventory: IInventory, side: EnumFacing): IItemHandlerModifiable = inventory match { + def asItemHandler(inventory: IInventory, side: Direction): IItemHandlerModifiable = inventory match { case inv: ISidedInventory if side != null => new SidedInvWrapper(inv, side) case _ => new InvWrapper(inventory) } @@ -36,8 +39,10 @@ object InventoryUtils { def haveSameItemType(stackA: ItemStack, stackB: ItemStack, checkNBT: Boolean = false): Boolean = !stackA.isEmpty && !stackB.isEmpty && stackA.getItem == stackB.getItem && - (!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage) && - (!checkNBT || ItemStack.areItemStackTagsEqual(stackA, stackB)) + (stackA.getDamageValue == stackB.getDamageValue) && + (!checkNBT || ItemStack.tagMatches(stackA, stackB)) + + private def optionToScala[T](opt: Optional[T]): Option[T] = if (opt.isPresent) Some(opt.get) else None /** * Retrieves an actual inventory implementation for a specified world coordinate. @@ -45,20 +50,20 @@ object InventoryUtils { * This performs special handling for (double-)chests and also checks for * mine carts with chests. */ - def inventoryAt(position: BlockPosition, side: EnumFacing): Option[IItemHandler] = position.world match { - case Some(world) if world.blockExists(position) => world.getTileEntity(position) match { - case tile: TileEntity if tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) => Option(tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) + def inventoryAt(position: BlockPosition, side: Direction): Option[IItemHandler] = position.world match { + case Some(world) if world.blockExists(position) => world.getBlockEntity(position) match { + case tile: TileEntity if tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).isPresent => optionToScala(tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).resolve) case tile: IInventory => Option(asItemHandler(tile, side)) - case _ => world.getEntitiesWithinAABB(classOf[Entity], position.bounds) - .filter(e => !e.isDead && e.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) - .map(_.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) + case _ => world.getEntitiesOfClass(classOf[Entity], position.bounds) + .filter(e => e.isAlive && e.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).isPresent) + .map(_.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).orElse(null)) .find(_ != null) } case _ => None } def anyInventoryAt(position: BlockPosition): Option[IItemHandler] = { - for(side <- null :: EnumFacing.VALUES.toList) { + for(side <- null :: Direction.values.toList) { inventoryAt(position, side) match { case inv: Some[IItemHandler] => return inv case _ => @@ -92,7 +97,7 @@ object InventoryUtils { def insertIntoInventorySlot(stack: ItemStack, inventory: IItemHandler, slot: Int, limit: Int = 64, simulate: Boolean = false): Boolean = (!stack.isEmpty && limit > 0 && stack.getCount > 0) && { val amount = stack.getCount min limit - val toInsert = stack.splitStack(amount) + val toInsert = stack.split(amount) inventory.insertItem(slot, toInsert, simulate) match { case remaining: ItemStack => val result = remaining.getCount < amount @@ -102,7 +107,7 @@ object InventoryUtils { } } - def insertIntoInventorySlot(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing], slot: Int, limit: Int, simulate: Boolean): Boolean = + def insertIntoInventorySlot(stack: ItemStack, inventory: IInventory, side: Option[Direction], slot: Int, limit: Int, simulate: Boolean): Boolean = insertIntoInventorySlot(stack, asItemHandler(inventory, side.orNull), slot, limit, simulate) /** @@ -150,7 +155,7 @@ object InventoryUtils { } } - def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, slot: Int, limit: Int): Int = + def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IInventory, side: Direction, slot: Int, limit: Int): Int = extractFromInventorySlot(consumer, asItemHandler(inventory, side), slot, limit) /** @@ -186,7 +191,7 @@ object InventoryUtils { success } - def insertIntoInventory(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing], limit: Int, simulate: Boolean, slots: Option[Iterable[Int]]): Boolean = + def insertIntoInventory(stack: ItemStack, inventory: IInventory, side: Option[Direction], limit: Int, simulate: Boolean, slots: Option[Iterable[Int]]): Boolean = insertIntoInventory(stack, asItemHandler(inventory, side.orNull), limit, simulate, slots) /** @@ -209,7 +214,7 @@ object InventoryUtils { 0 } - def extractAnyFromInventory(consumer: ItemStack => Unit, inventory: IInventory, side: EnumFacing, limit: Int): Int = + def extractAnyFromInventory(consumer: ItemStack => Unit, inventory: IInventory, side: Direction, limit: Int): Int = extractAnyFromInventory(consumer, asItemHandler(inventory, side), limit) /** @@ -238,14 +243,14 @@ object InventoryUtils { remaining } - def extractFromInventory(stack: ItemStack, inventory: IInventory, side: EnumFacing, simulate: Boolean, exact: Boolean): ItemStack = + def extractFromInventory(stack: ItemStack, inventory: IInventory, side: Direction, simulate: Boolean, exact: Boolean): ItemStack = extractFromInventory(stack, asItemHandler(inventory, side), simulate, exact) /** * Utility method for calling insertIntoInventory on an inventory * in the world. */ - def insertIntoInventoryAt(stack: ItemStack, position: BlockPosition, side: Option[EnumFacing] = None, limit: Int = 64, simulate: Boolean = false): Boolean = + def insertIntoInventoryAt(stack: ItemStack, position: BlockPosition, side: Option[Direction] = None, limit: Int = 64, simulate: Boolean = false): Boolean = inventoryAt(position, side.orNull).exists(insertIntoInventory(stack, _, limit, simulate)) type Extractor = () => Int @@ -254,7 +259,7 @@ object InventoryUtils { * Utility method for calling extractFromInventory on an inventory * in the world. */ - def getExtractorFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64): Extractor = + def getExtractorFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: Direction, limit: Int = 64): Extractor = inventoryAt(position, side) match { case Some(inventory) => () => extractAnyFromInventory(consumer, inventory, limit) case _ => null @@ -277,7 +282,7 @@ object InventoryUtils { extractAnyFromInventory( insertIntoInventory(_, sink, limit), source, limit = limit) - def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int): Int = + def transferBetweenInventories(source: IInventory, sourceSide: Direction, sink: IInventory, sinkSide: Option[Direction], limit: Int): Int = transferBetweenInventories(asItemHandler(source, sourceSide), asItemHandler(sink, sinkSide.orNull), limit) /** @@ -293,14 +298,14 @@ object InventoryUtils { insertIntoInventory(_, sink, limit), source, sourceSlot, limit = limit) } - def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int): Int = + def transferBetweenInventoriesSlots(source: IInventory, sourceSide: Direction, sourceSlot: Int, sink: IInventory, sinkSide: Option[Direction], sinkSlot: Option[Int], limit: Int): Int = transferBetweenInventoriesSlots(asItemHandler(source, sourceSide), sourceSlot, asItemHandler(sink, sinkSide.orNull), sinkSlot, limit) /** * Utility method for calling transferBetweenInventories on inventories * in the world. */ - def getTransferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64): Extractor = + def getTransferBetweenInventoriesAt(source: BlockPosition, sourceSide: Direction, sink: BlockPosition, sinkSide: Option[Direction], limit: Int = 64): Extractor = inventoryAt(source, sourceSide) match { case Some(sourceInventory) => inventoryAt(sink, sinkSide.orNull) match { @@ -314,7 +319,7 @@ object InventoryUtils { * Utility method for calling transferBetweenInventoriesSlots on inventories * in the world. */ - def getTransferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64): Extractor = + def getTransferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: Direction, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[Direction], sinkSlot: Option[Int], limit: Int = 64): Extractor = inventoryAt(sourcePos, sourceSide) match { case Some(sourceInventory) => inventoryAt(sinkPos, sinkSide.orNull) match { @@ -328,8 +333,8 @@ object InventoryUtils { * Utility method for dropping contents from a single inventory slot into * the world. */ - def dropSlot(position: BlockPosition, inventory: IInventory, slot: Int, count: Int, direction: Option[EnumFacing] = None): Boolean = { - StackOption(inventory.decrStackSize(slot, count)) match { + def dropSlot(position: BlockPosition, inventory: IInventory, slot: Int, count: Int, direction: Option[Direction] = None): Boolean = { + StackOption(inventory.removeItem(slot, count)) match { case SomeStack(stack) if stack.getCount > 0 => spawnStackInWorld(position, stack, direction); true case _ => false } @@ -339,10 +344,10 @@ object InventoryUtils { * Utility method for dumping all inventory contents into the world. */ def dropAllSlots(position: BlockPosition, inventory: IInventory): Unit = { - for (slot <- 0 until inventory.getSizeInventory) { - StackOption(inventory.getStackInSlot(slot)) match { + for (slot <- 0 until inventory.getContainerSize) { + StackOption(inventory.getItem(slot)) match { case SomeStack(stack) if stack.getCount > 0 => - inventory.setInventorySlotContents(slot, ItemStack.EMPTY) + inventory.setItem(slot, ItemStack.EMPTY) spawnStackInWorld(position, stack) case _ => // Nothing. } @@ -352,16 +357,16 @@ object InventoryUtils { /** * Try inserting an item stack into a player inventory. If that fails, drop it into the world. */ - def addToPlayerInventory(stack: ItemStack, player: EntityPlayer, spawnInWorld: Boolean = true): Unit = { + def addToPlayerInventory(stack: ItemStack, player: PlayerEntity, spawnInWorld: Boolean = true): Unit = { if (!stack.isEmpty) { - if (player.inventory.addItemStackToInventory(stack)) { - player.inventory.markDirty() - if (player.openContainer != null) { - player.openContainer.detectAndSendChanges() + if (player.inventory.add(stack)) { + player.inventory.setChanged() + if (player.containerMenu != null) { + player.containerMenu.broadcastChanges() } } if (stack.getCount > 0 && spawnInWorld) { - player.dropItem(stack, false, false) + player.drop(stack, false, false) } } } @@ -369,22 +374,23 @@ object InventoryUtils { /** * Utility method for spawning an item stack in the world. */ - def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: Option[EnumFacing] = None, validator: Option[EntityItem => Boolean] = None): EntityItem = position.world match { + def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: Option[Direction] = None, validator: Option[ItemEntity => Boolean] = None): ItemEntity = position.world match { case Some(world) if !stack.isEmpty && stack.getCount > 0 => - val rng = world.rand - val (ox, oy, oz) = direction.fold((0, 0, 0))(d => (d.getFrontOffsetX, d.getFrontOffsetY, d.getFrontOffsetZ)) + val rng = world.random + val (ox, oy, oz) = direction.fold((0, 0, 0))(d => (d.getStepX, d.getStepY, d.getStepZ)) val (tx, ty, tz) = ( 0.1 * (rng.nextDouble - 0.5) + ox * 0.65, 0.1 * (rng.nextDouble - 0.5) + oy * 0.75 + (ox + oz) * 0.25, 0.1 * (rng.nextDouble - 0.5) + oz * 0.65) val dropPos = position.offset(0.5 + tx, 0.5 + ty, 0.5 + tz) - val entity = new EntityItem(world, dropPos.x, dropPos.y, dropPos.z, stack.copy()) - entity.motionX = 0.0125 * (rng.nextDouble - 0.5) + ox * 0.03 - entity.motionY = 0.0125 * (rng.nextDouble - 0.5) + oy * 0.08 + (ox + oz) * 0.03 - entity.motionZ = 0.0125 * (rng.nextDouble - 0.5) + oz * 0.03 + val entity = new ItemEntity(world, dropPos.x, dropPos.y, dropPos.z, stack.copy()) + entity.setDeltaMovement(new Vector3d( + 0.0125 * (rng.nextDouble - 0.5) + ox * 0.03, + 0.0125 * (rng.nextDouble - 0.5) + oy * 0.08 + (ox + oz) * 0.03, + 0.0125 * (rng.nextDouble - 0.5) + oz * 0.03)) if (validator.fold(true)(_ (entity))) { - entity.setPickupDelay(15) - world.spawnEntity(entity) + entity.setPickUpDelay(15) + world.addFreshEntity(entity) entity } else null diff --git a/src/main/scala/li/cil/oc/util/ItemColorizer.scala b/src/main/scala/li/cil/oc/util/ItemColorizer.scala index 7043d29ced..6fae0051d4 100644 --- a/src/main/scala/li/cil/oc/util/ItemColorizer.scala +++ b/src/main/scala/li/cil/oc/util/ItemColorizer.scala @@ -1,7 +1,7 @@ package li.cil.oc.util import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT /** * @author asie, Vexatos @@ -10,38 +10,29 @@ object ItemColorizer { /** * Return whether the specified armor ItemStack has a color. */ - def hasColor(stack: ItemStack): Boolean = stack.hasTagCompound && stack.getTagCompound.hasKey("display") && stack.getTagCompound.getCompoundTag("display").hasKey("color") + def hasColor(stack: ItemStack): Boolean = stack.hasTag && stack.getTag.contains("display") && stack.getTag.getCompound("display").contains("color") /** * Return the color for the specified armor ItemStack. */ def getColor(stack: ItemStack): Int = { - val tag = stack.getTagCompound + val tag = stack.getTag if (tag != null) { - val displayTag = tag.getCompoundTag("display") - if (displayTag == null) -1 else if (displayTag.hasKey("color")) displayTag.getInteger("color") else -1 + val displayTag = tag.getCompound("display") + if (displayTag == null) -1 else if (displayTag.contains("color")) displayTag.getInt("color") else -1 } else -1 } def removeColor(stack: ItemStack) { - val tag = stack.getTagCompound + val tag = stack.getTag if (tag != null) { - val displayTag = tag.getCompoundTag("display") - if (displayTag.hasKey("color")) displayTag.removeTag("color") + val displayTag = tag.getCompound("display") + if (displayTag.contains("color")) displayTag.remove("color") } } def setColor(stack: ItemStack, color: Int) { - var tag = stack.getTagCompound - if (tag == null) { - tag = new NBTTagCompound - stack.setTagCompound(tag) - } - val displayTag = tag.getCompoundTag("display") - if (!tag.hasKey("display")) { - tag.setTag("display", displayTag) - } - displayTag.setInteger("color", color) + stack.getOrCreateTagElement("display").putInt("color", color) } } diff --git a/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala b/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala index 939682bdae..8b62dae8ff 100644 --- a/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala +++ b/src/main/scala/li/cil/oc/util/ItemStackWrapper.scala @@ -8,9 +8,9 @@ import net.minecraft.item.ItemStack import scala.language.implicitConversions class ItemStackWrapper(val inner: ItemStack) extends Ordered[ItemStackWrapper] { - def id = if (inner.getItem != null) Item.getIdFromItem(inner.getItem) else 0 + def id = if (inner.getItem != null) Item.getId(inner.getItem) else 0 - def damage = if (inner.getItem != null) inner.getItemDamage else 0 + def damage = if (inner.getItem != null) inner.getDamageValue else 0 override def compare(that: ItemStackWrapper) = { if (this.id == that.id) this.damage - that.damage diff --git a/src/main/scala/li/cil/oc/util/ItemUtils.scala b/src/main/scala/li/cil/oc/util/ItemUtils.scala index 80a1100ea2..527b35d00c 100644 --- a/src/main/scala/li/cil/oc/util/ItemUtils.scala +++ b/src/main/scala/li/cil/oc/util/ItemUtils.scala @@ -11,37 +11,39 @@ import li.cil.oc.api import li.cil.oc.common.Tier import net.minecraft.block.Block import net.minecraft.item.Item -import net.minecraft.item.ItemBlock -import net.minecraft.item.ItemBucket +import net.minecraft.item.BlockItem +import net.minecraft.item.BucketItem import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.CraftingManager +import net.minecraft.item.crafting.RecipeManager +import net.minecraft.item.crafting.ICraftingRecipe import net.minecraft.item.crafting.IRecipe +import net.minecraft.item.crafting.IRecipeType import net.minecraft.item.crafting.Ingredient -import net.minecraft.item.crafting.ShapedRecipes -import net.minecraft.item.crafting.ShapelessRecipes +import net.minecraft.item.crafting.ShapedRecipe +import net.minecraft.item.crafting.ShapelessRecipe +import net.minecraft.inventory.CraftingInventory import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.oredict.ShapedOreRecipe -import net.minecraftforge.oredict.ShapelessOreRecipe +import net.minecraft.nbt.CompoundNBT +import net.minecraftforge.registries.ForgeRegistries import scala.collection.convert.WrapAsScala._ import scala.collection.mutable object ItemUtils { - def getDisplayName(nbt: NBTTagCompound): Option[String] = { - if (nbt.hasKey("display")) { - val displayNbt = nbt.getCompoundTag("display") - if (displayNbt.hasKey("Name")) + def getDisplayName(nbt: CompoundNBT): Option[String] = { + if (nbt.contains("display")) { + val displayNbt = nbt.getCompound("display") + if (displayNbt.contains("Name")) return Option(displayNbt.getString("Name")) } None } - def setDisplayName(nbt: NBTTagCompound, name: String): Unit = { - if (!nbt.hasKey("display")) { - nbt.setTag("display", new NBTTagCompound()) + def setDisplayName(nbt: CompoundNBT, name: String): Unit = { + if (!nbt.contains("display")) { + nbt.put("display", new CompoundNBT()) } - nbt.getCompoundTag("display").setString("Name", name) + nbt.getCompound("display").putString("Name", name) } def caseTier(stack: ItemStack): Int = { @@ -68,46 +70,44 @@ object ItemUtils { def caseNameWithTierSuffix(name: String, tier: Int): String = name + (if (tier == Tier.Four) "creative" else (tier + 1).toString) - def loadTag(data: Array[Byte]): NBTTagCompound = { + def loadTag(data: Array[Byte]): CompoundNBT = { val bais = new ByteArrayInputStream(data) CompressedStreamTools.readCompressed(bais) } def saveStack(stack: ItemStack): Array[Byte] = { - val tag = new NBTTagCompound() - stack.writeToNBT(tag) + val tag = new CompoundNBT() + stack.save(tag) saveTag(tag) } - def saveTag(tag: NBTTagCompound): Array[Byte] = { + def saveTag(tag: CompoundNBT): Array[Byte] = { val baos = new ByteArrayOutputStream() CompressedStreamTools.writeCompressed(tag, baos) baos.toByteArray } - def getIngredients(stack: ItemStack): Array[ItemStack] = try { + def getIngredients(manager: RecipeManager, stack: ItemStack): Array[ItemStack] = try { def getFilteredInputs(inputs: Iterable[ItemStack], outputSize: Int) = (inputs.filter(input => !input.isEmpty && input.getCount / outputSize > 0 && // Strip out buckets, because those are returned when crafting, and // we have no way of returning the fluid only (and I can't be arsed // to make it output fluids into fluiducts or such, sorry). - !input.getItem.isInstanceOf[ItemBucket]).toArray, outputSize) + !input.getItem.isInstanceOf[BucketItem]).toArray, outputSize) - def getOutputSize(recipe: IRecipe) = recipe.getRecipeOutput.getCount + def getOutputSize(recipe: IRecipe[_]) = recipe.getResultItem.getCount def isInputBlacklisted(stack: ItemStack) = stack.getItem match { - case item: ItemBlock => Settings.get.disassemblerInputBlacklist.contains(Block.REGISTRY.getNameForObject(item.getBlock)) - case item: Item => Settings.get.disassemblerInputBlacklist.contains(Item.REGISTRY.getNameForObject(item)) + case item: BlockItem => Settings.get.disassemblerInputBlacklist.contains(ForgeRegistries.BLOCKS.getKey(item.getBlock)) + case item: Item => Settings.get.disassemblerInputBlacklist.contains(ForgeRegistries.ITEMS.getKey(item)) case _ => false } - val (ingredients, count) = CraftingManager.REGISTRY. - filter(recipe => !recipe.getRecipeOutput.isEmpty && recipe.getRecipeOutput.isItemEqual(stack)).collect { - case recipe: ShapedRecipes => getFilteredInputs(resolveOreDictEntries(recipe.recipeItems), getOutputSize(recipe)) - case recipe: ShapelessRecipes => getFilteredInputs(resolveOreDictEntries(recipe.recipeItems), getOutputSize(recipe)) - case recipe: ShapedOreRecipe => getFilteredInputs(resolveOreDictEntries(recipe.getIngredients), getOutputSize(recipe)) - case recipe: ShapelessOreRecipe => getFilteredInputs(resolveOreDictEntries(recipe.getIngredients), getOutputSize(recipe)) + val (ingredients, count) = manager.getAllRecipesFor[CraftingInventory, ICraftingRecipe](IRecipeType.CRAFTING). + filter(recipe => !recipe.getResultItem.isEmpty && recipe.getResultItem.sameItem(stack)).collect { + case recipe: ShapedRecipe => getFilteredInputs(resolveOreDictEntries(recipe.getIngredients), getOutputSize(recipe)) + case recipe: ShapelessRecipe => getFilteredInputs(resolveOreDictEntries(recipe.getIngredients), getOutputSize(recipe)) }.collectFirst { case (inputs, outputSize) if !inputs.exists(isInputBlacklisted) => (inputs, outputSize) } match { @@ -116,13 +116,13 @@ object ItemUtils { } // Avoid positive feedback loops. - if (ingredients.exists(ingredient => ingredient.isItemEqual(stack))) { + if (ingredients.exists(ingredient => ingredient.sameItem(stack))) { return Array.empty[ItemStack] } // Merge equal items for size division by output size. val merged = mutable.ArrayBuffer.empty[ItemStack] for (ingredient <- ingredients) { - merged.find(_.isItemEqual(ingredient)) match { + merged.find(_.sameItem(ingredient)) match { case Some(entry) => entry.grow(ingredient.getCount) case _ => merged += ingredient.copy() } @@ -148,7 +148,7 @@ object ItemUtils { private lazy val rng = new Random() private def resolveOreDictEntries[T](entries: Iterable[Ingredient]) = entries.collect { - case ing: Ingredient if ing.getMatchingStacks.nonEmpty => ing.getMatchingStacks()(rng.nextInt(ing.getMatchingStacks.length)) + case ing: Ingredient if ing.getItems.nonEmpty => ing.getItems()(rng.nextInt(ing.getItems.length)) } } diff --git a/src/main/scala/li/cil/oc/util/NbtDataStream.scala b/src/main/scala/li/cil/oc/util/NbtDataStream.scala index 29c4de9115..80110926df 100644 --- a/src/main/scala/li/cil/oc/util/NbtDataStream.scala +++ b/src/main/scala/li/cil/oc/util/NbtDataStream.scala @@ -1,10 +1,10 @@ package li.cil.oc.util -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object NbtDataStream { - def getShortArray(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { - if (!nbt.hasKey(key)) { + def getShortArray(nbt: CompoundNBT, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { + if (!nbt.contains(key)) { return false } @@ -21,8 +21,8 @@ object NbtDataStream { true } - def getIntArrayLegacy(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { - if (!nbt.hasKey(key)) { + def getIntArrayLegacy(nbt: CompoundNBT, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { + if (!nbt.contains(key)) { return false } // legacy format @@ -40,15 +40,15 @@ object NbtDataStream { true } - def setShortArray(nbt: NBTTagCompound, key: String, array: Array[Short]): Unit = { + def setShortArray(nbt: CompoundNBT, key: String, array: Array[Short]): Unit = { val rawByteWriter = new java.io.ByteArrayOutputStream() val memWriter = new java.io.DataOutputStream(rawByteWriter) array.foreach(memWriter.writeShort(_)) - nbt.setByteArray(key, rawByteWriter.toByteArray) + nbt.putByteArray(key, rawByteWriter.toByteArray) } - def getOptBoolean(nbt: NBTTagCompound, key: String, df: Boolean): Boolean = if (nbt.hasKey(key)) nbt.getBoolean(key) else df - def getOptString(nbt: NBTTagCompound, key: String, df: String): String = if (nbt.hasKey(key)) nbt.getString(key) else df - def getOptNbt(nbt: NBTTagCompound, key: String): NBTTagCompound = if (nbt.hasKey(key)) nbt.getCompoundTag(key) else new NBTTagCompound - def getOptInt(nbt: NBTTagCompound, key: String, df: Int): Int = if (nbt.hasKey(key)) nbt.getInteger(key) else df + def getOptBoolean(nbt: CompoundNBT, key: String, df: Boolean): Boolean = if (nbt.contains(key)) nbt.getBoolean(key) else df + def getOptString(nbt: CompoundNBT, key: String, df: String): String = if (nbt.contains(key)) nbt.getString(key) else df + def getOptNbt(nbt: CompoundNBT, key: String): CompoundNBT = if (nbt.contains(key)) nbt.getCompound(key) else new CompoundNBT + def getOptInt(nbt: CompoundNBT, key: String, df: Int): Int = if (nbt.contains(key)) nbt.getInt(key) else df } diff --git a/src/main/scala/li/cil/oc/util/OldScaledResolution.java b/src/main/scala/li/cil/oc/util/OldScaledResolution.java deleted file mode 100644 index 14b9cce066..0000000000 --- a/src/main/scala/li/cil/oc/util/OldScaledResolution.java +++ /dev/null @@ -1,37 +0,0 @@ -package li.cil.oc.util; - -import net.minecraft.client.Minecraft; -import net.minecraft.util.math.MathHelper; - -public class OldScaledResolution { - private final int scaledWidth; - private final int scaledHeight; - - public OldScaledResolution(Minecraft minecraft, int width, int height) { - int scaleFactor = 1; - int guiScale = minecraft.gameSettings.guiScale; - - if (guiScale == 0) { - guiScale = 1000; - } - - while (scaleFactor < guiScale && width / (scaleFactor + 1) >= 320 && height / (scaleFactor + 1) >= 240) { - ++scaleFactor; - } - - if (minecraft.isUnicode() && scaleFactor % 2 != 0 && scaleFactor != 1) { - --scaleFactor; - } - - this.scaledWidth = MathHelper.ceil((double) width / (double) scaleFactor); - this.scaledHeight = MathHelper.ceil((double) height / (double) scaleFactor); - } - - public int getScaledWidth() { - return this.scaledWidth; - } - - public int getScaledHeight() { - return this.scaledHeight; - } -} diff --git a/src/main/scala/li/cil/oc/util/PackedColor.scala b/src/main/scala/li/cil/oc/util/PackedColor.scala index 7714986153..6d532ceafe 100644 --- a/src/main/scala/li/cil/oc/util/PackedColor.scala +++ b/src/main/scala/li/cil/oc/util/PackedColor.scala @@ -3,7 +3,7 @@ package li.cil.oc.util import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.Persistable -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object PackedColor { @@ -47,9 +47,9 @@ object PackedColor { def isFromPalette(value: Int): Boolean = false - override def load(nbt: NBTTagCompound) {} + override def loadData(nbt: CompoundNBT) {} - override def save(nbt: NBTTagCompound) {} + override def saveData(nbt: CompoundNBT) {} } class SingleBitFormat(val color: Int) extends ColorFormat { @@ -104,13 +104,13 @@ object PackedColor { 0xCCCCCC, 0x336699, 0x9933CC, 0x333399, 0x663300, 0x336600, 0xFF3333, 0x000000) - override def load(nbt: NBTTagCompound) { + override def loadData(nbt: CompoundNBT) { val loaded = nbt.getIntArray("palette") Array.copy(loaded, 0, palette, 0, math.min(loaded.length, palette.length)) } - override def save(nbt: NBTTagCompound) { - nbt.setIntArray("palette", palette) + override def saveData(nbt: CompoundNBT) { + nbt.putIntArray("palette", palette) } } diff --git a/src/main/scala/li/cil/oc/util/PlayerUtils.scala b/src/main/scala/li/cil/oc/util/PlayerUtils.scala index 6f24c64dc5..c26e027545 100644 --- a/src/main/scala/li/cil/oc/util/PlayerUtils.scala +++ b/src/main/scala/li/cil/oc/util/PlayerUtils.scala @@ -1,26 +1,26 @@ package li.cil.oc.util -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.EnumParticleTypes +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.nbt.CompoundNBT +import net.minecraft.particles.IParticleData object PlayerUtils { - def persistedData(player: EntityPlayer): NBTTagCompound = { - val nbt = player.getEntityData - if (!nbt.hasKey(EntityPlayer.PERSISTED_NBT_TAG)) { - nbt.setTag(EntityPlayer.PERSISTED_NBT_TAG, new NBTTagCompound()) + def persistedData(player: PlayerEntity): CompoundNBT = { + val nbt = player.getPersistentData + if (!nbt.contains(PlayerEntity.PERSISTED_NBT_TAG)) { + nbt.put(PlayerEntity.PERSISTED_NBT_TAG, new CompoundNBT()) } - nbt.getCompoundTag(EntityPlayer.PERSISTED_NBT_TAG) + nbt.getCompound(PlayerEntity.PERSISTED_NBT_TAG) } - def spawnParticleAround(player: EntityPlayer, effectType: EnumParticleTypes, chance: Double = 1.0): Unit = { - val rng = player.getEntityWorld.rand + def spawnParticleAround(player: PlayerEntity, effectType: IParticleData, chance: Double = 1.0): Unit = { + val rng = player.level.random if (chance >= 1 || rng.nextDouble() < chance) { - val bounds = player.getEntityBoundingBox + val bounds = player.getBoundingBox val x = bounds.minX + (bounds.maxX - bounds.minX) * rng.nextDouble() * 1.5 val y = bounds.minY + (bounds.maxY - bounds.minY) * rng.nextDouble() * 0.5 val z = bounds.minZ + (bounds.maxZ - bounds.minZ) * rng.nextDouble() * 1.5 - player.getEntityWorld.spawnParticle(effectType, x, y, z, 0, 0, 0) + player.level.addParticle(effectType, x, y, z, 0, 0, 0) } } } diff --git a/src/main/scala/li/cil/oc/util/Rarity.scala b/src/main/scala/li/cil/oc/util/Rarity.scala index 428461ea64..46ee454077 100644 --- a/src/main/scala/li/cil/oc/util/Rarity.scala +++ b/src/main/scala/li/cil/oc/util/Rarity.scala @@ -1,9 +1,10 @@ package li.cil.oc.util -import net.minecraft.item.EnumRarity +import net.minecraft.item.{Rarity => _Rarity} object Rarity { - private val lookup = Array(EnumRarity.COMMON, EnumRarity.UNCOMMON, EnumRarity.RARE, EnumRarity.EPIC) + import _Rarity._ + private val lookup = Array(_Rarity.COMMON, _Rarity.UNCOMMON, _Rarity.RARE, _Rarity.EPIC) def byTier(tier: Int) = lookup(tier max 0 min (lookup.length - 1)) } diff --git a/src/main/scala/li/cil/oc/util/RenderState.scala b/src/main/scala/li/cil/oc/util/RenderState.scala index 03d64058fc..65be1dbe65 100644 --- a/src/main/scala/li/cil/oc/util/RenderState.scala +++ b/src/main/scala/li/cil/oc/util/RenderState.scala @@ -1,16 +1,14 @@ package li.cil.oc.util +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.OpenComputers import li.cil.oc.Settings import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.GlStateManager.CullFace import net.minecraft.client.renderer.RenderHelper import org.lwjgl.opengl._ -import org.lwjgl.util.glu.GLU -// This class has evolved into a wrapper for GlStateManager that basically does -// nothing but call the corresponding GlStateManager methods and then also +// This class has evolved into a wrapper for RenderSystem that basically does +// nothing but call the corresponding RenderSystem methods and then also // forcefully applies whatever that call *should* do. This way the state // manager's internal state is kept up-to-date but we also avoid issues with // that state being incorrect causing wrong behavior (I've had too many render @@ -18,12 +16,21 @@ import org.lwjgl.util.glu.GLU // because the state manager thought it already was in the state to change to, // so I frankly don't care if this is less performant anymore). object RenderState { - val arb = GLContext.getCapabilities.GL_ARB_multitexture && !GLContext.getCapabilities.OpenGL13 + def getErrorString(errorCode: Int): String = errorCode match { + case GL11.GL_NO_ERROR => "No error" + case GL11.GL_INVALID_ENUM => "Enum argument out of range" + case GL11.GL_INVALID_VALUE => "Numeric argument out of range" + case GL11.GL_INVALID_OPERATION => "Operation illegal in current state" + case GL11.GL_STACK_OVERFLOW => "Command would cause a stack overflow" + case GL11.GL_STACK_UNDERFLOW => "Command would cause a stack underflow" + case GL11.GL_OUT_OF_MEMORY => "Not enough memory left to execute command" + case _ => f"Unknown [0x$errorCode%X]" + } def checkError(where: String) { val error = GL11.glGetError if (error != 0 && Settings.get.logOpenGLErrors) { - OpenComputers.log.warn("GL ERROR @ " + where + ": " + GLU.gluErrorString(error)) + OpenComputers.log.warn("GL ERROR @ " + where + ": " + getErrorString(error)) } } @@ -35,52 +42,46 @@ object RenderState { else false } - // pushAttrib/popAttrib currently breaks the GlStateManager because it doesn't + // pushAttrib/popAttrib currently breaks the RenderSystem because it doesn't // accordingly pushes/pops its cache, so it gets into an illegal state... // See https://gist.github.com/fnuecke/9a5b2499835fca9b52419277dc6239ca def pushAttrib(): Unit = { -// GlStateManager.glPushAttrib(mask) +// RenderSystem.glPushAttrib(mask) } def popAttrib(): Unit = { -// GlStateManager.popAttrib() +// RenderSystem.popAttrib() } def disableEntityLighting() { - Minecraft.getMinecraft.entityRenderer.disableLightmap() - GlStateManager.disableLighting() - GlStateManager.disableLight(0) - GlStateManager.disableLight(1) - GlStateManager.disableColorMaterial() + RenderSystem.disableLighting() + RenderSystem.disableColorMaterial() } def enableEntityLighting() { - Minecraft.getMinecraft.entityRenderer.enableLightmap() - GlStateManager.enableLighting() - GlStateManager.enableLight(0) - GlStateManager.enableLight(1) - GlStateManager.enableColorMaterial() + RenderSystem.enableLighting() + RenderSystem.enableColorMaterial() } def makeItBlend() { - GlStateManager.enableBlend() + RenderSystem.enableBlend() GL11.glEnable(GL11.GL_BLEND) - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) } def disableBlend() { - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_ZERO) - GlStateManager.disableBlend() + RenderSystem.blendFunc(GL11.GL_ONE, GL11.GL_ZERO) + RenderSystem.disableBlend() GL11.glDisable(GL11.GL_BLEND) } def setBlendAlpha(alpha: Float) = { - GlStateManager.color(1, 1, 1, alpha) - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) + RenderSystem.color4f(1, 1, 1, alpha) + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) } def bindTexture(id: Int): Unit = { - GlStateManager.bindTexture(id) + RenderSystem.bindTexture(id) GL11.glBindTexture(GL11.GL_TEXTURE_2D, id) } } diff --git a/src/main/scala/li/cil/oc/util/RotationHelper.scala b/src/main/scala/li/cil/oc/util/RotationHelper.scala index 8eb8ee6e3c..3cf4d2f2e7 100644 --- a/src/main/scala/li/cil/oc/util/RotationHelper.scala +++ b/src/main/scala/li/cil/oc/util/RotationHelper.scala @@ -1,46 +1,48 @@ package li.cil.oc.util -import net.minecraft.util.EnumFacing +import net.minecraft.util.Direction import scala.collection.mutable object RotationHelper { + private val DIRECTIONS = Direction.values + + def getNumDirections = DIRECTIONS.length + + def getFront(index: Int): Direction = DIRECTIONS(Math.floorMod(index, DIRECTIONS.length)) + def fromYaw(yaw: Float) = { (yaw / 360 * 4).round & 3 match { - case 0 => EnumFacing.SOUTH - case 1 => EnumFacing.WEST - case 2 => EnumFacing.NORTH - case 3 => EnumFacing.EAST + case 0 => Direction.SOUTH + case 1 => Direction.WEST + case 2 => Direction.NORTH + case 3 => Direction.EAST } } - def toLocal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) = + def toLocal(pitch: Direction, yaw: Direction, value: Direction) = translationFor(pitch, yaw)(value.ordinal) - def toGlobal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) = + def toGlobal(pitch: Direction, yaw: Direction, value: Direction) = inverseTranslationFor(pitch, yaw)(value.ordinal) - def translationFor(pitch: EnumFacing, yaw: EnumFacing) = + def translationFor(pitch: Direction, yaw: Direction) = translationCache.synchronized(translationCache. getOrElseUpdate(pitch, mutable.Map.empty). getOrElseUpdate(yaw, translations(pitch.ordinal)(yaw.ordinal - 2))) - def inverseTranslationFor(pitch: EnumFacing, yaw: EnumFacing) = + def inverseTranslationFor(pitch: Direction, yaw: Direction) = inverseTranslationCache.synchronized(inverseTranslationCache. getOrElseUpdate(pitch, mutable.Map.empty). getOrElseUpdate(yaw, { val t = translationFor(pitch, yaw) - t.indices. - map(EnumFacing.getFront). - map(t.indexOf). - map(EnumFacing.getFront). - toArray + t.indices.map(Direction.from3DDataValue).map(t.indexOf).map(Direction.from3DDataValue).toArray })) // ----------------------------------------------------------------------- // - private val translationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]] - private val inverseTranslationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]] + private val translationCache = mutable.Map.empty[Direction, mutable.Map[Direction, Array[Direction]]] + private val inverseTranslationCache = mutable.Map.empty[Direction, mutable.Map[Direction, Array[Direction]]] /** * Translates forge directions based on the block's pitch and yaw. The base @@ -82,12 +84,12 @@ object RotationHelper { /** Shortcuts for forge directions to make the above more readable. */ private object D { - val down = EnumFacing.DOWN - val up = EnumFacing.UP - val north = EnumFacing.NORTH - val south = EnumFacing.SOUTH - val west = EnumFacing.WEST - val east = EnumFacing.EAST + val down = Direction.DOWN + val up = Direction.UP + val north = Direction.NORTH + val south = Direction.SOUTH + val west = Direction.WEST + val east = Direction.EAST } } diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index ce4393a47f..5751101f4f 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -251,45 +251,45 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col } } - def load(nbt: NBTTagCompound): Unit = { + def loadData(nbt: CompoundNBT): Unit = { val maxResolution = math.max(Settings.screenResolutionsByTier.last._1, Settings.screenResolutionsByTier.last._2) - val w = nbt.getInteger("width") min maxResolution max 1 - val h = nbt.getInteger("height") min maxResolution max 1 + val w = nbt.getInt("width") min maxResolution max 1 + val h = nbt.getInt("height") min maxResolution max 1 size = (w, h) - val b = nbt.getTagList("buffer", NBT.TAG_STRING) - for (i <- 0 until math.min(h, b.tagCount)) { - val value = b.getStringTagAt(i) + val b = nbt.getList("buffer", NBT.TAG_STRING) + for (i <- 0 until math.min(h, b.size)) { + val value = b.getString(i) System.arraycopy(value.toCharArray, 0, buffer(i), 0, math.min(value.length, buffer(i).length)) } - val depth = api.internal.TextBuffer.ColorDepth.values.apply(nbt.getInteger("depth") min (api.internal.TextBuffer.ColorDepth.values.length - 1) max 0) + val depth = api.internal.TextBuffer.ColorDepth.values.apply(nbt.getInt("depth") min (api.internal.TextBuffer.ColorDepth.values.length - 1) max 0) _format = PackedColor.Depth.format(depth) - _format.load(nbt) - foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette")) - background = PackedColor.Color(nbt.getInteger("background"), nbt.getBoolean("backgroundIsPalette")) + _format.loadData(nbt) + foreground = PackedColor.Color(nbt.getInt("foreground"), nbt.getBoolean("foregroundIsPalette")) + background = PackedColor.Color(nbt.getInt("background"), nbt.getBoolean("backgroundIsPalette")) if (!NbtDataStream.getShortArray(nbt, "colors", color, w, h)) { NbtDataStream.getIntArrayLegacy(nbt, "color", color, w, h) } } - def save(nbt: NBTTagCompound): Unit = { - nbt.setInteger("width", width) - nbt.setInteger("height", height) + def saveData(nbt: CompoundNBT): Unit = { + nbt.putInt("width", width) + nbt.putInt("height", height) - val b = new NBTTagList() + val b = new ListNBT() for (i <- 0 until height) { - b.appendTag(new NBTTagString(String.valueOf(buffer(i)))) + b.add(StringNBT.valueOf(String.valueOf(buffer(i)))) } - nbt.setTag("buffer", b) - - nbt.setInteger("depth", _format.depth.ordinal) - _format.save(nbt) - nbt.setInteger("foreground", _foreground.value) - nbt.setBoolean("foregroundIsPalette", _foreground.isPalette) - nbt.setInteger("background", _background.value) - nbt.setBoolean("backgroundIsPalette", _background.isPalette) + nbt.put("buffer", b) + + nbt.putInt("depth", _format.depth.ordinal) + _format.saveData(nbt) + nbt.putInt("foreground", _foreground.value) + nbt.putBoolean("foregroundIsPalette", _foreground.isPalette) + nbt.putInt("background", _background.value) + nbt.putBoolean("backgroundIsPalette", _background.isPalette) NbtDataStream.setShortArray(nbt, "colors", color.flatten.map(_.toShort)) } diff --git a/src/main/scala/li/cil/oc/util/Tooltip.scala b/src/main/scala/li/cil/oc/util/Tooltip.scala index 7ec02a735e..465003a558 100644 --- a/src/main/scala/li/cil/oc/util/Tooltip.scala +++ b/src/main/scala/li/cil/oc/util/Tooltip.scala @@ -4,6 +4,9 @@ import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.client.KeyBindings import net.minecraft.client.Minecraft +import net.minecraft.client.gui.FontRenderer +import net.minecraft.util.text.CharacterManager.ISliceAcceptor +import net.minecraft.util.text.Style import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ @@ -11,7 +14,7 @@ import scala.collection.convert.WrapAsScala._ object Tooltip { private val maxWidth = 220 - private def font = Minecraft.getMinecraft.fontRenderer + private def font = Minecraft.getInstance.font def get(name: String, args: Any*): java.util.List[String] = { if (!Localization.canLocalize(Settings.namespace + "tooltip." + name)) return Seq.empty[String] @@ -19,14 +22,14 @@ object Tooltip { format(args.map(_.toString): _*) if (font == null) return tooltip.lines.toList // Some mods request tooltips before font renderer is available. val isSubTooltip = name.contains(".") - val shouldShorten = (isSubTooltip || font.getStringWidth(tooltip) > maxWidth) && !KeyBindings.showExtendedTooltips + val shouldShorten = (isSubTooltip || font.width(tooltip) > maxWidth) && !KeyBindings.showExtendedTooltips if (shouldShorten) { if (isSubTooltip) Seq.empty[String] else Seq(Localization.localizeImmediately("tooltip.toolong", KeyBindings.getKeyBindingName(KeyBindings.extendedTooltip))) } else tooltip. lines. - map(font.listFormattedStringToWidth(_, maxWidth).map(_.asInstanceOf[String].trim() + " ")). + map(wrap(font, _, maxWidth).map(_.asInstanceOf[String].trim() + " ")). flatten. toList } @@ -36,9 +39,17 @@ object Tooltip { Localization.localizeImmediately("tooltip." + name). format(args.map(_.toString): _*). lines. - map(font.listFormattedStringToWidth(_, maxWidth).map(_.asInstanceOf[String].trim() + " ")). + map(wrap(font, _, maxWidth).map(_.asInstanceOf[String].trim() + " ")). flatten. toList } else Seq.empty[String] + + private def wrap(font: FontRenderer, line: String, width: Int): java.util.List[String] = { + val list = new java.util.ArrayList[String] + font.getSplitter.splitLines(line, width, net.minecraft.util.text.Style.EMPTY, true, new ISliceAcceptor { + override def accept(style: Style, start: Int, end: Int) = list.add(line.substring(start, end)) + }) + list + } } diff --git a/src/main/scala/li/cil/oc/util/UpdateCheck.scala b/src/main/scala/li/cil/oc/util/UpdateCheck.scala index 25b0ccb8ec..eea471a113 100644 --- a/src/main/scala/li/cil/oc/util/UpdateCheck.scala +++ b/src/main/scala/li/cil/oc/util/UpdateCheck.scala @@ -7,8 +7,7 @@ import com.google.gson.Gson import com.google.gson.stream.JsonReader import li.cil.oc.OpenComputers import li.cil.oc.Settings -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.versioning.ComparableVersion +import org.apache.maven.artifact.versioning.ComparableVersion import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global @@ -39,7 +38,7 @@ object UpdateCheck { if (candidates.nonEmpty) { val latest = candidates.maxBy(release => new ComparableVersion(release.tag_name.stripPrefix("v"))) val remoteVersion = new ComparableVersion(latest.tag_name.stripPrefix("v")) - val localVersion = new ComparableVersion(Loader.instance.getIndexedModList.get(OpenComputers.ID).getVersion) + val localVersion = new ComparableVersion(OpenComputers.modContainer.getModInfo.getVersion.toString) if (remoteVersion.compareTo(localVersion) > 0) { OpenComputers.log.info(s"A newer version of OpenComputers is available: ${latest.tag_name}.") return Some(latest) diff --git a/src/main/scala/li/cil/oc/util/UpgradeExperience.scala b/src/main/scala/li/cil/oc/util/UpgradeExperience.scala index afb6d530ef..6dc8ca6780 100644 --- a/src/main/scala/li/cil/oc/util/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/util/UpgradeExperience.scala @@ -2,16 +2,16 @@ package li.cil.oc.util import li.cil.oc.Settings import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.CompoundNBT object UpgradeExperience { final val XpTag = Settings.namespace + "xp" - def getExperience(nbt: NBTTagCompound): Double = nbt.getDouble(XpTag) max 0 + def getExperience(nbt: CompoundNBT): Double = nbt.getDouble(XpTag) max 0 - def getExperience(stack: ItemStack): Double = if (!stack.hasTagCompound) 0 else getExperience(stack.getTagCompound) + def getExperience(stack: ItemStack): Double = if (!stack.hasTag) 0 else getExperience(stack.getTag) - def setExperience(nbt: NBTTagCompound, experience: Double): Unit = nbt.setDouble(XpTag, experience) + def setExperience(nbt: CompoundNBT, experience: Double): Unit = nbt.putDouble(XpTag, experience) def xpForLevel(level: Int): Double = if (level == 0) 0 From 3271284e5d03426a29bb39dd23857bab318a76f8 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 17:57:28 +0200 Subject: [PATCH 005/159] Upgrade codebase to Scala 2.12 Scala 2.13 is incompatible due to changed implcits --- build.gradle | 2 +- src/main/scala/li/cil/oc/OpenComputers.scala | 2 +- src/main/scala/li/cil/oc/Settings.scala | 8 +++----- src/main/scala/li/cil/oc/client/Manual.scala | 7 ++++--- .../scala/li/cil/oc/client/gui/Assembler.scala | 4 ++-- src/main/scala/li/cil/oc/client/gui/Case.scala | 3 ++- .../cil/oc/client/gui/CustomGuiContainer.scala | 2 +- src/main/scala/li/cil/oc/client/gui/Drone.scala | 3 ++- .../cil/oc/client/gui/DynamicGuiContainer.scala | 4 ++-- src/main/scala/li/cil/oc/client/gui/Manual.scala | 5 +++-- src/main/scala/li/cil/oc/client/gui/Rack.scala | 2 +- src/main/scala/li/cil/oc/client/gui/Robot.scala | 6 ++++-- src/main/scala/li/cil/oc/client/gui/Screen.scala | 3 ++- src/main/scala/li/cil/oc/client/gui/Server.scala | 2 +- .../li/cil/oc/client/renderer/PetRenderer.scala | 2 +- .../oc/client/renderer/block/CableModel.scala | 3 ++- .../oc/client/renderer/block/DroneModel.scala | 2 +- .../renderer/block/ModelInitialization.scala | 2 +- .../client/renderer/block/NetSplitterModel.scala | 2 +- .../oc/client/renderer/block/PrintModel.scala | 2 +- .../oc/client/renderer/block/RobotModel.scala | 2 +- .../oc/client/renderer/block/ScreenModel.scala | 3 ++- .../client/renderer/block/ServerRackModel.scala | 2 +- .../segment/render/OreDictImageProvider.scala | 2 +- .../renderer/tileentity/RobotRenderer.scala | 2 +- .../li/cil/oc/common/ComponentTracker.scala | 5 +++-- .../scala/li/cil/oc/common/EventHandler.scala | 5 +++-- src/main/scala/li/cil/oc/common/IMC.scala | 2 +- src/main/scala/li/cil/oc/common/Loot.scala | 2 +- .../scala/li/cil/oc/common/PacketBuilder.scala | 2 +- src/main/scala/li/cil/oc/common/Proxy.scala | 2 +- .../scala/li/cil/oc/common/block/Cable.scala | 5 +++-- src/main/scala/li/cil/oc/common/block/Case.scala | 2 +- .../li/cil/oc/common/block/Disassembler.scala | 2 +- .../scala/li/cil/oc/common/block/DiskDrive.scala | 2 +- .../scala/li/cil/oc/common/block/Hologram.scala | 2 +- .../li/cil/oc/common/block/Microcontroller.scala | 3 ++- .../scala/li/cil/oc/common/block/Print.scala | 5 +++-- src/main/scala/li/cil/oc/common/block/Raid.scala | 3 ++- .../scala/li/cil/oc/common/block/Redstone.scala | 2 +- .../li/cil/oc/common/block/RobotProxy.scala | 2 +- .../scala/li/cil/oc/common/block/Screen.scala | 2 +- .../li/cil/oc/common/block/SimpleBlock.scala | 2 +- .../block/property/PropertyRotatable.scala | 2 +- .../oc/common/block/traits/PowerAcceptor.scala | 2 +- .../cil/oc/common/component/TerminalServer.scala | 4 ++-- .../li/cil/oc/common/component/TextBuffer.scala | 4 ++-- .../cil/oc/common/container/ComponentSlot.scala | 2 +- .../li/cil/oc/common/container/Player.scala | 2 +- .../scala/li/cil/oc/common/entity/Drone.scala | 3 ++- .../oc/common/event/AngelUpgradeHandler.scala | 2 +- .../common/event/ChunkloaderUpgradeHandler.scala | 2 +- .../common/event/ExperienceUpgradeHandler.scala | 2 +- .../cil/oc/common/event/HoverBootsHandler.scala | 2 +- .../event/WirelessNetworkCardHandler.scala | 2 +- .../oc/common/inventory/ComponentInventory.scala | 2 +- .../scala/li/cil/oc/common/item/HoverBoots.scala | 3 ++- .../scala/li/cil/oc/common/item/LinkedCard.scala | 2 +- .../scala/li/cil/oc/common/item/Server.scala | 2 +- .../scala/li/cil/oc/common/item/Tablet.scala | 5 +++-- .../scala/li/cil/oc/common/item/Wrench.scala | 3 ++- .../li/cil/oc/common/item/traits/CPULike.scala | 2 +- .../li/cil/oc/common/item/traits/Delegate.scala | 2 +- .../cil/oc/common/item/traits/SimpleItem.scala | 2 +- .../oc/common/nanomachines/ControllerImpl.scala | 4 ++-- .../oc/common/nanomachines/Nanomachines.scala | 2 +- .../oc/common/nanomachines/NeuralNetwork.scala | 2 +- .../nanomachines/provider/MagnetProvider.scala | 2 +- .../nanomachines/provider/PotionProvider.scala | 2 +- .../nanomachines/provider/ScalaProvider.scala | 3 ++- .../cil/oc/common/template/DroneTemplate.scala | 3 ++- .../template/MicrocontrollerTemplate.scala | 3 ++- .../cil/oc/common/template/RobotTemplate.scala | 3 ++- .../cil/oc/common/template/TabletTemplate.scala | 3 ++- .../oc/common/template/TemplateBlacklist.scala | 2 +- .../li/cil/oc/common/tileentity/Adapter.scala | 2 +- .../li/cil/oc/common/tileentity/Assembler.scala | 2 +- .../li/cil/oc/common/tileentity/Capacitor.scala | 2 +- .../oc/common/tileentity/CarpetedCapacitor.scala | 4 ++-- .../scala/li/cil/oc/common/tileentity/Case.scala | 2 +- .../li/cil/oc/common/tileentity/Charger.scala | 4 ++-- .../cil/oc/common/tileentity/Disassembler.scala | 2 +- .../li/cil/oc/common/tileentity/DiskDrive.scala | 2 +- .../li/cil/oc/common/tileentity/Hologram.scala | 2 +- .../oc/common/tileentity/Microcontroller.scala | 3 ++- .../cil/oc/common/tileentity/NetSplitter.scala | 2 +- .../oc/common/tileentity/PowerConverter.scala | 2 +- .../li/cil/oc/common/tileentity/Print.scala | 2 +- .../li/cil/oc/common/tileentity/Printer.scala | 2 +- .../oc/common/tileentity/traits/Computer.scala | 2 +- .../traits/power/AppliedEnergistics2.scala | 6 +++--- .../oc/integration/appeng/NetworkControl.scala | 4 ++-- .../computercraft/RelayPeripheral.scala | 5 +++-- .../oc/integration/jei/CallbackDocHandler.scala | 4 ++-- .../oc/integration/jei/ManualUsageHandler.scala | 4 ++-- .../scala/li/cil/oc/integration/jei/ModJEI.scala | 4 ++-- .../cil/oc/integration/jei/RelayGuiHandler.scala | 2 +- .../integration/mekanism/ConverterGasStack.scala | 2 +- .../minecraft/ConverterFluidStack.scala | 2 +- .../minecraft/ConverterFluidTankInfo.scala | 2 +- .../minecraft/ConverterFluidTankProperties.scala | 2 +- .../minecraft/ConverterItemStack.scala | 2 +- .../oc/integration/minecraft/ConverterNBT.scala | 2 +- .../integration/minecraft/ConverterWorld.scala | 2 +- .../minecraft/EventHandlerVanilla.scala | 2 +- .../opencomputers/ConverterLinkedCard.scala | 2 +- .../opencomputers/ConverterNanomachines.scala | 2 +- .../oc/integration/opencomputers/DriverCPU.scala | 4 ++-- .../scala/li/cil/oc/server/agent/Player.scala | 2 +- .../agent/PlayerInteractionManagerHelper.scala | 2 +- .../scala/li/cil/oc/server/component/APU.scala | 2 +- .../scala/li/cil/oc/server/component/Agent.scala | 2 +- .../scala/li/cil/oc/server/component/CPU.scala | 2 +- .../li/cil/oc/server/component/DataCard.scala | 2 +- .../li/cil/oc/server/component/DebugCard.scala | 3 ++- .../oc/server/component/DiskDriveMountable.scala | 2 +- .../scala/li/cil/oc/server/component/Drive.scala | 2 +- .../scala/li/cil/oc/server/component/Drone.scala | 4 ++-- .../li/cil/oc/server/component/EEPROM.scala | 2 +- .../li/cil/oc/server/component/FileSystem.scala | 2 +- .../li/cil/oc/server/component/Geolyzer.scala | 5 +++-- .../cil/oc/server/component/GraphicsCard.scala | 2 +- .../cil/oc/server/component/InternetCard.scala | 5 ++--- .../li/cil/oc/server/component/Keyboard.scala | 2 +- .../li/cil/oc/server/component/LinkedCard.scala | 4 ++-- .../li/cil/oc/server/component/Memory.scala | 2 +- .../cil/oc/server/component/MotionSensor.scala | 4 ++-- .../li/cil/oc/server/component/NetworkCard.scala | 4 ++-- .../li/cil/oc/server/component/Redstone.scala | 2 +- .../oc/server/component/RedstoneBundled.scala | 2 +- .../oc/server/component/RedstoneVanilla.scala | 2 +- .../oc/server/component/RedstoneWireless.scala | 2 +- .../scala/li/cil/oc/server/component/Robot.scala | 2 +- .../li/cil/oc/server/component/Server.scala | 2 +- .../li/cil/oc/server/component/Tablet.scala | 2 +- .../scala/li/cil/oc/server/component/Trade.scala | 2 +- .../li/cil/oc/server/component/Transposer.scala | 2 +- .../cil/oc/server/component/UpgradeAngel.scala | 2 +- .../server/component/UpgradeBarcodeReader.scala | 2 +- .../cil/oc/server/component/UpgradeBattery.scala | 2 +- .../oc/server/component/UpgradeChunkloader.scala | 2 +- .../oc/server/component/UpgradeCrafting.scala | 2 +- .../oc/server/component/UpgradeDatabase.scala | 2 +- .../oc/server/component/UpgradeExperience.scala | 16 ++++++++-------- .../oc/server/component/UpgradeGenerator.scala | 2 +- .../component/UpgradeInventoryController.scala | 2 +- .../cil/oc/server/component/UpgradeLeash.scala | 4 ++-- .../li/cil/oc/server/component/UpgradeMF.scala | 2 +- .../oc/server/component/UpgradeNavigation.scala | 2 +- .../cil/oc/server/component/UpgradePiston.scala | 2 +- .../li/cil/oc/server/component/UpgradeSign.scala | 2 +- .../server/component/UpgradeSolarGenerator.scala | 2 +- .../li/cil/oc/server/component/UpgradeTank.scala | 2 +- .../server/component/UpgradeTankController.scala | 2 +- .../oc/server/component/UpgradeTractorBeam.scala | 4 ++-- .../cil/oc/server/component/UpgradeTrading.scala | 4 ++-- .../server/component/WirelessNetworkCard.scala | 2 +- .../component/traits/InventoryAnalytics.scala | 2 +- .../component/traits/InventoryWorldControl.scala | 2 +- .../oc/server/component/traits/WorldAware.scala | 2 +- .../traits/WorldInventoryAnalytics.scala | 2 +- .../scala/li/cil/oc/server/driver/Registry.scala | 5 +++-- .../li/cil/oc/server/machine/ArgumentsImpl.scala | 2 +- .../scala/li/cil/oc/server/machine/Machine.scala | 7 ++++--- .../oc/server/machine/luac/ComponentAPI.scala | 2 +- .../cil/oc/server/machine/luac/ComputerAPI.scala | 2 +- .../machine/luac/NativeLuaArchitecture.scala | 2 +- .../cil/oc/server/machine/luac/UserdataAPI.scala | 2 +- .../oc/server/machine/luaj/ComponentAPI.scala | 2 +- .../cil/oc/server/machine/luaj/ComputerAPI.scala | 2 +- .../machine/luaj/LuaJLuaArchitecture.scala | 2 +- .../cil/oc/server/machine/luaj/UserdataAPI.scala | 2 +- .../li/cil/oc/server/network/Component.scala | 4 ++-- .../scala/li/cil/oc/server/network/Node.scala | 4 ++-- .../li/cil/oc/server/network/Waypoints.scala | 2 +- .../cil/oc/server/network/WirelessNetwork.scala | 2 +- src/main/scala/li/cil/oc/util/Color.scala | 2 +- .../scala/li/cil/oc/util/DatabaseAccess.scala | 4 ++-- .../scala/li/cil/oc/util/ExtendedLuaState.scala | 2 +- src/main/scala/li/cil/oc/util/ExtendedNBT.scala | 3 ++- .../scala/li/cil/oc/util/InventoryUtils.scala | 2 +- src/main/scala/li/cil/oc/util/ItemUtils.scala | 2 +- src/main/scala/li/cil/oc/util/ScalaClosure.scala | 2 +- src/main/scala/li/cil/oc/util/Tooltip.scala | 4 ++-- 184 files changed, 264 insertions(+), 236 deletions(-) diff --git a/build.gradle b/build.gradle index 9bd1c9b4fc..0e2c9d9752 100644 --- a/build.gradle +++ b/build.gradle @@ -121,7 +121,7 @@ dependencies { minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" implementation "com.typesafe:config:1.2.1" - implementation "org.scala-lang:scala-library:2.11.12" + implementation "org.scala-lang:scala-library:2.12.+" compileOnly fg.deobf("li.cil.tis3d:tis3d-1.16.5-forge:${config.tis3d.version}") compileOnly fg.deobf("mcp.mobius.waila:Hwyla:${config.hwyla.version}:api") diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 463db11e29..6027ca7206 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -20,7 +20,7 @@ import net.minecraftforge.fml.network.simple.SimpleChannel import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ @Mod(OpenComputers.ID) object OpenComputers { diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 893e656be3..2fcfcb9a8e 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -17,11 +17,10 @@ import li.cil.oc.server.component.DebugCard import li.cil.oc.server.component.DebugCard.AccessContext import net.minecraftforge.fml.loading.FMLPaths import org.apache.commons.codec.binary.Hex -import org.apache.commons.lang3.StringEscapeUtils import org.apache.maven.artifact.versioning.DefaultArtifactVersion import org.apache.maven.artifact.versioning.VersionRange -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.io.Codec import scala.io.Source @@ -530,7 +529,6 @@ object Settings { try { val renderSettings = ConfigRenderOptions.defaults.setJson(false).setOriginComments(false) val nl = sys.props("line.separator") - val nle = StringEscapeUtils.escapeJava(nl) file.getParentFile.mkdirs() val out = new PrintWriter(file) out.write(config.root.render(renderSettings).lines. @@ -539,7 +537,7 @@ object Settings { // Finalize the string. filter(_ != "").mkString(nl). // Newline after values. - replaceAll(s"((?:\\s*#.*$nle)(?:\\s*[^#\\s].*$nle)+)", "$1" + nl)) + replaceAll(s"((?:\\s*#.*$nl)(?:\\s*[^#\\s].*$nl)+)", "$1" + nl)) out.close() } catch { @@ -576,7 +574,7 @@ object Settings { val prefix = "opencomputers." val configVersion = new DefaultArtifactVersion(if (config.hasPath(prefix + "version")) config.getString(prefix + "version") else "0.0.0") var patched = config - if (!configVersion.equals(modVersion)) { + if (configVersion.compareTo(modVersion) != 0) { OpenComputers.log.info(s"Updating config from version '${configVersion}' to '${defaults.getString(prefix + "version")}'.") patched = patched.withValue(prefix + "version", defaults.getValue(prefix + "version")) for ((version, paths) <- configPatches if version.containsVersion(configVersion)) { diff --git a/src/main/scala/li/cil/oc/client/Manual.scala b/src/main/scala/li/cil/oc/client/Manual.scala index 4c5c91fe7e..f5eaeaf0dd 100644 --- a/src/main/scala/li/cil/oc/client/Manual.scala +++ b/src/main/scala/li/cil/oc/client/Manual.scala @@ -16,8 +16,9 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.World import scala.annotation.tailrec -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object Manual extends ManualAPI { @@ -37,7 +38,7 @@ object Manual extends ManualAPI { val imageProviders = mutable.Buffer.empty[(String, ImageProvider)] - val history = new mutable.Stack[History] + val history = new mutable.ArrayStack[History] reset() diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 2d99098534..1024360359 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -17,8 +17,8 @@ import net.minecraft.inventory.container.Slot import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) extends DynamicGuiContainer(new container.Assembler(id, playerInventory, assembler), diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index ffb3ea85e4..e7bebb0e51 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -10,7 +10,8 @@ import li.cil.oc.common.tileentity import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaCollection +import scala.collection.convert.ImplicitConversionsToJava._ class Case(id: Int, playerInventory: PlayerInventory, val computer: tileentity.Case) extends DynamicGuiContainer(new container.Case(id, playerInventory, computer), diff --git a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala index feacf698a1..11e9734c32 100644 --- a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala @@ -18,7 +18,7 @@ import net.minecraft.util.text.ITextProperties import net.minecraft.util.text.LanguageMap import net.minecraft.util.text.StringTextComponent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ // Workaround because certain other mods *cough*TMI*cough* do base class // transformations that break things! Such fun. Many annoyed. And yes, this diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 2c149a33fa..83ae228cb8 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -20,7 +20,8 @@ import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaCollection +import scala.collection.convert.ImplicitConversionsToJava._ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) extends DynamicGuiContainer(new container.Drone(id, playerInventory, drone), diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index ff3d3ea238..5e68b4cd63 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -22,8 +22,8 @@ import net.minecraft.inventory.container.Slot import net.minecraft.util.text.ITextComponent import org.lwjgl.opengl.GL11 -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ abstract class DynamicGuiContainer[C <: Container](container: C, inv: PlayerInventory, title: ITextComponent) extends CustomGuiContainer(container, inv, title) { diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index fc9c5de99d..82c86c5ef3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -17,8 +17,9 @@ import net.minecraft.util.text.ITextProperties import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.{asJavaIterable, seqAsJavaList} +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window { final val documentMaxWidth = 230 diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index ed6ff9847e..33d7c71afe 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -16,7 +16,7 @@ import net.minecraft.util.Direction import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 -import scala.collection.convert.WrapAsJava.asJavaCollection +import scala.collection.JavaConverters.asJavaCollection class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) extends DynamicGuiContainer(new container.Rack(id, playerInventory, rack), diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index e3b4a167c6..3d1ba96bd1 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -15,6 +15,7 @@ import li.cil.oc.common.container import li.cil.oc.common.tileentity import li.cil.oc.integration.opencomputers import li.cil.oc.util.RenderState +import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats @@ -23,12 +24,13 @@ import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.GL11 -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaCollection +import scala.collection.convert.ImplicitConversionsToJava._ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Robot) extends DynamicGuiContainer(new container.Robot(id, playerInventory, robot), playerInventory, StringTextComponent.EMPTY) - with traits.InputBuffer { + with traits.InputBuffer with INestedGuiEventHandler { override protected val buffer: TextBuffer = robot.components.collect { case Some(buffer: api.internal.TextBuffer) => buffer diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index 60a4d43c8e..dc6e994558 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -6,12 +6,13 @@ import li.cil.oc.api import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.util.RenderState +import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.screen import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val hasKeyboardCallback: () => Boolean, val hasPower: () => Boolean) - extends screen.Screen(StringTextComponent.EMPTY) with traits.InputBuffer { + extends screen.Screen(StringTextComponent.EMPTY) with traits.InputBuffer with INestedGuiEventHandler { override protected def hasKeyboard = hasKeyboardCallback() diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index f2afe79dee..0fb265b9d0 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -12,7 +12,7 @@ import net.minecraft.client.Minecraft import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory -import scala.collection.convert.WrapAsJava.asJavaCollection +import scala.collection.JavaConverters.asJavaCollection class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val rack: Option[tileentity.Rack] = None, val slot: Int = 0) extends DynamicGuiContainer(new container.Server(id, playerInventory, serverInventory), diff --git a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala index 2e180e3ba4..6fe801a532 100644 --- a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala @@ -19,7 +19,7 @@ import net.minecraftforge.eventbus.api.EventPriority import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.event.TickEvent.ClientTickEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object PetRenderer { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala index 03a4e9d0c5..8b14f8348b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala @@ -23,7 +23,8 @@ import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.client.model.data.IModelData -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.bufferAsJavaList +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable object CableModel extends CableModel diff --git a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala index a1e12ef33b..e688bfc765 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala @@ -14,7 +14,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3d -import scala.collection.convert.WrapAsJava.bufferAsJavaList +import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable object DroneModel extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 84ac88e8d1..721101a70a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -26,7 +26,7 @@ import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.MinecraftForge import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object ModelInitialization { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala index 4478b90507..ca560f1ada 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala @@ -20,7 +20,7 @@ import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsJava.bufferAsJavaList +import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable object NetSplitterModel extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala index 471e178a97..4ef1d1d142 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala @@ -25,7 +25,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraftforge.client.model.data.IModelData -import scala.collection.convert.WrapAsJava.bufferAsJavaList +import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable object PrintModel extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala index 9a42294b16..cfaa2a82d7 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/RobotModel.scala @@ -13,7 +13,7 @@ import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava.bufferAsJavaList +import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable object RobotModel extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala index 8414835514..2526256b4e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ScreenModel.scala @@ -20,7 +20,8 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraftforge.client.model.data.IModelData -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.seqAsJavaList +import scala.collection.convert.ImplicitConversionsToJava._ object ScreenModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala index 5601a3bff2..0632457781 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ServerRackModel.scala @@ -20,7 +20,7 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.common.MinecraftForge import net.minecraftforge.client.model.data.IModelData -import scala.collection.convert.WrapAsJava.bufferAsJavaList +import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable class ServerRackModel(val parent: IBakedModel) extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala index 8433df34e3..bafc9f30fe 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala @@ -9,7 +9,7 @@ import net.minecraft.tags._ import net.minecraft.util.ResourceLocation import scala.collection.mutable -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object OreDictImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index fee25b868e..3a19666baf 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -38,7 +38,7 @@ import net.minecraftforge.client.ForgeHooksClient import net.minecraftforge.common.MinecraftForge import org.lwjgl.opengl.GL11 -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable import scala.language.implicitConversions diff --git a/src/main/scala/li/cil/oc/common/ComponentTracker.scala b/src/main/scala/li/cil/oc/common/ComponentTracker.scala index 9fbc02b039..359e832b50 100644 --- a/src/main/scala/li/cil/oc/common/ComponentTracker.scala +++ b/src/main/scala/li/cil/oc/common/ComponentTracker.scala @@ -8,8 +8,9 @@ import net.minecraft.world.World import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable /** diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index b4e36be026..46dfd36374 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -62,7 +62,7 @@ import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -280,8 +280,9 @@ object EventHandler { val server = ServerLifecycleHooks.getCurrentServer if (server.getPlayerList.isOp(player.getGameProfile)) { Future { - UpdateCheck.info onSuccess { + UpdateCheck.info foreach { case Some(release) => player.sendMessage(Localization.Chat.InfoNewVersion(release.tag_name), Util.NIL_UUID) + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/IMC.scala b/src/main/scala/li/cil/oc/common/IMC.scala index 264a0507e9..7e86efac31 100644 --- a/src/main/scala/li/cil/oc/common/IMC.scala +++ b/src/main/scala/li/cil/oc/common/IMC.scala @@ -22,7 +22,7 @@ import net.minecraft.util.math.BlockPos import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.InterModComms.IMCMessage -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object IMC { def handleMessage(message: IMCMessage): Unit = { diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index 303edf3c49..e7c95be520 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -24,7 +24,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable //class Loot extends WeightedRandomChestContent(api.Items.get(Constants.ItemName.Floppy).item(), api.Items.get(Constants.ItemName.Floppy).createItemStack(1).getDamageValue, 1, 1, Settings.get.lootProbability) { diff --git a/src/main/scala/li/cil/oc/common/PacketBuilder.scala b/src/main/scala/li/cil/oc/common/PacketBuilder.scala index 382aa44236..d8fd02ccd8 100644 --- a/src/main/scala/li/cil/oc/common/PacketBuilder.scala +++ b/src/main/scala/li/cil/oc/common/PacketBuilder.scala @@ -24,7 +24,7 @@ import net.minecraftforge.fml.network.PacketDistributor import net.minecraftforge.fml.server.ServerLifecycleHooks import net.minecraftforge.registries._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stream) { def writeRegistryEntry[T <: IForgeRegistryEntry[T]](registry: IForgeRegistry[T], value: T): Unit = diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 0345d4f510..522141b400 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -45,7 +45,7 @@ import net.minecraftforge.fml.network.NetworkEvent import net.minecraftforge.fml.network.NetworkRegistry import net.minecraftforge.registries.ForgeRegistries -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.reflect.ClassTag @Deprecated diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index 27325376de..772fd672cd 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -18,11 +18,12 @@ import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceResult} import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.IBlockReader import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeBlock -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.reflect.ClassTag -class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends SimpleBlock with traits.CustomDrops[tileentity.Cable] { +class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends SimpleBlock with IForgeBlock with traits.CustomDrops[tileentity.Cable] { // For Immibis Microblock support. val ImmibisMicroblocks_TransformableBlockMarker = null diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 50819c07eb..98723c0fda 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -23,7 +23,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index c1a0ec06e7..3a263cbb0d 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -15,7 +15,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 4178635b1f..6849c28c51 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -21,7 +21,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class DiskDrive extends SimpleBlock with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index 6cb1702320..adb11e7bbc 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -17,7 +17,7 @@ import net.minecraft.world.IBlockReader import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Hologram(val tier: Int) extends SimpleBlock { val bounds = new AxisAlignedBB(0, 0, 0, 1, 0.5f, 1) diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index f5fbb183af..b71e885113 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -31,10 +31,11 @@ import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { +class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) extends RedstoneAware with IForgeBlock with traits.PowerAcceptor with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { setCreativeTab(null) ItemBlacklist.hide(this) diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 85cc75a0a3..6ba9d6d79c 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -31,12 +31,13 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld +import net.minecraftforge.common.extensions.IForgeBlock -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.reflect.ClassTag class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware(Properties.of(Material.METAL).strength(1, 5).noOcclusion()) - with traits.CustomDrops[tileentity.Print] { + with IForgeBlock with traits.CustomDrops[tileentity.Print] { setCreativeTab(null) ItemBlacklist.hide(this) diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index 466cf6f98b..6dd7192955 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -20,10 +20,11 @@ import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends SimpleBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { +class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends SimpleBlock with IForgeBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index c4c226e538..3a8f9d2bc2 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -13,7 +13,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Redstone extends RedstoneAware { override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index dfa985b749..34397ebd1b 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -41,7 +41,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 10).noOcclusion()) extends RedstoneAware(props) with traits.StateAware { setCreativeTab(null) diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 94b31ad532..2ba022d9df 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -32,7 +32,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.{IBlockReader, World} import net.minecraftforge.api.distmarker.{Dist, OnlyIn} -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Screen(val tier: Int) extends RedstoneAware { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index f115ee2a3c..6627ceab09 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -43,7 +43,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.ToolType -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).strength(2, 5)) extends ContainerBlock(props.isValidSpawn(new IExtendedPositionPredicate[EntityType[_]] { override def test(state: BlockState, world: IBlockReader, pos: BlockPos, entity: EntityType[_]) = state.getBlock.asInstanceOf[SimpleBlock].isValidSpawn(state, world, pos, entity) diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala index 92961e330e..02c82f1880 100644 --- a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala +++ b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala @@ -5,7 +5,7 @@ import com.google.common.base.Predicates import net.minecraft.state.DirectionProperty import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ object PropertyRotatable { final val Facing = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL.asInstanceOf[Predicate[Direction]]) diff --git a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala index 7fc5f3cc09..e2608bb242 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala @@ -11,7 +11,7 @@ import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait PowerAcceptor extends SimpleBlock { def energyThroughput: Double diff --git a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala index 0bf50876dc..555c45a39b 100644 --- a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala +++ b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala @@ -33,8 +33,8 @@ import net.minecraft.util.Direction import net.minecraft.util.Hand import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsScala._ -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToScala._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environment with EnvironmentHost with Analyzable with RackMountable with Lifecycle with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index a7ea84b2d5..6379456e7c 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -41,8 +41,8 @@ import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.TextBufferProxy with VideoRamRasterizer with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index a1811e24e0..348c68c8be 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -9,7 +9,7 @@ import net.minecraft.util.ResourceLocation import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) extends Slot(inventory, index, x, y) { def agentContainer: Player diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index 489e552d8b..245c944732 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -21,7 +21,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.util.FakePlayer -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable abstract class Player(cType: ContainerType[_ <: Player], id: Int, val playerInventory: PlayerInventory, val otherInventory: IInventory) extends Container(cType, id) { diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index de85bdf2c5..de6b59e3f9 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -55,7 +55,8 @@ import net.minecraft.world.server.ServerWorld import net.minecraftforge.fluids.IFluidTank import net.minecraftforge.fml.network.NetworkHooks -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ object Drone { val DataRunning: DataParameter[lang.Boolean] = EntityDataManager.defineId(classOf[Drone], DataSerializers.BOOLEAN) diff --git a/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala index 317296dc87..133d0b6142 100644 --- a/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/AngelUpgradeHandler.scala @@ -5,7 +5,7 @@ import li.cil.oc.api.network.Node import li.cil.oc.server.component.UpgradeAngel import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object AngelUpgradeHandler { @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala index 45b434bed2..f42a883fe7 100644 --- a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala @@ -17,7 +17,7 @@ import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraft.entity.Entity -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.immutable import scala.collection.mutable diff --git a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala index 1c712ced30..d88a972859 100644 --- a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala @@ -11,7 +11,7 @@ import li.cil.oc.server.component import net.minecraft.util.Util import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ExperienceUpgradeHandler { @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala b/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala index 79abab6235..8d621f5e9a 100644 --- a/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/HoverBootsHandler.scala @@ -10,7 +10,7 @@ import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent import net.minecraftforge.event.entity.living.LivingFallEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object HoverBootsHandler { @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala b/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala index 43dbe8a1d4..a5bc98579e 100644 --- a/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/WirelessNetworkCardHandler.scala @@ -5,7 +5,7 @@ import li.cil.oc.api.event.RobotMoveEvent import li.cil.oc.server.component.WirelessNetworkCard import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object WirelessNetworkCardHandler { @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala b/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala index a425e554ce..8ec5e5f402 100644 --- a/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/ComponentInventory.scala @@ -13,7 +13,7 @@ import li.cil.oc.integration.opencomputers.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable trait ComponentInventory extends Inventory with network.Environment { diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index 3305f8649e..1dc48b3518 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -24,9 +24,10 @@ import net.minecraft.potion.EffectInstance import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.extensions.IForgeItem class HoverBoots(props: Properties = new Properties().tab(CreativeTab).setNoRepair()) - extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with traits.SimpleItem with traits.Chargeable { + extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with IForgeItem with traits.SimpleItem with traits.Chargeable { override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON diff --git a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala index 8f48a9d4dd..5758705552 100644 --- a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala @@ -11,7 +11,7 @@ import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class LinkedCard(val parent: Delegator) extends traits.Delegate with traits.ItemTier { override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index a08a0f6c53..824500c729 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -19,7 +19,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import scala.collection.mutable -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class Server(val parent: Delegator, val tier: Int) extends traits.Delegate { override val unlocalizedName: String = super.unlocalizedName + tier diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index 64a38428c6..d153e05da8 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -64,8 +64,9 @@ import net.minecraftforge.event.TickEvent.ServerTickEvent import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel with traits.Chargeable { final val TimeToAnalyze = 10 diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index c6179dd510..d165d508e0 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -18,12 +18,13 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.IWorldReader import net.minecraft.world.World import net.minecraftforge.common.ToolType +import net.minecraftforge.common.extensions.IForgeItem object Wrench { val WrenchType: ToolType = ToolType.get("wrench") } -class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench.WrenchType, 1).tab(CreativeTab)) extends Item(props) with traits.SimpleItem with api.internal.Wrench { +class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench.WrenchType, 1).tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with api.internal.Wrench { override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true override def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { diff --git a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala index 0c53086577..cc5dd30c33 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala @@ -18,7 +18,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.TranslationTextComponent import net.minecraft.world.World -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.language.existentials trait CPULike extends Delegate { diff --git a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala b/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala index f4b13bfc11..d00458736d 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala @@ -28,7 +28,7 @@ import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait Delegate { def parent: Delegator diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 86849f9ebe..3799c18cb6 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -25,7 +25,7 @@ import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait SimpleItem extends Item { def createItemStack(amount: Int = 1) = new ItemStack(this, amount) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala index 32550f539c..e5bfe49178 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala @@ -31,8 +31,8 @@ import net.minecraft.potion.EffectInstance import net.minecraft.util.ResourceLocation import net.minecraft.world.World -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class ControllerImpl(val player: PlayerEntity) extends Controller with WirelessEndpoint { diff --git a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala index 40324fa914..c05ad7fb77 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala @@ -8,7 +8,7 @@ import li.cil.oc.server.PacketSender import li.cil.oc.util.PlayerUtils import net.minecraft.entity.player.PlayerEntity -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable object Nanomachines extends api.detail.NanomachinesAPI { diff --git a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala index 7190915731..4a5e47b12c 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala @@ -16,7 +16,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.TextFormatting import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.util.Random diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala index c9d4b91e59..6060551903 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala @@ -8,7 +8,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.CompoundNBT import net.minecraft.util.math.vector.Vector3d -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object MagnetProvider extends ScalaProvider("9324d5ec-71f1-41c2-b51c-406e527668fc") { override def createScalaBehaviors(player: PlayerEntity) = Iterable(new MagnetBehavior(player)) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala index 8e05924bc3..987c341541 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala @@ -12,7 +12,7 @@ import net.minecraft.potion.EffectInstance import net.minecraft.util.ResourceLocation import net.minecraftforge.registries.ForgeRegistries -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object PotionProvider extends ScalaProvider("c29e4eec-5a46-479a-9b3d-ad0f06da784a") { // Lazy to give other mods a chance to register their potions. diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala index a24f7445d7..849b763b78 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala @@ -4,7 +4,8 @@ import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.prefab.AbstractProvider import net.minecraft.entity.player.PlayerEntity -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ abstract class ScalaProvider(id: String) extends AbstractProvider(id) { def createScalaBehaviors(player: PlayerEntity): Iterable[Behavior] diff --git a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala index 5de4adc5ee..0b34419bd4 100644 --- a/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/DroneTemplate.scala @@ -13,7 +13,8 @@ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ object DroneTemplate extends Template { override protected val suggestedComponents = Array( diff --git a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala index 119abf23bd..ccd2d3ca99 100644 --- a/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/MicrocontrollerTemplate.scala @@ -12,7 +12,8 @@ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ object MicrocontrollerTemplate extends Template { override protected val suggestedComponents = Array( diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala index 4e8971574b..e3251b04ee 100644 --- a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -11,7 +11,8 @@ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ object RobotTemplate extends Template { override protected def hostClass = classOf[internal.Robot] diff --git a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala index d3180af979..26d48c1626 100644 --- a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala @@ -11,7 +11,8 @@ import li.cil.oc.util.ItemUtils import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ object TabletTemplate extends Template { override protected val suggestedComponents = Array( diff --git a/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala b/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala index 7bc4f0e225..158b774161 100644 --- a/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala +++ b/src/main/scala/li/cil/oc/common/template/TemplateBlacklist.scala @@ -7,7 +7,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation import net.minecraftforge.registries.ForgeRegistries -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object TemplateBlacklist { private lazy val TheBlacklist = { // scnr diff --git a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala index 11ac4e7475..02cb094212 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala @@ -25,7 +25,7 @@ import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class Adapter extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala index c3db18f83c..54d9ef7798 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala @@ -24,7 +24,7 @@ import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Assembler extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { val node = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala index 13182e4705..bec03da366 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala @@ -14,7 +14,7 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Capacitor extends TileEntity(null) with traits.Environment with DeviceInfo { // Start with maximum theoretical capacity, gets reduced after validation. diff --git a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala index fade14c91e..17e1a6122b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala @@ -11,8 +11,8 @@ import net.minecraft.entity.passive.{OcelotEntity, SheepEntity} import net.minecraft.util.DamageSource import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class CarpetedCapacitor extends Capacitor with traits.Tickable { private final lazy val deviceInfo = Map( diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala index aefba594aa..e78016dc4d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala @@ -24,7 +24,7 @@ import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Case(var tier: Int) extends TileEntity(null) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { def this() = { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index 634cbc1c4d..79e8645a90 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -30,8 +30,8 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class Charger extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index 28be470007..b8c4327d1f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -26,7 +26,7 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer diff --git a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala index fdc388e7e9..1507c71428 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala @@ -29,7 +29,7 @@ import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class DiskDrive extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { // Used on client side to check whether to render disk activity indicators. diff --git a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala index 63164a7164..de5a98615a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala @@ -22,7 +22,7 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class Hologram(var tier: Int) extends TileEntity(null) with traits.Environment with SidedEnvironment with Analyzable with traits.RotatableTile with traits.Tickable with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 8ac3d04273..3f4d924b8a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -29,7 +29,8 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.JavaConverters.asJavaIterable +import scala.collection.convert.ImplicitConversionsToJava._ class Microcontroller extends TileEntity(null) with traits.PowerAcceptor with traits.Hub with traits.Computer with ISidedInventory with internal.Microcontroller with DeviceInfo { val info = new MicrocontrollerData() diff --git a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala index 8aa8f9259d..24a1517f9e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala @@ -19,7 +19,7 @@ import net.minecraft.util.SoundCategory import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class NetSplitter extends TileEntity(null) with traits.Environment with traits.OpenSides with traits.RedstoneAware with api.network.SidedEnvironment with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala index be21a3f2d0..26e4db0ff5 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala @@ -14,7 +14,7 @@ import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class PowerConverter extends TileEntity(null) with traits.PowerAcceptor with traits.Environment with traits.NotAnalyzable with DeviceInfo { val node = api.Network.newNode(this, Visibility.None). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index 27162cef6c..c1ed466447 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -24,7 +24,7 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) extends TileEntity(null) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { def this() = this(None, None, None) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index 5a5ca5b965..6df4a17724 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -27,7 +27,7 @@ import net.minecraft.util.math.AxisAlignedBB import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Printer extends TileEntity(null) with traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { val node: ComponentConnector = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index 60069ab11f..4baff36ce7 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -22,7 +22,7 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable trait Computer extends Environment with ComponentInventory with Rotatable with BundledRedstoneAware with api.network.Analyzable with api.machine.MachineHost with StateAware with Tickable { diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala index 1d23e60f60..65439add00 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala @@ -19,7 +19,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraftforge.fml.common._ -import scala.collection.JavaConversions +import scala.collection.JavaConverters trait AppliedEnergistics2 extends Common with IGridHost { private lazy val useAppliedEnergistics2Power = isServer && Mods.AppliedEnergistics2.isModAvailable @@ -127,9 +127,9 @@ class AppliedEnergistics2GridBlock(val tileEntity: AppliedEnergistics2) extends override def onGridNotification(p1: GridNotification): Unit = {} override def getConnectableSides: util.EnumSet[Direction] = { - val connectableSides = JavaConversions.asJavaCollection(Direction.values.filter(tileEntity.canConnectPower)) + val connectableSides = JavaConverters.asJavaCollection(Direction.values.filter(tileEntity.canConnectPower)) if (connectableSides.isEmpty) { - val s = util.EnumSet.copyOf(JavaConversions.asJavaCollection(Direction.values)) + val s = util.EnumSet.copyOf(JavaConverters.asJavaCollection(Direction.values)) s.clear() s } diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index 4b9e2aeef8..644e50242d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -32,8 +32,8 @@ import net.minecraft.world.World import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global diff --git a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala index dd60721ddd..042ae36829 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala @@ -15,8 +15,9 @@ import li.cil.oc.common.tileentity.Relay import li.cil.oc.util.ResultWrapper._ import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.mapAsJavaMap +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class RelayPeripheral(val relay: Relay) extends IDynamicPeripheral { diff --git a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala index 48c9820fbe..31fdd35fa7 100644 --- a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala @@ -24,8 +24,8 @@ import net.minecraft.util.text.CharacterManager.ISliceAcceptor import net.minecraft.util.text.Style import net.minecraft.util.text.TextFormatting -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object CallbackDocHandler { diff --git a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala index 49683119cf..4088ec50f6 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala @@ -20,8 +20,8 @@ import net.minecraft.util.ResourceLocation import net.minecraft.client.gui.widget.button.Button import org.lwjgl.glfw.GLFW -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ object ManualUsageHandler { diff --git a/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala b/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala index 0c86caa22b..8bd2316574 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModJEI.scala @@ -6,10 +6,10 @@ import mezz.jei.api.runtime.IIngredientManager import mezz.jei.api.runtime.IJeiRuntime import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava.seqAsJavaList +import scala.collection.JavaConverters.seqAsJavaList import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ModJEI { var runtime: Option[IJeiRuntime] = None diff --git a/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala b/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala index 6abd7d21f1..569dba0975 100644 --- a/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/RelayGuiHandler.scala @@ -6,7 +6,7 @@ import li.cil.oc.client.gui.Relay import mezz.jei.api.gui.handlers.IGuiContainerHandler import net.minecraft.client.renderer.Rectangle2d -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ object RelayGuiHandler extends IGuiContainerHandler[Relay] { diff --git a/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala index 57e94bd486..2bb4727391 100644 --- a/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala +++ b/src/main/scala/li/cil/oc/integration/mekanism/ConverterGasStack.scala @@ -9,7 +9,7 @@ import mekanism.api.chemical.gas.Gas import mekanism.api.chemical.gas.GasStack import net.minecraftforge.registries.ForgeRegistry -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterGasStack extends api.driver.Converter { override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala index c200b730ed..b7ae55a38f 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala @@ -4,7 +4,7 @@ import java.util import li.cil.oc.api -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterFluidStack extends api.driver.Converter { override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala index cb677efb17..c0e312daf5 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankInfo.scala @@ -5,7 +5,7 @@ import java.util import li.cil.oc.api import net.minecraftforge.fluids -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterFluidTankInfo extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala index a3b2ad3fd7..3c7c80cd77 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidTankProperties.scala @@ -5,7 +5,7 @@ import java.util import li.cil.oc.api import li.cil.oc.util.ExtendedArguments.TankProperties -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterFluidTankProperties extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala index 7ed795a572..d7b55baa9b 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala @@ -14,7 +14,7 @@ import net.minecraft.nbt.{CompoundNBT, ListNBT, StringNBT} import net.minecraft.tags.ItemTags import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object ConverterItemStack extends api.driver.Converter { diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala index d7cf411718..e4d162fe0e 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterNBT.scala @@ -5,7 +5,7 @@ import java.util import li.cil.oc.api import net.minecraft.nbt._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterNBT extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala index 0305e560de..5c75574987 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterWorld.scala @@ -9,7 +9,7 @@ import li.cil.oc.api import net.minecraft.world.World import net.minecraft.world.server.ServerWorld -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterWorld extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = { diff --git a/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala b/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala index f93b196d53..1ca782bbb1 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/EventHandlerVanilla.scala @@ -13,7 +13,7 @@ import net.minecraft.block.Blocks import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fluids.IFluidBlock -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object EventHandlerVanilla { @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ConverterLinkedCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ConverterLinkedCard.scala index 93138a93b4..d7b3a299c9 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ConverterLinkedCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ConverterLinkedCard.scala @@ -9,7 +9,7 @@ import li.cil.oc.api.driver.Converter import li.cil.oc.server.component import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterLinkedCard extends Converter { lazy val linkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ConverterNanomachines.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ConverterNanomachines.scala index 2eb0d27027..8116a9d362 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ConverterNanomachines.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ConverterNanomachines.scala @@ -9,7 +9,7 @@ import li.cil.oc.api.driver.Converter import li.cil.oc.common.item.data.NanomachineData import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object ConverterNanomachines extends Converter { lazy val nanomachines = api.Items.get(Constants.ItemName.Nanomachines) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala index 21c70b7d7b..1b3a6b9ea8 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala @@ -13,8 +13,8 @@ import li.cil.oc.server.machine.luac.NativeLuaArchitecture import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ object DriverCPU extends DriverCPU diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index 7a1416b231..51ef3c029d 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -59,7 +59,7 @@ import net.minecraftforge.eventbus.api.{Event, EventPriority, SubscribeEvent} import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.wrapper._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object Player { def profileFor(agent: internal.Agent): GameProfile = { diff --git a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala index dfb862e84e..4e096d5ddf 100644 --- a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala +++ b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala @@ -12,7 +12,7 @@ import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.fml.common.ObfuscationReflectionHelper import net.minecraftforge.eventbus.api.{EventPriority, SubscribeEvent} -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object PlayerInteractionManagerHelper { diff --git a/src/main/scala/li/cil/oc/server/component/APU.scala b/src/main/scala/li/cil/oc/server/component/APU.scala index cb3e3a948e..51b918a347 100644 --- a/src/main/scala/li/cil/oc/server/component/APU.scala +++ b/src/main/scala/li/cil/oc/server/component/APU.scala @@ -7,7 +7,7 @@ import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass import li.cil.oc.Settings -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class APU(tier: Int) extends GraphicsCard(tier) { private final lazy val deviceInfo = Map( diff --git a/src/main/scala/li/cil/oc/server/component/Agent.scala b/src/main/scala/li/cil/oc/server/component/Agent.scala index 9bf06d0642..5dab607def 100644 --- a/src/main/scala/li/cil/oc/server/component/Agent.scala +++ b/src/main/scala/li/cil/oc/server/component/Agent.scala @@ -33,7 +33,7 @@ import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.common.MinecraftForge -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait Agent extends traits.WorldControl with traits.InventoryControl with traits.InventoryWorldControl with traits.TankAware with traits.TankControl with traits.TankWorldControl { def agent: internal.Agent diff --git a/src/main/scala/li/cil/oc/server/component/CPU.scala b/src/main/scala/li/cil/oc/server/component/CPU.scala index ecaef64912..38168a887d 100644 --- a/src/main/scala/li/cil/oc/server/component/CPU.scala +++ b/src/main/scala/li/cil/oc/server/component/CPU.scala @@ -12,7 +12,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class CPU(val tier: Int) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Neighbors). diff --git a/src/main/scala/li/cil/oc/server/component/DataCard.scala b/src/main/scala/li/cil/oc/server/component/DataCard.scala index 6a14ee4e51..238098afc4 100644 --- a/src/main/scala/li/cil/oc/server/component/DataCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DataCard.scala @@ -29,7 +29,7 @@ import net.minecraft.nbt.CompoundNBT import org.apache.commons.codec.binary.Base64 import org.apache.commons.io.output.ByteArrayOutputStream -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ abstract class DataCard extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Neighbors). diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index 50b26191a1..b73a6c9338 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -69,7 +69,8 @@ import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistry import net.minecraftforge.registries.IForgeRegistry -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.{collectionAsScalaIterable, mapAsScalaMap} +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with DebugNode { diff --git a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala index 3f95438f6d..67e7aed668 100644 --- a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala @@ -31,7 +31,7 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.util.Direction import net.minecraft.util.Hand -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends AbstractManagedEnvironment with ItemStackInventory with ComponentInventory with RackMountable with Analyzable with DeviceInfo { // Stored for filling data packet when queried. diff --git a/src/main/scala/li/cil/oc/server/component/Drive.scala b/src/main/scala/li/cil/oc/server/component/Drive.scala index 5f971df817..0c64f54194 100644 --- a/src/main/scala/li/cil/oc/server/component/Drive.scala +++ b/src/main/scala/li/cil/oc/server/component/Drive.scala @@ -28,7 +28,7 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.world.storage.FolderName import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Option[EnvironmentHost], val sound: Option[String], val speed: Int, val isLocked: Boolean) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index 66126005e5..d08da82ea9 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -22,8 +22,8 @@ import net.minecraft.util.SoundEvents import net.minecraft.util.Direction import net.minecraft.util.SoundCategory -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class Drone(val agent: entity.Drone) extends AbstractManagedEnvironment with Agent with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/EEPROM.scala b/src/main/scala/li/cil/oc/server/component/EEPROM.scala index e65b216888..1fb8d05057 100644 --- a/src/main/scala/li/cil/oc/server/component/EEPROM.scala +++ b/src/main/scala/li/cil/oc/server/component/EEPROM.scala @@ -17,7 +17,7 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Neighbors). diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index 622e881189..1d27d14b52 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -29,7 +29,7 @@ import net.minecraft.nbt.IntArrayNBT import net.minecraft.nbt.ListNBT import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option[EnvironmentHost], val sound: Option[String], val speed: Int) extends AbstractManagedEnvironment with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala index 7ec82021ce..6225e8fecf 100644 --- a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala @@ -37,8 +37,9 @@ import net.minecraft.world.biome.Biome.RainType import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.MinecraftForge -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.mapAsJavaMap +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.language.existentials class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.WorldControl with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 2eaadc75e5..8bb1c3c1ff 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -16,7 +16,7 @@ import net.minecraft.nbt.{CompoundNBT, ListNBT} import li.cil.oc.common.component import li.cil.oc.common.component.GpuTextBuffer -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.util.matching.Regex // IMPORTANT: usually methods with side effects should *not* be direct diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index 2075a6e982..bf7794405f 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -34,8 +34,8 @@ import li.cil.oc.util.ThreadPoolFactory import net.minecraft.server.MinecraftServer import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class InternetCard extends AbstractManagedEnvironment with DeviceInfo { @@ -203,7 +203,6 @@ object InternetCard { selector.select() - import scala.collection.JavaConversions._ val selectedKeys = selector.selectedKeys val readableKeys = mutable.HashSet[SelectionKey]() selectedKeys.filter(_.isReadable).foreach(key => { diff --git a/src/main/scala/li/cil/oc/server/component/Keyboard.scala b/src/main/scala/li/cil/oc/server/component/Keyboard.scala index f5719dfcad..eeeb5b0148 100644 --- a/src/main/scala/li/cil/oc/server/component/Keyboard.scala +++ b/src/main/scala/li/cil/oc/server/component/Keyboard.scala @@ -17,7 +17,7 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import net.minecraft.entity.player.PlayerEntity -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable // TODO key up when screen is disconnected from which the key down came diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index 1795f9dfad..cf3ada270c 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -18,8 +18,8 @@ import li.cil.oc.common.Tier import li.cil.oc.server.network.QuantumNetwork import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class LinkedCard extends AbstractManagedEnvironment with QuantumNetwork.QuantumNode with DeviceInfo with traits.WakeMessageAware { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/Memory.scala b/src/main/scala/li/cil/oc/server/component/Memory.scala index ae0b167131..545400e097 100644 --- a/src/main/scala/li/cil/oc/server/component/Memory.scala +++ b/src/main/scala/li/cil/oc/server/component/Memory.scala @@ -12,7 +12,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Memory(val tier: Int) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Neighbors). diff --git a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala index 48a11fad2e..927dcd500a 100644 --- a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala @@ -22,8 +22,8 @@ import net.minecraft.potion.Effects import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceContext, RayTraceResult} import net.minecraft.util.math.vector.Vector3d -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvironment with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala index ce119b5e13..b8b2376321 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -23,8 +23,8 @@ import li.cil.oc.common.Tier import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.nbt._ -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class NetworkCard(val host: EnvironmentHost) extends AbstractManagedEnvironment with RackBusConnectable with DeviceInfo with traits.WakeMessageAware { diff --git a/src/main/scala/li/cil/oc/server/component/Redstone.scala b/src/main/scala/li/cil/oc/server/component/Redstone.scala index b6571aaf4c..4833882e5d 100644 --- a/src/main/scala/li/cil/oc/server/component/Redstone.scala +++ b/src/main/scala/li/cil/oc/server/component/Redstone.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.tileentity.traits.BundledRedstoneAware import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.server.component -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ object Redstone { diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala index 3987fc780e..8a087ddab1 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala @@ -13,7 +13,7 @@ import li.cil.oc.api.machine.Context import li.cil.oc.common.tileentity.traits.BundledRedstoneAware import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ trait RedstoneBundled extends RedstoneVanilla { private final lazy val deviceInfo = Map( diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala index eadfc5dd44..bcc2f7187a 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala @@ -19,7 +19,7 @@ import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.RotationHelper import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { def redstone: EnvironmentHost with RedstoneAware diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index 42af249a5e..c583bdd432 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -16,7 +16,7 @@ import li.cil.oc.integration.Mods import li.cil.oc.integration.util import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ trait RedstoneWireless extends RedstoneSignaller with DeviceInfo { def redstone: EnvironmentHost diff --git a/src/main/scala/li/cil/oc/server/component/Robot.scala b/src/main/scala/li/cil/oc/server/component/Robot.scala index 85a1cbf48e..1008b077a1 100644 --- a/src/main/scala/li/cil/oc/server/component/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/Robot.scala @@ -27,7 +27,7 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.particles.ParticleTypes import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with Agent with DeviceInfo { override val node = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index e55d138b35..dc918e6951 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -40,7 +40,7 @@ import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider import net.minecraftforge.common.util.LazyOptional -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment with MachineHost with ServerInventory with ComponentInventory with Analyzable with internal.Server with ICapabilityProvider with DeviceInfo { lazy val machine: api.machine.Machine = Machine.create(this) diff --git a/src/main/scala/li/cil/oc/server/component/Tablet.scala b/src/main/scala/li/cil/oc/server/component/Tablet.scala index d3848c8527..3bfe8ddffa 100644 --- a/src/main/scala/li/cil/oc/server/component/Tablet.scala +++ b/src/main/scala/li/cil/oc/server/component/Tablet.scala @@ -16,7 +16,7 @@ import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment import li.cil.oc.common.item.TabletWrapper -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class Tablet(val tablet: TabletWrapper) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/Trade.scala b/src/main/scala/li/cil/oc/server/component/Trade.scala index 6489ea5896..9f4d53e615 100644 --- a/src/main/scala/li/cil/oc/server/component/Trade.scala +++ b/src/main/scala/li/cil/oc/server/component/Trade.scala @@ -21,7 +21,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.registry.Registry import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.ref.WeakReference class Trade(val info: TradeInfo) extends AbstractValue { diff --git a/src/main/scala/li/cil/oc/server/component/Transposer.scala b/src/main/scala/li/cil/oc/server/component/Transposer.scala index ee91d6b4d6..b2a95beb45 100644 --- a/src/main/scala/li/cil/oc/server/component/Transposer.scala +++ b/src/main/scala/li/cil/oc/server/component/Transposer.scala @@ -18,7 +18,7 @@ import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.language.existentials object Transposer { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala b/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala index c11a0e778d..d30cf495a6 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala @@ -13,7 +13,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ // Note-to-self: this has a component to allow the robot telling it has the // upgrade. diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala b/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala index 9209c877f1..19f36dd26d 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeBarcodeReader.scala @@ -19,7 +19,7 @@ import net.minecraft.nbt.ListNBT import net.minecraft.util.Direction import net.minecraft.world.server.ServerWorld -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeBarcodeReader(val host: EnvironmentHost) extends AbstractManagedEnvironment with DeviceInfo { override val node = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeBattery.scala b/src/main/scala/li/cil/oc/server/component/UpgradeBattery.scala index 8f3d3ceb71..ca614dd792 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeBattery.scala @@ -12,7 +12,7 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeBattery(val tier: Int) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala index eea7fddc48..01f13a8f29 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala @@ -22,7 +22,7 @@ import net.minecraft.util.math.ChunkPos import net.minecraft.world.World import net.minecraft.world.server.ServerWorld -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeChunkloader(val host: EnvironmentHost) extends AbstractManagedEnvironment with DeviceInfo { override val node = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala index d4a0f67734..2b454c4248 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala @@ -22,7 +22,7 @@ import net.minecraft.inventory.{CraftResultInventory, IInventory} import net.minecraft.inventory.container.Container import net.minecraft.inventory.container.CraftingResultSlot -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala index a46e2a1d3e..05e33b04ac 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala @@ -22,7 +22,7 @@ import li.cil.oc.util.StackOption import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment with internal.Database with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala index 6cd299c625..df5f08f0a5 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala @@ -15,14 +15,14 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab.AbstractManagedEnvironment -import li.cil.oc.util.UpgradeExperience +import li.cil.oc.util.{UpgradeExperience => ExperienceUtil} import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.entity.item.ExperienceOrbEntity import net.minecraft.item.Items import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends AbstractManagedEnvironment with DeviceInfo { final val MaxLevel = 30 @@ -46,7 +46,7 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A var level = 0 - def xpForNextLevel: Double = UpgradeExperience.xpForLevel(level + 1) + def xpForNextLevel: Double = ExperienceUtil.xpForLevel(level + 1) def addExperience(value: Double) { if (level < MaxLevel) { @@ -66,7 +66,7 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A // xp(level) = base + (level * const) ^ exp // pow(xp(level) - base, 1/exp) / const = level val oldLevel = level - level = UpgradeExperience.calculateLevelFromExperience(experience) + level = ExperienceUtil.calculateLevelFromExperience(experience) if (node != null) { if (level != oldLevel) { updateClient() @@ -77,7 +77,7 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A @Callback(direct = true, doc = """function():number -- The current level of experience stored in this experience upgrade.""") def level(context: Context, args: Arguments): Array[AnyRef] = - result(UpgradeExperience.calculateExperienceLevel(level, experience)) + result(ExperienceUtil.calculateExperienceLevel(level, experience)) @Callback(doc = """function():boolean -- Tries to consume an enchanted item to add experience to the upgrade.""") def consume(context: Context, args: Arguments): Array[AnyRef] = { @@ -117,12 +117,12 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A override def saveData(nbt: CompoundNBT) { super.saveData(nbt) - UpgradeExperience.setExperience(nbt, experience) + ExperienceUtil.setExperience(nbt, experience) } override def loadData(nbt: CompoundNBT) { super.loadData(nbt) - experience = UpgradeExperience.getExperience(nbt) + experience = ExperienceUtil.getExperience(nbt) updateXpInfo() } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index 1e1f21ab3b..c1d11abad4 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -24,7 +24,7 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.util.text.ITextComponent import net.minecraftforge.common.ForgeHooks -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala index c9974de64b..c9d3cef93e 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala @@ -19,7 +19,7 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ object UpgradeInventoryController { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala index 623e727ab3..46f59b8a0e 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala @@ -26,8 +26,8 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.StringNBT import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with traits.WorldAware with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala index 6dbc6326c4..8d249309d4 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala @@ -22,7 +22,7 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3d -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ /** * Mostly stolen from {@link li.cil.oc.common.tileentity.Adapter} diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index 04310bc9f5..4a7b2b77f1 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -27,7 +27,7 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.util.Direction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala index 4a64029547..a3831fde2b 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala @@ -24,7 +24,7 @@ import net.minecraft.util.math.BlockPos import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.block.material.PushReaction -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ protected object PistonTraits { trait ExtendAware { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala index 7a256f9364..b1164c9678 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala @@ -28,7 +28,7 @@ import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.eventbus.api.Event -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { private final lazy val deviceInfo = Map( diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala index a3e2c7f6f8..0db2b903dc 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala @@ -17,7 +17,7 @@ import net.minecraft.util.Direction import net.minecraft.world.World import net.minecraft.world.biome.Biome.RainType -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeSolarGenerator(val host: EnvironmentHost) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala index 91cae7f751..84850fe079 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTank.scala @@ -17,7 +17,7 @@ import net.minecraftforge.fluids.IFluidTank import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction import net.minecraftforge.fluids.capability.templates.FluidTank -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ class UpgradeTank(val owner: EnvironmentHost, val capacity: Int) extends AbstractManagedEnvironment with IFluidTank with DeviceInfo { override val node = Network.newNode(this, Visibility.None).create() diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala index 12e8517b69..3402eca90d 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala @@ -17,7 +17,7 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ object UpgradeTankController { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala index 4bd6472d58..ff4065bd1f 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTractorBeam.scala @@ -22,8 +22,8 @@ import net.minecraft.entity.item.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.util.math.BlockPos -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ object UpgradeTractorBeam { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala index e2f1010370..6b408d3b6d 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTrading.scala @@ -21,8 +21,8 @@ import net.minecraft.entity.Entity import net.minecraft.entity.merchant.IMerchant import net.minecraft.util.math.vector.Vector3d -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class UpgradeTrading(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.WorldAware with DeviceInfo { diff --git a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala index f606906290..7f73a763d9 100644 --- a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala @@ -19,7 +19,7 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.language.implicitConversions abstract class WirelessNetworkCard(host: EnvironmentHost) extends NetworkCard(host) with WirelessEndpoint { diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala index f22f063f66..54487cf023 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala @@ -11,7 +11,7 @@ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.StackOption._ import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait InventoryAnalytics extends InventoryAware with NetworkAware { @Callback(doc = """function([slot:number]):table -- Get a description of the stack in the specified slot or the selected slot.""") diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala index bcfed8a7f8..18120e5878 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala @@ -16,7 +16,7 @@ import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.entity.item.ItemTossEvent import net.minecraftforge.eventbus.api.Event.Result -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRestricted { @Callback(doc = "function(side:number):boolean -- Compare the block on the specified side with the one in the selected slot. Returns true if equal.") diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala index 0a05d47268..422e295fcb 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala @@ -24,7 +24,7 @@ import net.minecraftforge.fluids.IFluidBlock import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.wrapper.InvWrapper -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait WorldAware { def position: BlockPosition diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala index 40720b6633..ffd3e2a557 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala @@ -15,7 +15,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraftforge.items.IItemHandler -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware { @Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the device.""") diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index 6ddfdb161d..b074308b4a 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -22,8 +22,9 @@ import net.minecraft.world.World import net.minecraftforge.items.CapabilityItemHandler import net.minecraftforge.items.IItemHandler -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.mapAsScalaMap +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.math.ScalaNumber diff --git a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala index cdb1f4ced5..1fb131edcd 100644 --- a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala +++ b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala @@ -11,7 +11,7 @@ import net.minecraft.nbt.CompoundNBT import net.minecraft.util.ResourceLocation import net.minecraftforge.registries.ForgeRegistries -import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable class ArgumentsImpl(val args: Seq[AnyRef]) extends Arguments { diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index cc91c7e81a..52298dff53 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -46,8 +46,9 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.Array.canBuildFrom -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.mapAsJavaMap +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable class Machine(val host: MachineHost) extends AbstractManagedEnvironment with machine.Machine with Runnable with DeviceInfo { @@ -152,7 +153,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac hasMemory = Option(architecture).fold(false)(_.recomputeMemory(components)) } - override def components: util.Map[String, String] = scala.collection.convert.WrapAsJava.mapAsJavaMap(_components) + override def components: util.Map[String, String] = mapAsJavaMap(_components) def componentCount: Int = (_components.foldLeft(0.0)((acc, entry) => entry match { case (_, name) => acc + (if (name != "filesystem") 1.0 else 0.25) diff --git a/src/main/scala/li/cil/oc/server/machine/luac/ComponentAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/ComponentAPI.scala index 8c724ff7a3..c947f56965 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/ComponentAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/ComponentAPI.scala @@ -3,7 +3,7 @@ package li.cil.oc.server.machine.luac import li.cil.oc.api.network.Component import li.cil.oc.util.ExtendedLuaState.extendLuaState -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class ComponentAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { def initialize() { diff --git a/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala index 6404f85b33..40d66a2aa3 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala @@ -7,7 +7,7 @@ import li.cil.oc.api.driver.item.Processor import li.cil.oc.api.network.Connector import li.cil.oc.util.ExtendedLuaState.extendLuaState -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class ComputerAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { def initialize() { diff --git a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala index fab5552439..be3d7e9dca 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala @@ -18,7 +18,7 @@ import li.cil.repack.com.naef.jnlua._ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ @Architecture.Name("Lua 5.2") class NativeLua52Architecture(machine: api.machine.Machine) extends NativeLuaArchitecture(machine) { diff --git a/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala index a52030c71d..ddc04a1dbf 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/UserdataAPI.scala @@ -14,7 +14,7 @@ import li.cil.oc.util.ExtendedLuaState.extendLuaState import net.minecraft.nbt.CompressedStreamTools import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class UserdataAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { def initialize() { diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/ComponentAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/ComponentAPI.scala index 8dfe93e412..3a94886355 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/ComponentAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/ComponentAPI.scala @@ -5,7 +5,7 @@ import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class ComponentAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { override def initialize() { diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala index 874c6a43cf..ecd43992c5 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala @@ -9,7 +9,7 @@ import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class ComputerAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { override def initialize() { diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala index 2dac043a50..202b3c6522 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/LuaJLuaArchitecture.scala @@ -19,7 +19,7 @@ import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ @Architecture.Name("LuaJ") class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture { diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/UserdataAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/UserdataAPI.scala index 336fc4eea5..187733fb9c 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/UserdataAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/UserdataAPI.scala @@ -8,7 +8,7 @@ import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ class UserdataAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { override def initialize() { diff --git a/src/main/scala/li/cil/oc/server/network/Component.scala b/src/main/scala/li/cil/oc/server/network/Component.scala index 1a347a5e46..28b8e5ca79 100644 --- a/src/main/scala/li/cil/oc/server/network/Component.scala +++ b/src/main/scala/li/cil/oc/server/network/Component.scala @@ -15,8 +15,8 @@ import li.cil.oc.server.machine.Machine import li.cil.oc.util.SideTracker import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ trait Component extends network.Component with Node { def visibility = _visibility diff --git a/src/main/scala/li/cil/oc/server/network/Node.scala b/src/main/scala/li/cil/oc/server/network/Node.scala index 82922574d8..37d43594fd 100644 --- a/src/main/scala/li/cil/oc/server/network/Node.scala +++ b/src/main/scala/li/cil/oc/server/network/Node.scala @@ -8,8 +8,8 @@ import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.{Node => ImmutableNode} import net.minecraft.nbt.CompoundNBT -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ trait Node extends ImmutableNode { def host: Environment diff --git a/src/main/scala/li/cil/oc/server/network/Waypoints.scala b/src/main/scala/li/cil/oc/server/network/Waypoints.scala index 3c1d3fb317..51e4f9064f 100644 --- a/src/main/scala/li/cil/oc/server/network/Waypoints.scala +++ b/src/main/scala/li/cil/oc/server/network/Waypoints.scala @@ -10,7 +10,7 @@ import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object Waypoints { diff --git a/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala b/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala index 61faeece03..97e7fc2a69 100644 --- a/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/WirelessNetwork.scala @@ -13,7 +13,7 @@ import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object WirelessNetwork { diff --git a/src/main/scala/li/cil/oc/util/Color.scala b/src/main/scala/li/cil/oc/util/Color.scala index 85b02125ad..a2ebbdf220 100644 --- a/src/main/scala/li/cil/oc/util/Color.scala +++ b/src/main/scala/li/cil/oc/util/Color.scala @@ -3,7 +3,7 @@ package li.cil.oc.util import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object Color { val rgbValues = Map( diff --git a/src/main/scala/li/cil/oc/util/DatabaseAccess.scala b/src/main/scala/li/cil/oc/util/DatabaseAccess.scala index 9802eec801..f62ffc7934 100644 --- a/src/main/scala/li/cil/oc/util/DatabaseAccess.scala +++ b/src/main/scala/li/cil/oc/util/DatabaseAccess.scala @@ -4,11 +4,11 @@ import li.cil.oc.api.network.Component import li.cil.oc.api.network.Node import li.cil.oc.server.component.UpgradeDatabase -import scala.collection.JavaConversions +import scala.collection.JavaConverters object DatabaseAccess { def databases(node: Node): Iterable[UpgradeDatabase] = - JavaConversions.iterableAsScalaIterable(node.network.nodes).collect { + JavaConverters.iterableAsScalaIterable(node.network.nodes).collect { case component: Component => component.host match { case db: UpgradeDatabase => db case _ => null diff --git a/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala b/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala index a7fa03ee9b..09df5b81b4 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala @@ -9,7 +9,7 @@ import li.cil.repack.com.naef.jnlua.JavaFunction import li.cil.repack.com.naef.jnlua.LuaState import li.cil.repack.com.naef.jnlua.LuaType -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.language.implicitConversions import scala.math.ScalaNumber diff --git a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala index 8552837e04..5c4fed26c6 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala @@ -6,7 +6,8 @@ import net.minecraft.nbt._ import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -import scala.collection.convert.WrapAsScala._ +import scala.collection.JavaConverters.mapAsScalaMap +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.language.implicitConversions import scala.language.reflectiveCalls diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 79bb9928b5..4dcc4e079d 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -20,7 +20,7 @@ import net.minecraftforge.items.IItemHandlerModifiable import net.minecraftforge.items.wrapper.InvWrapper import net.minecraftforge.items.wrapper.SidedInvWrapper -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ object InventoryUtils { diff --git a/src/main/scala/li/cil/oc/util/ItemUtils.scala b/src/main/scala/li/cil/oc/util/ItemUtils.scala index 527b35d00c..8eaf72bcd0 100644 --- a/src/main/scala/li/cil/oc/util/ItemUtils.scala +++ b/src/main/scala/li/cil/oc/util/ItemUtils.scala @@ -26,7 +26,7 @@ import net.minecraft.nbt.CompressedStreamTools import net.minecraft.nbt.CompoundNBT import net.minecraftforge.registries.ForgeRegistries -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable object ItemUtils { diff --git a/src/main/scala/li/cil/oc/util/ScalaClosure.scala b/src/main/scala/li/cil/oc/util/ScalaClosure.scala index 801003f7c7..56fca9c3a4 100644 --- a/src/main/scala/li/cil/oc/util/ScalaClosure.scala +++ b/src/main/scala/li/cil/oc/util/ScalaClosure.scala @@ -8,7 +8,7 @@ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs import li.cil.repack.org.luaj.vm2.lib.VarArgFunction -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.language.implicitConversions import scala.math.ScalaNumber diff --git a/src/main/scala/li/cil/oc/util/Tooltip.scala b/src/main/scala/li/cil/oc/util/Tooltip.scala index 465003a558..f2f4369de6 100644 --- a/src/main/scala/li/cil/oc/util/Tooltip.scala +++ b/src/main/scala/li/cil/oc/util/Tooltip.scala @@ -8,8 +8,8 @@ import net.minecraft.client.gui.FontRenderer import net.minecraft.util.text.CharacterManager.ISliceAcceptor import net.minecraft.util.text.Style -import scala.collection.convert.WrapAsJava._ -import scala.collection.convert.WrapAsScala._ +import scala.collection.convert.ImplicitConversionsToJava._ +import scala.collection.convert.ImplicitConversionsToScala._ object Tooltip { private val maxWidth = 220 From 2dc2f89ff51b73c165e40b274bc066f77d89347a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 18:07:17 +0200 Subject: [PATCH 006/159] Fix mods.toml using incorrect forge version --- build.gradle | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 0e2c9d9752..6880e77551 100644 --- a/build.gradle +++ b/build.gradle @@ -157,13 +157,11 @@ dependencies { } processResources { - inputs.property "version", project.simpleVersion - inputs.property "mcversion", config.minecraft.version - inputs.property "fversion", config.forge.version + def reducedForgeVer = config.forge.version.replaceAll(/(\d+\.\d+)(\.\d+)/, "\$1") from(sourceSets.main.resources.srcDirs) { duplicatesStrategy = "include" include 'META-INF/mods.toml' - expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'fversion':config.forge.version + expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'fversion':reducedForgeVer } from(sourceSets.main.resources.srcDirs) { duplicatesStrategy = "include" From 56ce2adbab41828b39dbce26776fc0f353175bba Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:08:53 +0200 Subject: [PATCH 007/159] Make OpenComputers a constructable mod class --- src/main/scala/li/cil/oc/OpenComputers.scala | 39 ++++++++++++------- src/main/scala/li/cil/oc/Settings.scala | 2 +- .../scala/li/cil/oc/common/item/Manual.scala | 2 +- .../server/machine/luac/LuaStateFactory.scala | 2 +- .../scala/li/cil/oc/util/UpdateCheck.scala | 4 +- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 6027ca7206..1ca752a110 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -14,22 +14,22 @@ import net.minecraftforge.fml.InterModComms import net.minecraftforge.fml.ModContainer import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import net.minecraftforge.fml.event.lifecycle._ import net.minecraftforge.fml.event.server._ +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext import net.minecraftforge.fml.network.simple.SimpleChannel import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger import scala.collection.convert.ImplicitConversionsToScala._ -@Mod(OpenComputers.ID) +@Mod.EventBusSubscriber(modid = OpenComputers.ID, bus = Bus.FORGE) object OpenComputers { final val ID = "opencomputers" final val Name = "OpenComputers" - val Version = ModLoadingContext.get.getActiveContainer.getModInfo.getVersion - final val log: Logger = LogManager.getLogger(Name) lazy val proxy: Proxy = { @@ -40,21 +40,18 @@ object OpenComputers { cls.getConstructor().newInstance().asInstanceOf[Proxy] } - val modContainer: ModContainer = ModLoadingContext.get.getActiveContainer - var channel: SimpleChannel = null - MinecraftForge.EVENT_BUS.register(this) + private var instance: Option[OpenComputers] = None + + def get = instance match { + case Some(oc) => oc + case _ => throw new IllegalStateException("not initialized") + } @Deprecated def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = proxy.openGui(player, guiId, world, x, y, z) - @SubscribeEvent - def commonInit(e: FMLCommonSetupEvent): Unit = { - proxy.preInit(e) - proxy.init(e) - } - @SubscribeEvent def serverStart(e: FMLServerStartingEvent): Unit = { ThreadPoolFactory.safePools.foreach(_.newThreadPool()) @@ -64,10 +61,26 @@ object OpenComputers { def serverStop(e: FMLServerStoppedEvent): Unit = { ThreadPoolFactory.safePools.foreach(_.waitForCompletion()) } +} + +@Mod(OpenComputers.ID) +class OpenComputers { + val modContainer: ModContainer = ModLoadingContext.get.getActiveContainer + + val Version = modContainer.getModInfo.getVersion + + FMLJavaModLoadingContext.get.getModEventBus.register(this) + OpenComputers.instance = Some(this) + + @SubscribeEvent + def commonInit(e: FMLCommonSetupEvent): Unit = { + OpenComputers.proxy.preInit(e) + OpenComputers.proxy.init(e) + } @SubscribeEvent def imc(e: InterModProcessEvent): Unit = InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) @SubscribeEvent - def loadComplete(e: FMLLoadCompleteEvent): Unit = proxy.postInit(e) + def loadComplete(e: FMLLoadCompleteEvent): Unit = OpenComputers.proxy.postInit(e) } diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 2fcfcb9a8e..b1b9ddd833 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -570,7 +570,7 @@ object Settings { // created by) against the current version to see if some hard changes // were made. If so, the new default values are copied over. private def patchConfig(config: Config, defaults: Config) = { - val modVersion = OpenComputers.modContainer.getModInfo.getVersion + val modVersion = OpenComputers.get.Version val prefix = "opencomputers." val configVersion = new DefaultArtifactVersion(if (config.hasPath(prefix + "version")) config.getString(prefix + "version") else "0.0.0") var patched = config diff --git a/src/main/scala/li/cil/oc/common/item/Manual.scala b/src/main/scala/li/cil/oc/common/item/Manual.scala index 497b26235b..ab57178a4d 100644 --- a/src/main/scala/li/cil/oc/common/item/Manual.scala +++ b/src/main/scala/li/cil/oc/common/item/Manual.scala @@ -21,7 +21,7 @@ import net.minecraftforge.api.distmarker.OnlyIn class Manual(val parent: Delegator) extends traits.Delegate { @OnlyIn(Dist.CLIENT) override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { - tooltip.add(new StringTextComponent(TextFormatting.DARK_GRAY.toString + "v" + OpenComputers.Version)) + tooltip.add(new StringTextComponent(TextFormatting.DARK_GRAY.toString + "v" + OpenComputers.get.Version)) super.tooltipLines(stack, world, tooltip, flag) } diff --git a/src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala b/src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala index 9cf0302a69..83cae8dd56 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala @@ -179,7 +179,7 @@ abstract class LuaStateFactory { return } - val tmpLibName = s"OpenComputersMod-${OpenComputers.Version}-$version-$libraryName" + val tmpLibName = s"OpenComputersMod-${OpenComputers.get.Version}-$version-$libraryName" val tmpBasePath = if (Settings.get.nativeInTmpDir) { val path = System.getProperty("java.io.tmpdir") if (path == null) "" diff --git a/src/main/scala/li/cil/oc/util/UpdateCheck.scala b/src/main/scala/li/cil/oc/util/UpdateCheck.scala index eea471a113..e22206653d 100644 --- a/src/main/scala/li/cil/oc/util/UpdateCheck.scala +++ b/src/main/scala/li/cil/oc/util/UpdateCheck.scala @@ -22,7 +22,7 @@ object UpdateCheck { private def initialize(): Option[Release] = { // Keep the version template split up so it's not replaced with the actual version... - if (Settings.get.updateCheck && OpenComputers.Version != ("@" + "VERSION" + "@")) { + if (Settings.get.updateCheck && OpenComputers.get.Version != ("@" + "VERSION" + "@")) { try { OpenComputers.log.info("Starting OpenComputers version check.") val reader = new JsonReader(new InputStreamReader(releasesUrl.openStream())) @@ -38,7 +38,7 @@ object UpdateCheck { if (candidates.nonEmpty) { val latest = candidates.maxBy(release => new ComparableVersion(release.tag_name.stripPrefix("v"))) val remoteVersion = new ComparableVersion(latest.tag_name.stripPrefix("v")) - val localVersion = new ComparableVersion(OpenComputers.modContainer.getModInfo.getVersion.toString) + val localVersion = new ComparableVersion(OpenComputers.get.Version.toString) if (remoteVersion.compareTo(localVersion) > 0) { OpenComputers.log.info(s"A newer version of OpenComputers is available: ${latest.tag_name}.") return Some(latest) From 54efa482a12ee219998659d0955d007964c609ac Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 20:18:52 +0200 Subject: [PATCH 008/159] Move block and item creation to correct events --- src/main/scala/li/cil/oc/OpenComputers.scala | 16 ++++++++++++++++ src/main/scala/li/cil/oc/common/Proxy.scala | 16 +++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 1ca752a110..f92f327f2e 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -2,9 +2,14 @@ package li.cil.oc import li.cil.oc.common.IMC import li.cil.oc.common.Proxy +import li.cil.oc.common.init.Blocks +import li.cil.oc.common.init.Items import li.cil.oc.util.ThreadPoolFactory +import net.minecraft.block.Block import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item import net.minecraft.world.World +import net.minecraftforge.event.RegistryEvent import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.RegisterCommandsEvent @@ -72,6 +77,17 @@ class OpenComputers { FMLJavaModLoadingContext.get.getModEventBus.register(this) OpenComputers.instance = Some(this) + @SubscribeEvent + def registerBlocks(e: RegistryEvent.Register[Block]) { + Blocks.init() + } + + @SubscribeEvent + def registerItems(e: RegistryEvent.Register[Item]) { + Items.init() + OpenComputers.proxy.initExtraTags() + } + @SubscribeEvent def commonInit(e: FMLCommonSetupEvent): Unit = { OpenComputers.proxy.preInit(e) diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 522141b400..8977252cfa 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -50,20 +50,18 @@ import scala.reflect.ClassTag @Deprecated class Proxy { + @Deprecated + def initExtraTags() { + OpenComputers.log.debug("Initializing additional OreDict entries.") + + tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, "chipDiamond", net.minecraft.item.Items.DIAMOND, "gemDiamond") + } + def preInit(e: FMLCommonSetupEvent) { Settings.load(FMLPaths.CONFIGDIR.get.resolve(Paths.get("opencomputers", "settings.conf")).toFile) MinecraftForge.EVENT_BUS.register(this) - OpenComputers.log.debug("Initializing blocks and items.") - - Blocks.init() - Items.init() - - OpenComputers.log.debug("Initializing additional OreDict entries.") - - tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, "chipDiamond", net.minecraft.item.Items.DIAMOND, "gemDiamond") - OpenComputers.log.info("Initializing OpenComputers API.") api.CreativeTab.instance = CreativeTab From f021125963fb23efd340c5c5085b2899d76687e2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 20:26:52 +0200 Subject: [PATCH 009/159] Fix crash due to missing Settings instance --- src/main/scala/li/cil/oc/OpenComputers.scala | 6 ++++++ src/main/scala/li/cil/oc/common/Proxy.scala | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index f92f327f2e..046fb7c497 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -1,5 +1,7 @@ package li.cil.oc +import java.nio.file.Paths + import li.cil.oc.common.IMC import li.cil.oc.common.Proxy import li.cil.oc.common.init.Blocks @@ -23,6 +25,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import net.minecraftforge.fml.event.lifecycle._ import net.minecraftforge.fml.event.server._ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext +import net.minecraftforge.fml.loading.FMLPaths import net.minecraftforge.fml.network.simple.SimpleChannel import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger @@ -77,6 +80,9 @@ class OpenComputers { FMLJavaModLoadingContext.get.getModEventBus.register(this) OpenComputers.instance = Some(this) + MinecraftForge.EVENT_BUS.register(OpenComputers.proxy) + Settings.load(FMLPaths.CONFIGDIR.get().resolve(Paths.get("opencomputers", "settings.conf")).toFile()) + @SubscribeEvent def registerBlocks(e: RegistryEvent.Register[Block]) { Blocks.init() diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 8977252cfa..f5ce69645b 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -1,6 +1,5 @@ package li.cil.oc.common -import java.nio.file.Paths import java.util.function.BiConsumer import java.util.function.Function import java.util.function.Predicate @@ -40,7 +39,6 @@ import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.RegistryEvent.MissingMappings import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.event.lifecycle._ -import net.minecraftforge.fml.loading.FMLPaths import net.minecraftforge.fml.network.NetworkEvent import net.minecraftforge.fml.network.NetworkRegistry import net.minecraftforge.registries.ForgeRegistries @@ -58,10 +56,6 @@ class Proxy { } def preInit(e: FMLCommonSetupEvent) { - Settings.load(FMLPaths.CONFIGDIR.get.resolve(Paths.get("opencomputers", "settings.conf")).toFile) - - MinecraftForge.EVENT_BUS.register(this) - OpenComputers.log.info("Initializing OpenComputers API.") api.CreativeTab.instance = CreativeTab From 5dcf92c1729f8061223c3b7e03be8c6ca66f7c20 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 20:42:28 +0200 Subject: [PATCH 010/159] Fix crash due to hard dependency on AE2 --- .../scala/li/cil/oc/common/EventHandler.scala | 18 ++++++++++-------- .../tileentity/traits/PowerAcceptor.scala | 2 +- .../traits/power/AppliedEnergistics2.scala | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 46dfd36374..c03e2445a0 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -115,14 +115,16 @@ object EventHandler { } } - def scheduleAE2Add(tileEntity: power.AppliedEnergistics2): Unit = { - if (SideTracker.isServer) pendingServer.synchronized { - tileEntity match { - case tile: IGridBlock => - pendingServer += (() => if (!tileEntity.isRemoved) { - tileEntity.getGridNode(AEPartLocation.INTERNAL).updateState() - }) - case _ => + object AE2 { + def scheduleAE2Add(tileEntity: power.AppliedEnergistics2): Unit = { + if (SideTracker.isServer) pendingServer.synchronized { + tileEntity match { + case tile: IGridBlock => + pendingServer += (() => if (!tileEntity.isRemoved) { + tileEntity.getGridNode(AEPartLocation.INTERNAL).updateState() + }) + case _ => + } } } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala index 6bcb8f90b7..dc91b37ad8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala @@ -2,4 +2,4 @@ package li.cil.oc.common.tileentity.traits trait PowerAcceptor extends power.Common - with power.AppliedEnergistics2 + // with power.AppliedEnergistics2 diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala index 65439add00..5e78937a19 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/power/AppliedEnergistics2.scala @@ -50,7 +50,7 @@ trait AppliedEnergistics2 extends Common with IGridHost { override def clearRemoved() { super.clearRemoved() - if (useAppliedEnergistics2Power) EventHandler.scheduleAE2Add(this) + if (useAppliedEnergistics2Power) EventHandler.AE2.scheduleAE2Add(this) } override def setRemoved() { From 6491940622c0eec2717efb9e37d1bd021a0229df Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:36:17 +0200 Subject: [PATCH 011/159] Fix crash due to hard dependency on ProjectRed --- .../traits/BundledRedstoneAware.scala | 6 +---- .../BundledProviderProjectRed.scala | 23 +++++++++++++++++++ .../projectred/ModProjectRed.scala | 2 ++ 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/li/cil/oc/integration/projectred/BundledProviderProjectRed.scala diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index 60dde87d0d..a39f609b6f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -12,7 +12,7 @@ import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import java.util -trait BundledRedstoneAware extends RedstoneAware with IBundledTile { +trait BundledRedstoneAware extends RedstoneAware { protected[tileentity] val _bundledInput: Array[Array[Int]] = Array.fill(6)(Array.fill(16)(-1)) @@ -183,8 +183,4 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile { nbt.setNewTagList(RednetInputTag, _rednetInput.view) } - - override def canConnectBundled(side: Int): Boolean = isOutputEnabled - - override def getBundledSignal(side: Int): Array[Byte] = getBundledOutput(Direction.from3DDataValue(side)).map(value => math.min(math.max(value, 0), 255).toByte) } diff --git a/src/main/scala/li/cil/oc/integration/projectred/BundledProviderProjectRed.scala b/src/main/scala/li/cil/oc/integration/projectred/BundledProviderProjectRed.scala new file mode 100644 index 0000000000..d453ae0eb8 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/projectred/BundledProviderProjectRed.scala @@ -0,0 +1,23 @@ +package li.cil.oc.integration.projectred + +import li.cil.oc.common.tileentity.traits.BundledRedstoneAware +import mrtjp.projectred.api.IBundledTileInteraction +import mrtjp.projectred.api.ProjectRedAPI +import net.minecraft.util.Direction +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World + +object BundledProviderProjectRed extends IBundledTileInteraction { + def install(): Unit = ProjectRedAPI.transmissionAPI.registerBundledTileInteraction(this) + + override def isValidInteractionFor(world: World, pos: BlockPos, side: Direction) = + world.getBlockEntity(pos).isInstanceOf[BundledRedstoneAware] + + override def canConnectBundled(world: World, pos: BlockPos, side: Direction): Boolean = + world.getBlockEntity(pos).asInstanceOf[BundledRedstoneAware].isOutputEnabled + + override def getBundledSignal(world: World, pos: BlockPos, side: Direction): Array[Byte] = { + val tileEntity = world.getBlockEntity(pos).asInstanceOf[BundledRedstoneAware] + tileEntity.getBundledOutput(side).map(value => math.min(math.max(value, 0), 255).toByte) + } +} diff --git a/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala index 714d87ced1..efecdc915e 100644 --- a/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala +++ b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala @@ -8,6 +8,7 @@ import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider import li.cil.oc.util.BlockPosition import mrtjp.projectred.api.ProjectRedAPI import net.minecraft.util.Direction +import net.minecraft.world.World object ModProjectRed extends ModProxy with RedstoneProvider { override def getMod = Mods.ProjectRedTransmission @@ -17,6 +18,7 @@ object ModProjectRed extends ModProxy with RedstoneProvider { api.IMC.registerWrenchToolCheck("li.cil.oc.integration.projectred.EventHandlerProjectRed.isWrench") BundledRedstone.addProvider(this) + BundledProviderProjectRed.install() } override def computeInput(pos: BlockPosition, side: Direction): Int = 0 From 1e30bbfad91c6baff41143af41f472bb76622b5e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:42:35 +0200 Subject: [PATCH 012/159] Fix crash due to illegal resource name --- src/main/scala/li/cil/oc/common/Proxy.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index f5ce69645b..5aa14aea80 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -52,7 +52,7 @@ class Proxy { def initExtraTags() { OpenComputers.log.debug("Initializing additional OreDict entries.") - tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, "chipDiamond", net.minecraft.item.Items.DIAMOND, "gemDiamond") + tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, OpenComputers.ID + ":chip_diamond", net.minecraft.item.Items.DIAMOND, "forge:gems/diamond") } def preInit(e: FMLCommonSetupEvent) { From 790f9f636a9f8053826d848d5aae0d3ae95aaf91 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:58:11 +0200 Subject: [PATCH 013/159] Fix crash due to changed property ABI --- .../oc/common/block/property/PropertyRotatable.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala index 02c82f1880..569ee9c44d 100644 --- a/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala +++ b/src/main/scala/li/cil/oc/common/block/property/PropertyRotatable.scala @@ -1,14 +1,14 @@ package li.cil.oc.common.block.property -import com.google.common.base.Predicate -import com.google.common.base.Predicates +import java.util.function.Predicate + import net.minecraft.state.DirectionProperty import net.minecraft.util.Direction import scala.collection.convert.ImplicitConversionsToJava._ object PropertyRotatable { - final val Facing = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL.asInstanceOf[Predicate[Direction]]) - final val Pitch = DirectionProperty.create("pitch", Predicates.in(Set(Direction.DOWN, Direction.UP, Direction.NORTH))) - final val Yaw = DirectionProperty.create("yaw", Direction.Plane.HORIZONTAL.asInstanceOf[Predicate[Direction]]) + final val Facing = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL) + final val Pitch = DirectionProperty.create("pitch", d => d.getAxis == Direction.Axis.Y || d == Direction.NORTH) + final val Yaw = DirectionProperty.create("yaw", Direction.Plane.HORIZONTAL) } From 16d0351270f9d9f01f561527e074fc636aa73015 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 26 Jul 2022 16:52:24 +0200 Subject: [PATCH 014/159] Fix crash because of wrong initialization order --- src/main/scala/li/cil/oc/OpenComputers.scala | 2 +- src/main/scala/li/cil/oc/client/Proxy.scala | 8 ++++---- src/main/scala/li/cil/oc/common/Proxy.scala | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 046fb7c497..2a46722bc4 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -82,6 +82,7 @@ class OpenComputers { MinecraftForge.EVENT_BUS.register(OpenComputers.proxy) Settings.load(FMLPaths.CONFIGDIR.get().resolve(Paths.get("opencomputers", "settings.conf")).toFile()) + OpenComputers.proxy.preInit() @SubscribeEvent def registerBlocks(e: RegistryEvent.Register[Block]) { @@ -96,7 +97,6 @@ class OpenComputers { @SubscribeEvent def commonInit(e: FMLCommonSetupEvent): Unit = { - OpenComputers.proxy.preInit(e) OpenComputers.proxy.init(e) } diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index e929f5be9b..7cfc6338bb 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -37,20 +37,20 @@ import net.minecraftforge.fml.network.NetworkRegistry @Deprecated private[oc] class Proxy extends CommonProxy { - override def preInit(e: FMLCommonSetupEvent) { - super.preInit(e) + override def preInit() { + super.preInit() api.API.manual = client.Manual MinecraftForge.EVENT_BUS.register(Textures) MinecraftForge.EVENT_BUS.register(NetSplitterModel) - - ModelInitialization.preInit() } override def init(e: FMLCommonSetupEvent) { super.init(e) + ModelInitialization.preInit() + CommonPacketHandler.clientHandler = PacketHandler ColorHandler.init() diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 5aa14aea80..c56ab41b42 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -55,7 +55,7 @@ class Proxy { tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, OpenComputers.ID + ":chip_diamond", net.minecraft.item.Items.DIAMOND, "forge:gems/diamond") } - def preInit(e: FMLCommonSetupEvent) { + def preInit() { OpenComputers.log.info("Initializing OpenComputers API.") api.CreativeTab.instance = CreativeTab From 2c6fa8b5fa706515a192e991b134be77b76d4f94 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 26 Jul 2022 19:53:00 +0200 Subject: [PATCH 015/159] Create EntityType for Drone --- src/main/scala/li/cil/oc/client/Proxy.scala | 16 ++++++--- .../scala/li/cil/oc/common/entity/Drone.scala | 22 ++---------- .../li/cil/oc/common/entity/EntityTypes.java | 34 +++++++++++++++++++ .../scala/li/cil/oc/common/item/Drone.scala | 2 +- .../li/cil/oc/server/component/Drone.scala | 2 +- 5 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/entity/EntityTypes.java diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 7cfc6338bb..60618a4ce6 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -1,5 +1,7 @@ package li.cil.oc.client +import com.mojang.blaze3d.systems.IRenderCall +import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.client @@ -17,6 +19,7 @@ import li.cil.oc.common.{PacketHandler => CommonPacketHandler} import li.cil.oc.common.{Proxy => CommonProxy} import li.cil.oc.common.component.TextBuffer import li.cil.oc.common.entity.Drone +import li.cil.oc.common.entity.EntityTypes import li.cil.oc.common.event.NanomachinesHandler import li.cil.oc.common.event.RackMountableRenderHandler import li.cil.oc.common.item.traits.Delegate @@ -55,7 +58,7 @@ private[oc] class Proxy extends CommonProxy { ColorHandler.init() - RenderingRegistry.registerEntityRenderingHandler(null, DroneRenderer) // TEMP + RenderingRegistry.registerEntityRenderingHandler(EntityTypes.DRONE, DroneRenderer) ClientRegistry.bindTileEntityRenderer(null, AdapterRenderer) // TEMP ClientRegistry.bindTileEntityRenderer(null, AssemblerRenderer) // TEMP @@ -87,12 +90,15 @@ private[oc] class Proxy extends CommonProxy { MinecraftForge.EVENT_BUS.register(TextBuffer) MinecraftForge.EVENT_BUS.register(MFUTargetRenderer) MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) - MinecraftForge.EVENT_BUS.register(Audio) MinecraftForge.EVENT_BUS.register(HologramRenderer) - MinecraftForge.EVENT_BUS.register(PetRenderer) - MinecraftForge.EVENT_BUS.register(Sound) - MinecraftForge.EVENT_BUS.register(TextBufferRenderCache) + + runOnRenderThread(() => MinecraftForge.EVENT_BUS.register(TextBufferRenderCache)) + } + + def runOnRenderThread(call: IRenderCall) { + if (RenderSystem.isOnRenderThreadOrInit) call.execute() + else RenderSystem.recordRenderCall(call) } override def getGuiHandler(): common.GuiHandler = client.GuiHandler diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index de6b59e3f9..881483abbe 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -34,6 +34,7 @@ import net.minecraft.block.BlockState import net.minecraft.block.material.Material import net.minecraft.entity.Entity import net.minecraft.entity.EntitySize +import net.minecraft.entity.EntityType import net.minecraft.entity.MoverType import net.minecraft.entity.Pose import net.minecraft.entity.item.ItemEntity @@ -75,7 +76,7 @@ object Drone { // internal.Rotatable is also in internal.Drone, but it wasn't since the start // so this is to ensure it is implemented here, in the very unlikely case that // someone decides to ship that specific version of the API. -class Drone(world: World) extends Entity(null, world) with MachineHost with internal.Drone with internal.Rotatable with Analyzable with Context { +class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, world) with MachineHost with internal.Drone with internal.Rotatable with Analyzable with Context { override def world: World = level // Some basic constants. @@ -86,14 +87,6 @@ class Drone(world: World) extends Entity(null, world) with MachineHost with inte val maxVelocity = 0.4f val maxInventorySize = 8 - @Deprecated - private val size = EntitySize.fixed(12 / 16f, 6 / 16f) - @Deprecated - override def getDimensions(pose: Pose) = size - - @Deprecated - override def fireImmune = true - // Rendering stuff, purely eyecandy. val targetFlapAngles: Array[Array[Float]] = Array.fill(4, 2)(0f) val flapAngles: Array[Array[Float]] = Array.fill(4, 2)(0f) @@ -226,15 +219,6 @@ class Drone(world: World) extends Entity(null, world) with MachineHost with inte targetZ = value.z.toFloat } - @Deprecated - def motionX = getDeltaMovement.x - - @Deprecated - def motionY = getDeltaMovement.y - - @Deprecated - def motionZ = getDeltaMovement.z - override def getVelocity = getDeltaMovement // ----------------------------------------------------------------------- // @@ -449,7 +433,7 @@ class Drone(world: World) extends Entity(null, world) with MachineHost with inte if (isRunning) { val toTarget = new Vector3d(targetX - getX, targetY - getY, targetZ - getZ) val distance = toTarget.length() - val velocity = new Vector3d(motionX, motionY, motionZ) + val velocity = getDeltaMovement if (distance > 0 && (distance > 0.005f || velocity.dot(velocity) > 0.005f)) { val acceleration = math.min(targetAcceleration.floatValue(), distance) / distance val velocityX = velocity.x + toTarget.x * acceleration diff --git a/src/main/scala/li/cil/oc/common/entity/EntityTypes.java b/src/main/scala/li/cil/oc/common/entity/EntityTypes.java new file mode 100644 index 0000000000..9dd5988aad --- /dev/null +++ b/src/main/scala/li/cil/oc/common/entity/EntityTypes.java @@ -0,0 +1,34 @@ +package li.cil.oc.common.entity; + +import li.cil.oc.OpenComputers; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.ObjectHolder; + +@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) +@ObjectHolder("opencomputers") +public final class EntityTypes { + public static final EntityType DRONE = null; + + @SubscribeEvent + public static void registerEntities(RegistryEvent.Register> e) { + register(e.getRegistry(), "drone", EntityType.Builder.of(Drone::new, EntityClassification.MISC) + .sized(12 / 16f, 6 / 16f).fireImmune()); + } + + private static void register(IForgeRegistry> registry, String name, EntityType.Builder builder) { + EntityType type = builder.build(name); + type.setRegistryName(new ResourceLocation(OpenComputers.ID(), name)); + registry.register(type); + } + + private EntityTypes() { + throw new Error(); + } +} diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 8318885708..614bc32155 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -52,7 +52,7 @@ class Drone(val parent: Delegator) extends traits.Delegate with CustomModel { override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get if (!world.isClientSide) { - val drone = new entity.Drone(world) + val drone = entity.EntityTypes.DRONE.create(world) player match { case fakePlayer: agent.Player => drone.ownerName = fakePlayer.agent.ownerName diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index d08da82ea9..c97f6ec589 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -97,7 +97,7 @@ class Drone(val agent: entity.Drone) extends AbstractManagedEnvironment with Age @Callback(doc = "function():number -- Get the current velocity in m/s.") def getVelocity(context: Context, args: Arguments): Array[AnyRef] = - result(math.sqrt(agent.motionX * agent.motionX + agent.motionY * agent.motionY + agent.motionZ * agent.motionZ) * 20) // per second + result(agent.getDeltaMovement.length * 20) // per second @Callback(doc = "function():number -- Get the maximum velocity, in m/s.") def getV1elocity(context: Context, args: Arguments): Array[AnyRef] = { From 48b7346916ce039c2a46f563e18250e8ce5c97c6 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:36:44 +0200 Subject: [PATCH 016/159] Create types for TileEntities --- src/main/scala/li/cil/oc/client/Proxy.scala | 36 +++--- .../li/cil/oc/common/block/Adapter.scala | 2 +- .../li/cil/oc/common/block/Assembler.scala | 2 +- .../scala/li/cil/oc/common/block/Cable.scala | 2 +- .../li/cil/oc/common/block/Capacitor.scala | 2 +- .../oc/common/block/CarpetedCapacitor.scala | 2 +- .../scala/li/cil/oc/common/block/Case.scala | 2 +- .../li/cil/oc/common/block/Charger.scala | 2 +- .../li/cil/oc/common/block/Disassembler.scala | 2 +- .../li/cil/oc/common/block/DiskDrive.scala | 2 +- .../li/cil/oc/common/block/Geolyzer.scala | 2 +- .../li/cil/oc/common/block/Hologram.scala | 2 +- .../li/cil/oc/common/block/Keyboard.scala | 2 +- .../cil/oc/common/block/Microcontroller.scala | 2 +- .../li/cil/oc/common/block/MotionSensor.scala | 2 +- .../li/cil/oc/common/block/NetSplitter.scala | 2 +- .../cil/oc/common/block/PowerConverter.scala | 2 +- .../oc/common/block/PowerDistributor.scala | 2 +- .../scala/li/cil/oc/common/block/Print.scala | 2 +- .../li/cil/oc/common/block/Printer.scala | 2 +- .../scala/li/cil/oc/common/block/Rack.scala | 2 +- .../scala/li/cil/oc/common/block/Raid.scala | 2 +- .../li/cil/oc/common/block/Redstone.scala | 2 +- .../scala/li/cil/oc/common/block/Relay.scala | 2 +- .../li/cil/oc/common/block/RobotProxy.scala | 4 +- .../scala/li/cil/oc/common/block/Screen.scala | 2 +- .../li/cil/oc/common/block/Transposer.scala | 2 +- .../li/cil/oc/common/block/Waypoint.scala | 2 +- .../li/cil/oc/common/tileentity/Adapter.scala | 3 +- .../cil/oc/common/tileentity/Assembler.scala | 3 +- .../li/cil/oc/common/tileentity/Cable.scala | 3 +- .../cil/oc/common/tileentity/Capacitor.scala | 3 +- .../common/tileentity/CarpetedCapacitor.scala | 3 +- .../li/cil/oc/common/tileentity/Case.scala | 7 +- .../li/cil/oc/common/tileentity/Charger.scala | 3 +- .../oc/common/tileentity/Disassembler.scala | 3 +- .../cil/oc/common/tileentity/DiskDrive.scala | 3 +- .../cil/oc/common/tileentity/Geolyzer.scala | 3 +- .../cil/oc/common/tileentity/Hologram.scala | 5 +- .../cil/oc/common/tileentity/Keyboard.scala | 3 +- .../common/tileentity/Microcontroller.scala | 3 +- .../oc/common/tileentity/MotionSensor.scala | 3 +- .../oc/common/tileentity/NetSplitter.scala | 3 +- .../oc/common/tileentity/PowerConverter.scala | 3 +- .../common/tileentity/PowerDistributor.scala | 3 +- .../li/cil/oc/common/tileentity/Print.scala | 10 +- .../li/cil/oc/common/tileentity/Printer.scala | 3 +- .../li/cil/oc/common/tileentity/Rack.scala | 5 +- .../li/cil/oc/common/tileentity/Raid.scala | 3 +- .../cil/oc/common/tileentity/Redstone.scala | 3 +- .../li/cil/oc/common/tileentity/Relay.scala | 3 +- .../li/cil/oc/common/tileentity/Robot.scala | 2 +- .../cil/oc/common/tileentity/RobotProxy.scala | 7 +- .../li/cil/oc/common/tileentity/Screen.scala | 5 +- .../oc/common/tileentity/TileEntityTypes.java | 121 ++++++++++++++++++ .../cil/oc/common/tileentity/Transposer.scala | 3 +- .../cil/oc/common/tileentity/Waypoint.scala | 3 +- 57 files changed, 236 insertions(+), 81 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 60618a4ce6..8c1bba6958 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -60,24 +60,24 @@ private[oc] class Proxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityTypes.DRONE, DroneRenderer) - ClientRegistry.bindTileEntityRenderer(null, AdapterRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, AssemblerRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, CaseRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, ChargerRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, DisassemblerRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, DiskDriveRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, GeolyzerRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, HologramRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, MicrocontrollerRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, NetSplitterRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, PowerDistributorRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, PrinterRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, RaidRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, RackRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, RelayRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, RobotRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, ScreenRenderer) // TEMP - ClientRegistry.bindTileEntityRenderer(null, TransposerRenderer) // TEMP + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ADAPTER, AdapterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ASSEMBLER, AssemblerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CASE, CaseRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CHARGER, ChargerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISASSEMBLER, DisassemblerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISK_DRIVE, DiskDriveRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.GEOLYZER, GeolyzerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.HOLOGRAM, HologramRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.MICROCONTROLLER, MicrocontrollerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.NET_SPLITTER, NetSplitterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.POWER_DISTRIBUTOR, PowerDistributorRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.PRINTER, PrinterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RAID, RaidRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RACK, RackRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RELAY, RelayRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ROBOT, RobotRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.SCREEN, ScreenRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.TRANSPOSER, TransposerRenderer) ClientRegistry.registerKeyBinding(KeyBindings.analyzeCopyAddr) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) diff --git a/src/main/scala/li/cil/oc/common/block/Adapter.scala b/src/main/scala/li/cil/oc/common/block/Adapter.scala index 386a535949..f7c2098ec5 100644 --- a/src/main/scala/li/cil/oc/common/block/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/block/Adapter.scala @@ -17,7 +17,7 @@ import net.minecraft.world.World class Adapter extends SimpleBlock with traits.GUI { override def guiType = GuiType.Adapter - override def newBlockEntity(world: IBlockReader) = new tileentity.Adapter() + override def newBlockEntity(world: IBlockReader) = new tileentity.Adapter(tileentity.TileEntityTypes.ADAPTER) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index 85da8f96c7..9b2896556e 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -14,5 +14,5 @@ class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateA override def guiType = GuiType.Assembler - override def newBlockEntity(world: IBlockReader) = new tileentity.Assembler() + override def newBlockEntity(world: IBlockReader) = new tileentity.Assembler(tileentity.TileEntityTypes.ASSEMBLER) } diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index 772fd672cd..be9812e9b9 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -42,7 +42,7 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Cable() + override def newBlockEntity(world: IBlockReader) = new tileentity.Cable(tileentity.TileEntityTypes.CABLE) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Capacitor.scala b/src/main/scala/li/cil/oc/common/block/Capacitor.scala index 2e79042be7..75670dfe8f 100644 --- a/src/main/scala/li/cil/oc/common/block/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/Capacitor.scala @@ -16,7 +16,7 @@ class Capacitor extends SimpleBlock { // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Capacitor() + override def newBlockEntity(world: IBlockReader) = new tileentity.Capacitor(tileentity.TileEntityTypes.CAPACITOR) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala index 30094ff704..946c0a40a2 100644 --- a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala @@ -4,5 +4,5 @@ import li.cil.oc.common.tileentity import net.minecraft.world.IBlockReader class CarpetedCapacitor extends Capacitor { - override def newBlockEntity(world: IBlockReader) = new tileentity.CarpetedCapacitor() + override def newBlockEntity(world: IBlockReader) = new tileentity.CarpetedCapacitor(tileentity.TileEntityTypes.CARPETED_CAPACITOR) } diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 98723c0fda..61d0ae143e 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -52,7 +52,7 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override def guiType = GuiType.Case - override def newBlockEntity(world: IBlockReader) = new tileentity.Case(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Case(tileentity.TileEntityTypes.CASE, tier) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Charger.scala b/src/main/scala/li/cil/oc/common/block/Charger.scala index 6ed3456658..57bd9d6b24 100644 --- a/src/main/scala/li/cil/oc/common/block/Charger.scala +++ b/src/main/scala/li/cil/oc/common/block/Charger.scala @@ -27,7 +27,7 @@ class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateA override def guiType = GuiType.Charger - override def newBlockEntity(world: IBlockReader) = new tileentity.Charger() + override def newBlockEntity(world: IBlockReader) = new tileentity.Charger(tileentity.TileEntityTypes.CHARGER) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index 3a263cbb0d..a1b1e65937 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -30,5 +30,5 @@ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.Sta override def guiType = GuiType.Disassembler - override def newBlockEntity(world: IBlockReader) = new tileentity.Disassembler() + override def newBlockEntity(world: IBlockReader) = new tileentity.Disassembler(tileentity.TileEntityTypes.DISASSEMBLER) } diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 6849c28c51..2d0888131c 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -40,7 +40,7 @@ class DiskDrive extends SimpleBlock with traits.GUI { override def guiType = GuiType.DiskDrive - override def newBlockEntity(world: IBlockReader) = new tileentity.DiskDrive() + override def newBlockEntity(world: IBlockReader) = new tileentity.DiskDrive(tileentity.TileEntityTypes.DISK_DRIVE) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala index 97751c48a6..a8f6914c15 100644 --- a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala @@ -5,5 +5,5 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World class Geolyzer extends SimpleBlock { - override def newBlockEntity(world: IBlockReader) = new tileentity.Geolyzer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Geolyzer(tileentity.TileEntityTypes.GEOLYZER) } diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index adb11e7bbc..1f1b37a829 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -38,5 +38,5 @@ class Hologram(val tier: Int) extends SimpleBlock { // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Hologram(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Hologram(tileentity.TileEntityTypes.HOLOGRAM, tier) } diff --git a/src/main/scala/li/cil/oc/common/block/Keyboard.scala b/src/main/scala/li/cil/oc/common/block/Keyboard.scala index d04afeb9d1..b6131b3496 100644 --- a/src/main/scala/li/cil/oc/common/block/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/block/Keyboard.scala @@ -59,7 +59,7 @@ class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5). // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Keyboard() + override def newBlockEntity(world: IBlockReader) = new tileentity.Keyboard(tileentity.TileEntityTypes.KEYBOARD) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index b71e885113..1b615f4be9 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -71,7 +71,7 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc override def energyThroughput: Double = Settings.get.caseRate(Tier.One) - override def newBlockEntity(world: IBlockReader) = new tileentity.Microcontroller() + override def newBlockEntity(world: IBlockReader) = new tileentity.Microcontroller(tileentity.TileEntityTypes.MICROCONTROLLER) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala index 2074c2d6fe..b155273e0a 100644 --- a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala @@ -5,5 +5,5 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World class MotionSensor extends SimpleBlock { - override def newBlockEntity(world: IBlockReader) = new tileentity.MotionSensor() + override def newBlockEntity(world: IBlockReader) = new tileentity.MotionSensor(tileentity.TileEntityTypes.MOTION_SENSOR) } diff --git a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala index 2beeea1914..f44dc20950 100644 --- a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala @@ -14,7 +14,7 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World class NetSplitter extends RedstoneAware { - override def newBlockEntity(world: IBlockReader) = new tileentity.NetSplitter() + override def newBlockEntity(world: IBlockReader) = new tileentity.NetSplitter(tileentity.TileEntityTypes.NET_SPLITTER) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index 12f75074ea..bb2cb24871 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -23,5 +23,5 @@ class PowerConverter extends SimpleBlock with traits.PowerAcceptor { override def energyThroughput: Double = Settings.get.powerConverterRate - override def newBlockEntity(world: IBlockReader) = new tileentity.PowerConverter() + override def newBlockEntity(world: IBlockReader) = new tileentity.PowerConverter(tileentity.TileEntityTypes.POWER_CONVERTER) } diff --git a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala index 9c714dfa71..6fb32fadc8 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala @@ -5,6 +5,6 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World class PowerDistributor extends SimpleBlock { - override def newBlockEntity(world: IBlockReader) = new tileentity.PowerDistributor() + override def newBlockEntity(world: IBlockReader) = new tileentity.PowerDistributor(tileentity.TileEntityTypes.POWER_DISTRIBUTOR) } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 6ba9d6d79c..2b0004f5b9 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -121,7 +121,7 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends // ----------------------------------------------------------------------- // - override def newBlockEntity(worldIn: IBlockReader) = new tileentity.Print() + override def newBlockEntity(worldIn: IBlockReader) = new tileentity.Print(tileentity.TileEntityTypes.PRINT) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala index 08b5f34525..d9fadf0260 100644 --- a/src/main/scala/li/cil/oc/common/block/Printer.scala +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -11,5 +11,5 @@ import net.minecraft.world.World class Printer extends SimpleBlock with traits.StateAware with traits.GUI { override def guiType = GuiType.Printer - override def newBlockEntity(world: IBlockReader) = new tileentity.Printer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Printer(tileentity.TileEntityTypes.PRINTER) } diff --git a/src/main/scala/li/cil/oc/common/block/Rack.scala b/src/main/scala/li/cil/oc/common/block/Rack.scala index 6563e5bafa..5b5aeee45a 100644 --- a/src/main/scala/li/cil/oc/common/block/Rack.scala +++ b/src/main/scala/li/cil/oc/common/block/Rack.scala @@ -31,7 +31,7 @@ class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAwar override def guiType = GuiType.Rack - override def newBlockEntity(world: IBlockReader) = new tileentity.Rack() + override def newBlockEntity(world: IBlockReader) = new tileentity.Rack(tileentity.TileEntityTypes.RACK) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index 6dd7192955..2e65e3f969 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -42,7 +42,7 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si override def guiType = GuiType.Raid - override def newBlockEntity(world: IBlockReader) = new tileentity.Raid() + override def newBlockEntity(world: IBlockReader) = new tileentity.Raid(tileentity.TileEntityTypes.RAID) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index 3a8f9d2bc2..8be44b651e 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -26,5 +26,5 @@ class Redstone extends RedstoneAware { // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Redstone() + override def newBlockEntity(world: IBlockReader) = new tileentity.Redstone(tileentity.TileEntityTypes.REDSTONE_IO) } diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala index 49e5fc5aa9..1dcada932a 100644 --- a/src/main/scala/li/cil/oc/common/block/Relay.scala +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -11,5 +11,5 @@ class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { override def energyThroughput = Settings.get.accessPointRate - override def newBlockEntity(world: IBlockReader) = new tileentity.Relay() + override def newBlockEntity(world: IBlockReader) = new tileentity.Relay(tileentity.TileEntityTypes.RELAY) } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 34397ebd1b..7d57bbbc5a 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -138,8 +138,8 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 override def newBlockEntity(world: IBlockReader): tileentity.RobotProxy = { moving.get match { - case Some(robot) => new tileentity.RobotProxy(robot) - case _ => new tileentity.RobotProxy() + case Some(robot) => new tileentity.RobotProxy(tileentity.TileEntityTypes.ROBOT, robot) + case _ => new tileentity.RobotProxy(tileentity.TileEntityTypes.ROBOT) } } diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 2ba022d9df..9386d859f7 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -52,7 +52,7 @@ class Screen(val tier: Int) extends RedstoneAware { // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Screen(tier) + override def newBlockEntity(world: IBlockReader) = new tileentity.Screen(tileentity.TileEntityTypes.SCREEN, tier) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Transposer.scala b/src/main/scala/li/cil/oc/common/block/Transposer.scala index c830c63b61..54a32daaca 100644 --- a/src/main/scala/li/cil/oc/common/block/Transposer.scala +++ b/src/main/scala/li/cil/oc/common/block/Transposer.scala @@ -8,5 +8,5 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World class Transposer extends SimpleBlock { - override def newBlockEntity(world: IBlockReader) = new tileentity.Transposer() + override def newBlockEntity(world: IBlockReader) = new tileentity.Transposer(tileentity.TileEntityTypes.TRANSPOSER) } diff --git a/src/main/scala/li/cil/oc/common/block/Waypoint.scala b/src/main/scala/li/cil/oc/common/block/Waypoint.scala index 3d09f57c3e..383d204c22 100644 --- a/src/main/scala/li/cil/oc/common/block/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/block/Waypoint.scala @@ -22,7 +22,7 @@ class Waypoint extends RedstoneAware { // ----------------------------------------------------------------------- // - override def newBlockEntity(world: IBlockReader) = new tileentity.Waypoint() + override def newBlockEntity(world: IBlockReader) = new tileentity.Waypoint(tileentity.TileEntityTypes.WAYPOINT) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala index 02cb094212..1639dd4bf5 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala @@ -21,6 +21,7 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.ListNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraftforge.common.util.Constants.NBT @@ -28,7 +29,7 @@ import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable -class Adapter extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { +class Adapter(selfType: TileEntityType[_ <: Adapter]) extends TileEntity(selfType) with traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { val node = api.Network.newNode(this, Visibility.Network).create() private val blocks = Array.fill[Option[(ManagedEnvironment, DriverBlock)]](6)(None) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala index 54d9ef7798..3297d7d91b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala @@ -20,13 +20,14 @@ import li.cil.oc.util.StackOption._ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Assembler extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { +class Assembler(selfType: TileEntityType[_ <: Assembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { val node = api.Network.newNode(this, Visibility.Network). withComponent("assembler"). withConnector(Settings.get.bufferConverter). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala index e2937cad71..43d08ba5fd 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala @@ -10,8 +10,9 @@ import li.cil.oc.util.ItemColorizer import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType -class Cable extends TileEntity(null) with traits.Environment with traits.NotAnalyzable with traits.ImmibisMicroblock with traits.Colored { +class Cable(selfType: TileEntityType[_ <: Cable]) extends TileEntity(selfType) with traits.Environment with traits.NotAnalyzable with traits.ImmibisMicroblock with traits.Colored { val node = api.Network.newNode(this, Visibility.None).create() setColor(Color.rgbValues(DyeColor.LIGHT_GRAY)) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala index bec03da366..06bfab5abb 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Capacitor.scala @@ -11,12 +11,13 @@ import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import scala.collection.convert.ImplicitConversionsToJava._ -class Capacitor extends TileEntity(null) with traits.Environment with DeviceInfo { +class Capacitor(selfType: TileEntityType[_ <: Capacitor]) extends TileEntity(selfType) with traits.Environment with DeviceInfo { // Start with maximum theoretical capacity, gets reduced after validation. // This is done so that we don't lose energy while loading. val node = api.Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala index 17e1a6122b..92fc496c09 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/CarpetedCapacitor.scala @@ -8,13 +8,14 @@ import li.cil.oc.api.driver.DeviceInfo.DeviceClass import li.cil.oc.Settings import net.minecraft.entity.LivingEntity import net.minecraft.entity.passive.{OcelotEntity, SheepEntity} +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.DamageSource import net.minecraft.util.Direction import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ -class CarpetedCapacitor extends Capacitor with traits.Tickable { +class CarpetedCapacitor(selfType: TileEntityType[_ <: CarpetedCapacitor]) extends Capacitor(selfType) with traits.Tickable { private final lazy val deviceInfo = Map( DeviceAttribute.Class -> DeviceClass.Power, DeviceAttribute.Description -> "Battery", diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala index e78016dc4d..2b00556142 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala @@ -20,15 +20,16 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Case(var tier: Int) extends TileEntity(null) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { - def this() = { - this(0) +class Case(selfType: TileEntityType[_ <: Case], var tier: Int) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { + def this(selfType: TileEntityType[_ <: Case]) = { + this(selfType, 0) // If no tier was defined when constructing this case, then we don't yet know the inventory size // this is set back to true when the nbt data is loaded isSizeInventoryReady = false diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index 79e8645a90..e45ea842cb 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -24,6 +24,7 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.particles.ParticleTypes import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.Util import net.minecraft.util.math.vector.Vector3d @@ -34,7 +35,7 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable -class Charger extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { +class Charger(selfType: TileEntityType[_ <: Charger]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index b8c4327d1f..62aa2d66c5 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -21,6 +21,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist @@ -30,7 +31,7 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -class Disassembler extends TileEntity(null) with traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo { +class Disassembler(selfType: TileEntityType[_ <: Disassembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo { val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() diff --git a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala index 1507c71428..feb4434339 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala @@ -25,13 +25,14 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class DiskDrive extends TileEntity(null) with traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { +class DiskDrive(selfType: TileEntityType[_ <: DiskDrive]) extends TileEntity(selfType) with traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { // Used on client side to check whether to render disk activity indicators. var lastAccess = 0L diff --git a/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala b/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala index ea5f551cd5..3cddb4f001 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Geolyzer.scala @@ -3,8 +3,9 @@ package li.cil.oc.common.tileentity import li.cil.oc.server.component import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType -class Geolyzer extends TileEntity(null) with traits.Environment { +class Geolyzer(selfType: TileEntityType[_ <: Geolyzer]) extends TileEntity(selfType) with traits.Environment { val geolyzer = new component.Geolyzer(this) def node = geolyzer.node diff --git a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala index de5a98615a..d022d12ba1 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala @@ -16,6 +16,7 @@ import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.vector.Vector3d @@ -25,8 +26,8 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable -class Hologram(var tier: Int) extends TileEntity(null) with traits.Environment with SidedEnvironment with Analyzable with traits.RotatableTile with traits.Tickable with DeviceInfo { - def this() = this(0) +class Hologram(selfType: TileEntityType[_ <: Hologram], var tier: Int) extends TileEntity(selfType) with traits.Environment with SidedEnvironment with Analyzable with traits.RotatableTile with traits.Tickable with DeviceInfo { + def this(selfType: TileEntityType[_ <: Hologram]) = this(selfType, 0) val node = api.Network.newNode(this, Visibility.Network). withComponent("hologram"). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala index 80c327e020..bf6d1e9f4a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala @@ -9,11 +9,12 @@ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Keyboard extends TileEntity(null) with traits.Environment with traits.Rotatable with traits.ImmibisMicroblock with SidedEnvironment with Analyzable { +class Keyboard(selfType: TileEntityType[_ <: Keyboard]) extends TileEntity(selfType) with traits.Environment with traits.Rotatable with traits.ImmibisMicroblock with SidedEnvironment with Analyzable { override def validFacings = Direction.values val keyboard = { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 3f4d924b8a..3b87a685b9 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -24,6 +24,7 @@ import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist @@ -32,7 +33,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.JavaConverters.asJavaIterable import scala.collection.convert.ImplicitConversionsToJava._ -class Microcontroller extends TileEntity(null) with traits.PowerAcceptor with traits.Hub with traits.Computer with ISidedInventory with internal.Microcontroller with DeviceInfo { +class Microcontroller(selfType: TileEntityType[_ <: Microcontroller]) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Hub with traits.Computer with ISidedInventory with internal.Microcontroller with DeviceInfo { val info = new MicrocontrollerData() override def node = null diff --git a/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala b/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala index 2c264bb58b..0f6218e8d2 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/MotionSensor.scala @@ -4,8 +4,9 @@ import li.cil.oc.api.network.Node import li.cil.oc.server.component import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType -class MotionSensor extends TileEntity(null) with traits.Environment with traits.Tickable { +class MotionSensor(selfType: TileEntityType[_ <: MotionSensor]) extends TileEntity(selfType) with traits.Environment with traits.Tickable { val motionSensor = new component.MotionSensor(this) def node: Node = motionSensor.node diff --git a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala index 24a1517f9e..7df8dadb93 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala @@ -14,6 +14,7 @@ import li.cil.oc.util.RotationHelper import net.minecraft.util.SoundEvents import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraftforge.api.distmarker.Dist @@ -22,7 +23,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable -class NetSplitter extends TileEntity(null) with traits.Environment with traits.OpenSides with traits.RedstoneAware with api.network.SidedEnvironment with DeviceInfo { +class NetSplitter(selfType: TileEntityType[_ <: NetSplitter]) extends TileEntity(selfType) with traits.Environment with traits.OpenSides with traits.RedstoneAware with api.network.SidedEnvironment with DeviceInfo { private lazy val deviceInfo: util.Map[String, String] = Map( DeviceAttribute.Class -> DeviceClass.Network, DeviceAttribute.Description -> "Ethernet controller", diff --git a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala index 26e4db0ff5..598fc534ef 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/PowerConverter.scala @@ -10,13 +10,14 @@ import li.cil.oc.api import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.network._ import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class PowerConverter extends TileEntity(null) with traits.PowerAcceptor with traits.Environment with traits.NotAnalyzable with DeviceInfo { +class PowerConverter(selfType: TileEntityType[_ <: PowerConverter]) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Environment with traits.NotAnalyzable with DeviceInfo { val node = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() diff --git a/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala b/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala index bbe585591c..83a5716dd0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/PowerDistributor.scala @@ -6,12 +6,13 @@ import li.cil.oc.api.network._ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class PowerDistributor extends TileEntity(null) with traits.Environment with traits.PowerBalancer with traits.NotAnalyzable { +class PowerDistributor(selfType: TileEntityType[_ <: PowerDistributor]) extends TileEntity(selfType) with traits.Environment with traits.PowerBalancer with traits.NotAnalyzable { val node = null private val nodes = Array.fill(6)(api.Network.newNode(this, Visibility.None). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index c1ed466447..8a9fb3a017 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -15,6 +15,7 @@ import li.cil.oc.util.ExtendedNBT._ import net.minecraft.util.SoundEvents import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraft.util.math.AxisAlignedBB @@ -26,9 +27,12 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) extends TileEntity(null) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { - def this() = this(None, None, None) - def this(canToggle: () => Boolean, scheduleUpdate: Int => Unit, onStateChange: () => Unit) = this(Option(canToggle), Option(scheduleUpdate), Option(onStateChange)) +class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) + extends TileEntity(selfType) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { + + def this(selfType: TileEntityType[_ <: Print]) = this(selfType, None, None, None) + def this(selfType: TileEntityType[_ <: Print], canToggle: () => Boolean, scheduleUpdate: Int => Unit, onStateChange: () => Unit) = + this(selfType, Option(canToggle), Option(scheduleUpdate), Option(onStateChange)) _isOutputEnabled = true diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index 6df4a17724..2402f00f11 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -22,6 +22,7 @@ import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraftforge.api.distmarker.Dist @@ -29,7 +30,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Printer extends TileEntity(null) with traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { +class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { val node: ComponentConnector = api.Network.newNode(this, Visibility.Network). withComponent("printer3d"). withConnector(Settings.get.bufferConverter). diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index 184e342ff8..4b084e1518 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -28,12 +28,15 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.IntArrayNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Rack extends TileEntity(null) with traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware { +class Rack(selfType: TileEntityType[_ <: Rack]) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Hub with traits.PowerBalancer + with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware { + var isRelayEnabled = false val lastData = new Array[CompoundNBT](getContainerSize) val hasChanged: Array[Boolean] = Array.fill(getContainerSize)(true) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 27db6cf3f4..1e46b81641 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -18,11 +18,12 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Raid extends TileEntity(null) with traits.Environment with traits.Inventory with traits.Rotatable with Analyzable { +class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable with Analyzable { val node = api.Network.newNode(this, Visibility.None).create() var filesystem: Option[FileSystem] = None diff --git a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala index b69e3b4467..23b657fba2 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Redstone.scala @@ -12,9 +12,10 @@ import li.cil.oc.server.component.RedstoneVanilla import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction -class Redstone extends TileEntity(null) with traits.Environment with traits.BundledRedstoneAware with traits.Tickable { +class Redstone(selfType: TileEntityType[_ <: Redstone]) extends TileEntity(selfType) with traits.Environment with traits.BundledRedstoneAware with traits.Tickable { val instance: RedstoneVanilla = if (BundledRedstone.isAvailable) new component.Redstone.Bundled(this) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index 8f892923e8..a0778964d3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -34,13 +34,14 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.Util import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Relay extends TileEntity(null) with traits.Hub with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { +class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) with traits.Hub with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { lazy final val WirelessNetworkCardTier1: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier1) lazy final val WirelessNetworkCardTier2: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier2) lazy final val LinkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 6801562fad..1a6a40d38b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -66,7 +66,7 @@ import scala.collection.mutable // robot moves we only create a new proxy tile entity, hook the instance of this // class that was held by the old proxy to it and can then safely forget the // old proxy, which will be cleaned up by Minecraft like any other tile entity. -class Robot extends TileEntity(null) with traits.Computer with traits.PowerInformation with traits.RotatableTile with IFluidHandler with internal.Robot with InventorySelection with TankSelection { +class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with traits.PowerInformation with traits.RotatableTile with IFluidHandler with internal.Robot with InventorySelection with TankSelection { var proxy: RobotProxy = _ val info = new RobotData() diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index f04258dcb8..6149c0281b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -29,14 +29,17 @@ import net.minecraft.inventory.ISidedInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.text.ITextComponent import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.IFluidTank -class RobotProxy(val robot: Robot) extends TileEntity(null) with traits.Computer with traits.PowerInformation with traits.RotatableTile with ISidedInventory with IFluidHandler with internal.Robot { - def this() = this(new Robot()) +class RobotProxy(selfType: TileEntityType[_ <: RobotProxy], val robot: Robot) extends TileEntity(selfType) + with traits.Computer with traits.PowerInformation with traits.RotatableTile with ISidedInventory with IFluidHandler with internal.Robot { + + def this(selfType: TileEntityType[_ <: RobotProxy]) = this(selfType, new Robot()) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 338d3c3e26..8c9b9f183e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -15,6 +15,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.projectile.ArrowEntity import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.math.AxisAlignedBB import net.minecraftforge.api.distmarker.Dist @@ -23,8 +24,8 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.mutable import scala.language.postfixOps -class Screen(var tier: Int) extends TileEntity(null) with traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] { - def this() = this(0) +class Screen(selfType: TileEntityType[_ <: Screen], var tier: Int) extends TileEntity(selfType) with traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] { + def this(selfType: TileEntityType[_ <: Screen]) = this(selfType, 0) // Enable redstone functionality. _isOutputEnabled = true diff --git a/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java b/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java new file mode 100644 index 0000000000..970a23f6d9 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java @@ -0,0 +1,121 @@ +package li.cil.oc.common.tileentity; + +import li.cil.oc.OpenComputers; +import li.cil.oc.Constants; +import li.cil.oc.api.Items; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.ObjectHolder; + +@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) +@ObjectHolder("opencomputers") +public final class TileEntityTypes { + public static final TileEntityType ADAPTER = null; + public static final TileEntityType ASSEMBLER = null; + public static final TileEntityType CABLE = null; + public static final TileEntityType CAPACITOR = null; + public static final TileEntityType CARPETED_CAPACITOR = null; + public static final TileEntityType CASE = null; + public static final TileEntityType CHARGER = null; + public static final TileEntityType DISASSEMBLER = null; + public static final TileEntityType DISK_DRIVE = null; + public static final TileEntityType GEOLYZER = null; + public static final TileEntityType HOLOGRAM = null; + public static final TileEntityType KEYBOARD = null; + public static final TileEntityType MICROCONTROLLER = null; + public static final TileEntityType MOTION_SENSOR = null; + public static final TileEntityType NET_SPLITTER = null; + public static final TileEntityType POWER_CONVERTER = null; + public static final TileEntityType POWER_DISTRIBUTOR = null; + public static final TileEntityType PRINT = null; + public static final TileEntityType PRINTER = null; + public static final TileEntityType RACK = null; + public static final TileEntityType RAID = null; + public static final TileEntityType REDSTONE_IO = null; + public static final TileEntityType RELAY = null; + // We use the RobotProxy instead of Robot here because those are the ones actually found in the world. + // Beware of TileEntityType.create for this as it will construct a new, empty robot. + public static final TileEntityType ROBOT = null; + public static final TileEntityType SCREEN = null; + public static final TileEntityType TRANSPOSER = null; + public static final TileEntityType WAYPOINT = null; + + @SubscribeEvent + public static void registerTileEntities(RegistryEvent.Register> e) { + register(e.getRegistry(), "adapter", TileEntityType.Builder.of(() -> new Adapter(ADAPTER), + Items.get(Constants.BlockName$.MODULE$.Adapter()).block())); + register(e.getRegistry(), "assembler", TileEntityType.Builder.of(() -> new Assembler(ASSEMBLER), + Items.get(Constants.BlockName$.MODULE$.Assembler()).block())); + register(e.getRegistry(), "cable", TileEntityType.Builder.of(() -> new Cable(CABLE), + Items.get(Constants.BlockName$.MODULE$.Cable()).block())); + register(e.getRegistry(), "capacitor", TileEntityType.Builder.of(() -> new Capacitor(CAPACITOR), + Items.get(Constants.BlockName$.MODULE$.Capacitor()).block())); + register(e.getRegistry(), "carpeted_capacitor", TileEntityType.Builder.of(() -> new CarpetedCapacitor(CARPETED_CAPACITOR), + Items.get(Constants.BlockName$.MODULE$.CarpetedCapacitor()).block())); + register(e.getRegistry(), "case", TileEntityType.Builder.of(() -> new Case(CASE), + Items.get(Constants.BlockName$.MODULE$.CaseCreative()).block(), + Items.get(Constants.BlockName$.MODULE$.CaseTier1()).block(), + Items.get(Constants.BlockName$.MODULE$.CaseTier2()).block(), + Items.get(Constants.BlockName$.MODULE$.CaseTier3()).block())); + register(e.getRegistry(), "charger", TileEntityType.Builder.of(() -> new Charger(CHARGER), + Items.get(Constants.BlockName$.MODULE$.Charger()).block())); + register(e.getRegistry(), "disassembler", TileEntityType.Builder.of(() -> new Disassembler(DISASSEMBLER), + Items.get(Constants.BlockName$.MODULE$.Disassembler()).block())); + register(e.getRegistry(), "disk_drive", TileEntityType.Builder.of(() -> new DiskDrive(DISK_DRIVE), + Items.get(Constants.BlockName$.MODULE$.DiskDrive()).block())); + register(e.getRegistry(), "geolyzer", TileEntityType.Builder.of(() -> new Geolyzer(GEOLYZER), + Items.get(Constants.BlockName$.MODULE$.Geolyzer()).block())); + register(e.getRegistry(), "hologram", TileEntityType.Builder.of(() -> new Hologram(HOLOGRAM), + Items.get(Constants.BlockName$.MODULE$.HologramTier1()).block(), + Items.get(Constants.BlockName$.MODULE$.HologramTier2()).block())); + register(e.getRegistry(), "keyboard", TileEntityType.Builder.of(() -> new Keyboard(KEYBOARD), + Items.get(Constants.BlockName$.MODULE$.Keyboard()).block())); + register(e.getRegistry(), "microcontroller", TileEntityType.Builder.of(() -> new Microcontroller(MICROCONTROLLER), + Items.get(Constants.BlockName$.MODULE$.Microcontroller()).block())); + register(e.getRegistry(), "motion_sensor", TileEntityType.Builder.of(() -> new MotionSensor(MOTION_SENSOR), + Items.get(Constants.BlockName$.MODULE$.MotionSensor()).block())); + register(e.getRegistry(), "net_splitter", TileEntityType.Builder.of(() -> new NetSplitter(NET_SPLITTER), + Items.get(Constants.BlockName$.MODULE$.NetSplitter()).block())); + register(e.getRegistry(), "power_converter", TileEntityType.Builder.of(() -> new PowerConverter(POWER_CONVERTER), + Items.get(Constants.BlockName$.MODULE$.PowerConverter()).block())); + register(e.getRegistry(), "power_distributor", TileEntityType.Builder.of(() -> new PowerDistributor(POWER_DISTRIBUTOR), + Items.get(Constants.BlockName$.MODULE$.PowerDistributor()).block())); + register(e.getRegistry(), "print", TileEntityType.Builder.of(() -> new Print(PRINT), + Items.get(Constants.BlockName$.MODULE$.Print()).block())); + register(e.getRegistry(), "printer", TileEntityType.Builder.of(() -> new Printer(PRINTER), + Items.get(Constants.BlockName$.MODULE$.Printer()).block())); + register(e.getRegistry(), "rack", TileEntityType.Builder.of(() -> new Rack(RACK), + Items.get(Constants.BlockName$.MODULE$.Rack()).block())); + register(e.getRegistry(), "raid", TileEntityType.Builder.of(() -> new Raid(RAID), + Items.get(Constants.BlockName$.MODULE$.Raid()).block())); + register(e.getRegistry(), "redstone_io", TileEntityType.Builder.of(() -> new Redstone(REDSTONE_IO), + Items.get(Constants.BlockName$.MODULE$.Redstone()).block())); + register(e.getRegistry(), "relay", TileEntityType.Builder.of(() -> new Relay(RELAY), + Items.get(Constants.BlockName$.MODULE$.Relay()).block())); + register(e.getRegistry(), "robot", TileEntityType.Builder.of(() -> new RobotProxy(ROBOT), + Items.get(Constants.BlockName$.MODULE$.Robot()).block())); + register(e.getRegistry(), "screen", TileEntityType.Builder.of(() -> new Screen(SCREEN), + Items.get(Constants.BlockName$.MODULE$.ScreenTier1()).block(), + Items.get(Constants.BlockName$.MODULE$.ScreenTier2()).block(), + Items.get(Constants.BlockName$.MODULE$.ScreenTier3()).block())); + register(e.getRegistry(), "transposer", TileEntityType.Builder.of(() -> new Transposer(TRANSPOSER), + Items.get(Constants.BlockName$.MODULE$.Transposer()).block())); + register(e.getRegistry(), "waypoint", TileEntityType.Builder.of(() -> new Waypoint(WAYPOINT), + Items.get(Constants.BlockName$.MODULE$.Waypoint()).block())); + } + + private static void register(IForgeRegistry> registry, String name, TileEntityType.Builder builder) { + TileEntityType type = builder.build(null); + type.setRegistryName(new ResourceLocation(OpenComputers.ID(), name)); + registry.register(type); + } + + private TileEntityTypes() { + throw new Error(); + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala index d542235949..a1957e9b54 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala @@ -3,8 +3,9 @@ package li.cil.oc.common.tileentity import li.cil.oc.server.component import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType -class Transposer extends TileEntity(null) with traits.Environment { +class Transposer(selfType: TileEntityType[_ <: Transposer]) extends TileEntity(selfType) with traits.Environment { val transposer = new component.Transposer.Block(this) def node = transposer.node diff --git a/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala b/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala index 92d6c0f278..0f69894c79 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Waypoint.scala @@ -11,11 +11,12 @@ import li.cil.oc.server.network.Waypoints import net.minecraft.nbt.CompoundNBT import net.minecraft.particles.ParticleTypes import net.minecraft.tileentity.TileEntity +import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Waypoint extends TileEntity(null) with traits.Environment with traits.Rotatable with traits.RedstoneAware with traits.Tickable { +class Waypoint(selfType: TileEntityType[_ <: Waypoint]) extends TileEntity(selfType) with traits.Environment with traits.Rotatable with traits.RedstoneAware with traits.Tickable { val node = api.Network.newNode(this, Visibility.Network). withComponent("waypoint"). create() From 1e6cea43bea9d69c8863a6c0b6be85bd1dbcd850 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 27 Jul 2022 22:05:53 +0200 Subject: [PATCH 017/159] Make mod jar executable by embedding dependencies --- build.gradle | 21 +++++++++++---------- build.properties | 3 ++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 6880e77551..f9c40a0a4e 100644 --- a/build.gradle +++ b/build.gradle @@ -72,21 +72,21 @@ minecraft { repositories { maven { - url "https://maven.cil.li/" + url "https://cursemaven.com" content { - includeGroup "li.cil.tis3d" + includeGroup "curse.maven" } } maven { - url "https://dvs1.progwml6.com/files/maven" + url "https://maven.cil.li/" content { - includeGroup "mezz.jei" + includeGroup "li.cil.tis3d" } } maven { - url "https://maven.tehnut.info/" + url "https://dvs1.progwml6.com/files/maven" content { - includeGroup "mcp.mobius.waila" + includeGroup "mezz.jei" } } maven { @@ -109,6 +109,7 @@ repositories { includeGroup "mekanism" } } + mavenCentral() } configurations { @@ -120,11 +121,11 @@ dependencies { var jeiSlug = "jei-${config.minecraft.version}" minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" - implementation "com.typesafe:config:1.2.1" - implementation "org.scala-lang:scala-library:2.12.+" + embedded "com.typesafe:config:1.2.1" + embedded "org.scala-lang:scala-library:2.12.+" compileOnly fg.deobf("li.cil.tis3d:tis3d-1.16.5-forge:${config.tis3d.version}") - compileOnly fg.deobf("mcp.mobius.waila:Hwyla:${config.hwyla.version}:api") + compileOnly fg.deobf("curse.maven:hwyla-${config.hwyla.projectId}:${config.hwyla.fileId}") compileOnly fg.deobf("org.squiddev:cc-tweaked-${config.minecraft.version}:${config.cct.version}") compileOnly ("appeng:appliedenergistics2:${config.ae2.version}:api") { @@ -181,7 +182,7 @@ allprojects { jar { configurations.embedded.each { dep -> from(project.zipTree(dep)) { - exclude 'META-INF', 'META-INF/**' + exclude '*', 'META-INF', 'META-INF/**' } } manifest { diff --git a/build.properties b/build.properties index 38a034c3e0..e3ebdfe583 100644 --- a/build.properties +++ b/build.properties @@ -7,7 +7,8 @@ mod.version=1.7.5 ae2.version=8.4.7 cct.version=1.100.5 -hwyla.version=1.10.11-B78_1.16.2 +hwyla.projectId=253449 +hwyla.fileId=3033595 jei.version=7.7.1.152 mekanism.version=1.16.5-10.1.2.457 tis3d.version=1.6.8+30 From faf5a8e2090c3e6bd394ef2fc5767172f1f7c134 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 28 Jul 2022 03:18:20 +0200 Subject: [PATCH 018/159] Add missing irrefutable matches where applicable --- .../li/cil/oc/client/renderer/block/ModelInitialization.scala | 2 +- src/main/scala/li/cil/oc/common/ComponentTracker.scala | 1 + src/main/scala/li/cil/oc/common/EventHandler.scala | 1 + src/main/scala/li/cil/oc/common/Loot.scala | 1 + src/main/scala/li/cil/oc/common/PacketHandler.scala | 1 + .../li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala | 2 ++ src/main/scala/li/cil/oc/common/tileentity/Print.scala | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 721101a70a..5ee780d3eb 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -222,7 +222,7 @@ object ModelInitialization { } registry.put(originalLocation, fake) } - case null => + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/ComponentTracker.scala b/src/main/scala/li/cil/oc/common/ComponentTracker.scala index 359e832b50..3caabb9979 100644 --- a/src/main/scala/li/cil/oc/common/ComponentTracker.scala +++ b/src/main/scala/li/cil/oc/common/ComponentTracker.scala @@ -50,6 +50,7 @@ abstract class ComponentTracker { @SubscribeEvent def onWorldUnload(e: WorldEvent.Unload): Unit = e.getWorld match { case world: World => clear(world) + case _ => } protected def clear(world: World): Unit = this.synchronized { diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index c03e2445a0..9e06e60595 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -510,6 +510,7 @@ object EventHandler { collect { case server: Server if server.machine != null => server.machine.stop() } }) } + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index e7c95be520..d41fcfd0e9 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -134,6 +134,7 @@ object Loot { } } } + case _ => } private def parseLootDisks(list: java.util.Properties, acc: mutable.ArrayBuffer[(ItemStack, Int)], external: Boolean) { diff --git a/src/main/scala/li/cil/oc/common/PacketHandler.scala b/src/main/scala/li/cil/oc/common/PacketHandler.scala index 3d4de3ff4d..2f5d3ec1a4 100644 --- a/src/main/scala/li/cil/oc/common/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/common/PacketHandler.scala @@ -59,6 +59,7 @@ object PacketHandler { // This will usually be stuff like typing while in screen GUIs. player match { case mp: ServerPlayerEntity => mp.resetLastActionTime() + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala index f42a883fe7..3d81c1b139 100644 --- a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala @@ -88,6 +88,7 @@ object ChunkloaderUpgradeHandler extends LoadingValidationCallback { } restoredTickets.clear() } + case _ => } // Note: it might be necessary to use pre move to force load the target chunk @@ -136,6 +137,7 @@ object ChunkloaderUpgradeHandler extends LoadingValidationCallback { loader.ticket = Some(centerChunk) } } + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index 8a9fb3a017..0c442238ac 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -76,6 +76,7 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo scheduleUpdate match { case Some(callback) => callback(delay) case _ if !getLevel.isClientSide => getLevel.asInstanceOf[ServerWorld].getBlockTicks.scheduleTick(getBlockPos, block, delay) + case _ => } } onStateChange.foreach(_.apply()) From cf22c7fba02e1a374f45bc14f394f8de69c58868 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 28 Jul 2022 17:43:11 +0200 Subject: [PATCH 019/159] Port language files to new JSON format --- .../assets/opencomputers/lang/de_DE.lang | 483 ----------------- .../assets/opencomputers/lang/de_de.json | 458 ++++++++++++++++ .../assets/opencomputers/lang/en_US.lang | 484 ----------------- .../assets/opencomputers/lang/en_us.json | 459 ++++++++++++++++ .../assets/opencomputers/lang/fr_FR.lang | 359 ------------- .../assets/opencomputers/lang/fr_fr.json | 339 ++++++++++++ .../assets/opencomputers/lang/it_IT.lang | 271 ---------- .../assets/opencomputers/lang/it_it.json | 259 +++++++++ .../assets/opencomputers/lang/pt_BR.lang | 490 ----------------- .../assets/opencomputers/lang/pt_PT.lang | 137 ----- .../assets/opencomputers/lang/pt_br.json | 465 ++++++++++++++++ .../assets/opencomputers/lang/pt_pt.json | 117 +++++ .../assets/opencomputers/lang/ru_RU.lang | 482 ----------------- .../assets/opencomputers/lang/ru_ru.json | 457 ++++++++++++++++ .../assets/opencomputers/lang/tr_TR.lang | 496 ------------------ .../assets/opencomputers/lang/tr_tr.json | 451 ++++++++++++++++ .../assets/opencomputers/lang/zh_CN.lang | 479 ----------------- .../assets/opencomputers/lang/zh_TW.lang | 468 ----------------- .../assets/opencomputers/lang/zh_cn.json | 454 ++++++++++++++++ .../assets/opencomputers/lang/zh_tw.json | 444 ++++++++++++++++ src/main/scala/li/cil/oc/Localization.scala | 6 +- .../li/cil/oc/common/block/SimpleBlock.scala | 2 +- .../oc/common/item/traits/SimpleItem.scala | 2 +- 23 files changed, 3907 insertions(+), 4155 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/lang/de_DE.lang create mode 100644 src/main/resources/assets/opencomputers/lang/de_de.json delete mode 100644 src/main/resources/assets/opencomputers/lang/en_US.lang create mode 100644 src/main/resources/assets/opencomputers/lang/en_us.json delete mode 100644 src/main/resources/assets/opencomputers/lang/fr_FR.lang create mode 100644 src/main/resources/assets/opencomputers/lang/fr_fr.json delete mode 100644 src/main/resources/assets/opencomputers/lang/it_IT.lang create mode 100644 src/main/resources/assets/opencomputers/lang/it_it.json delete mode 100644 src/main/resources/assets/opencomputers/lang/pt_BR.lang delete mode 100644 src/main/resources/assets/opencomputers/lang/pt_PT.lang create mode 100644 src/main/resources/assets/opencomputers/lang/pt_br.json create mode 100644 src/main/resources/assets/opencomputers/lang/pt_pt.json delete mode 100644 src/main/resources/assets/opencomputers/lang/ru_RU.lang create mode 100644 src/main/resources/assets/opencomputers/lang/ru_ru.json delete mode 100644 src/main/resources/assets/opencomputers/lang/tr_TR.lang create mode 100644 src/main/resources/assets/opencomputers/lang/tr_tr.json delete mode 100644 src/main/resources/assets/opencomputers/lang/zh_CN.lang delete mode 100644 src/main/resources/assets/opencomputers/lang/zh_TW.lang create mode 100644 src/main/resources/assets/opencomputers/lang/zh_cn.json create mode 100644 src/main/resources/assets/opencomputers/lang/zh_tw.json diff --git a/src/main/resources/assets/opencomputers/lang/de_DE.lang b/src/main/resources/assets/opencomputers/lang/de_DE.lang deleted file mode 100644 index 4cf64dd2c8..0000000000 --- a/src/main/resources/assets/opencomputers/lang/de_DE.lang +++ /dev/null @@ -1,483 +0,0 @@ -# German (de_DE) localization file -# Translations might not be 100% correct -# Suggestions for improvements are welcome - -# Blocks -tile.oc.adapter.name=Adapter -tile.oc.assembler.name=Elektronik-Werkbank -tile.oc.cable.name=Kabel -tile.oc.capacitor.name=Kondensator -tile.oc.carpetedcapacitor.name=Teppichkondensator -tile.oc.case1.name=Computergehäuse (Stufe 1) -tile.oc.case2.name=Computergehäuse (Stufe 2) -tile.oc.case3.name=Computergehäuse (Stufe 3) -tile.oc.caseCreative.name=Computergehäuse (Kreativ) -tile.oc.chameliumBlock.name=Chamälium-Block -tile.oc.charger.name=Ladestation -tile.oc.disassembler.name=Recycler -tile.oc.diskDrive.name=Diskettenlaufwerk -tile.oc.endstone.name=Endstein -tile.oc.geolyzer.name=Geolyzer -tile.oc.hologram1.name=Hologrammprojektor (Stufe 1) -tile.oc.hologram2.name=Hologrammprojektor (Stufe 2) -tile.oc.keyboard.name=Tastatur -tile.oc.microcontroller.name=Mikrocontroller -tile.oc.motionSensor.name=Bewegungsmelder -tile.oc.netSplitter.name=Net Splitter -tile.oc.powerConverter.name=Leistungswandler -tile.oc.powerDistributor.name=Stromverteiler -tile.oc.print.name=3D-Druck -tile.oc.printer.name=3D-Drucker -tile.oc.raid.name=Raid -tile.oc.redstone.name=Redstone-I/O -tile.oc.relay.name=Relais -tile.oc.robot.name=Roboter -tile.oc.robotAfterimage.name=Roboter -tile.oc.screen1.name=Bildschirm (Stufe 1) -tile.oc.screen2.name=Bildschirm (Stufe 2) -tile.oc.screen3.name=Bildschirm (Stufe 3) -tile.oc.rack.name=Serverschrank -tile.oc.transposer.name=Transposer -tile.oc.waypoint.name=Wegpunkt - -# Items -item.oc.AbstractBusCard.name=Abstrakter-Bus-Karte -item.oc.Acid.name=Grog -item.oc.ALU.name=Arithmetisch-logische Einheit (ALU) -item.oc.Analyzer.name=Messgerät -item.oc.APU0.name=Beschleunigter Prozessor (APU) (Stufe 1) -item.oc.APU1.name=Beschleunigter Prozessor (APU) (Stufe 2) -item.oc.APU2.name=Beschleunigter Prozessor (APU) (Kreativ) -item.oc.ArrowKeys.name=Pfeiltasten -item.oc.ButtonGroup.name=Tastengruppe -item.oc.CardBase.name=Kartenbasis -item.oc.Chamelium.name=Chamälium -item.oc.CircuitBoard.name=Leiterplatte -item.oc.ComponentBus0.name=Komponentenschnittstelle (Stufe 1) -item.oc.ComponentBus1.name=Komponentenschnittstelle (Stufe 2) -item.oc.ComponentBus2.name=Komponentenschnittstelle (Stufe 3) -item.oc.componentbus3.name=Komponentenschnittstelle (Kreativ) -item.oc.ControlUnit.name=Steuerwerk (CU) -item.oc.CPU0.name=Hauptprozessor (CPU) (Stufe 1) -item.oc.CPU1.name=Hauptprozessor (CPU) (Stufe 2) -item.oc.CPU2.name=Hauptprozessor (CPU) (Stufe 3) -item.oc.CuttingWire.name=Schneidedraht -item.oc.DataCard0.name=Datenkarte (Stufe 1) -item.oc.DataCard1.name=Datenkarte (Stufe 2) -item.oc.DataCard2.name=Datenkarte (Stufe 3) -item.oc.DebugCard.name=Debug-Karte -item.oc.Debugger.name=Netzwerk-Debugger -item.oc.DiamondChip.name=Diamantsplitter -item.oc.Disk.name=Platte -item.oc.DiskDriveMountable.name=Diskettenlaufwerk -item.oc.Drone.name=Drohne -item.oc.DroneCase0.name=Drohnengehäuse (Stufe 1) -item.oc.DroneCase1.name=Drohnengehäuse (Stufe 2) -item.oc.DroneCase3.name=Drohnengehäuse (Kreativ) -item.oc.eeprom.name=EEPROM -item.oc.FloppyDisk.name=Diskette -item.oc.GraphicsCard0.name=Grafikkarte (Stufe 1) -item.oc.GraphicsCard1.name=Grafikkarte (Stufe 2) -item.oc.GraphicsCard2.name=Grafikkarte (Stufe 3) -item.oc.HardDiskDrive0.name=Festplatte (Stufe 1) -item.oc.HardDiskDrive1.name=Festplatte (Stufe 2) -item.oc.HardDiskDrive2.name=Festplatte (Stufe 3) -item.oc.hoverBoots.name=Schwebestiefel -item.oc.InkCartridge.name=Tintenkartusche -item.oc.InkCartridgeEmpty.name=Tintenkartusche (Leer) -item.oc.InternetCard.name=Internetkarte -item.oc.Interweb.name=Internetz -item.oc.IronNugget.name=Eisennugget -item.oc.LinkedCard.name=Verknüpfte Karte -item.oc.Manual.name=OpenComputers-Handbuch -item.oc.Memory0.name=Speicher (Stufe 1) -item.oc.Memory1.name=Speicher (Stufe 1.5) -item.oc.Memory2.name=Speicher (Stufe 2) -item.oc.Memory3.name=Speicher (Stufe 2.5) -item.oc.Memory4.name=Speicher (Stufe 3) -item.oc.Memory5.name=Speicher (Stufe 3.5) -item.oc.Microchip0.name=Mikrochip (Stufe 1) -item.oc.Microchip1.name=Mikrochip (Stufe 2) -item.oc.Microchip2.name=Mikrochip (Stufe 3) -item.oc.MicrocontrollerCase0.name=Mikrocontroller-Gehäuse (Stufe 1) -item.oc.MicrocontrollerCase1.name=Mikrocontroller-Gehäuse (Stufe 2) -item.oc.MicrocontrollerCase3.name=Mikrocontroller-Gehäuse (Kreativ) -item.oc.Nanomachines.name=Nanomaschinen -item.oc.NetworkCard.name=Netzwerkkarte -item.oc.NumPad.name=Ziffernblock -item.oc.Present.name=Ein kleines Etwas... -item.oc.PrintedCircuitBoard.name=Gedruckte Leiterplatte (PCB) -item.oc.RawCircuitBoard.name=Leiterplattenrohling -item.oc.RedstoneCard0.name=Redstonekarte (Stufe 1) -item.oc.RedstoneCard1.name=Redstonekarte (Stufe 2) -item.oc.Server0.name=Server (Stufe 1) -item.oc.Server1.name=Server (Stufe 2) -item.oc.Server2.name=Server (Stufe 3) -item.oc.Server3.name=Server (Kreativ) -item.oc.Tablet.name=Tablet -item.oc.TabletCase0.name=Tablet-Gehäuse (Stufe 1) -item.oc.TabletCase1.name=Tablet-Gehäuse (Stufe 2) -item.oc.TabletCase3.name=Tablet-Gehäuse (Kreativ) -item.oc.Terminal.name=Fernbedienung -item.oc.TerminalServer.name=Terminalserver -item.oc.TexturePicker.name=Texturenwähler -item.oc.Transistor.name=Transistor -item.oc.UpgradeAngel.name=Schwebe-Upgrade -item.oc.UpgradeBattery0.name=Akku-Upgrade (Stufe 1) -item.oc.UpgradeBattery1.name=Akku-Upgrade (Stufe 2) -item.oc.UpgradeBattery2.name=Akku-Upgrade (Stufe 3) -item.oc.UpgradeChunkloader.name=Chunkloader-Upgrade -item.oc.UpgradeContainerCard0.name=Karten-Behälter (Stufe 1) -item.oc.UpgradeContainerCard1.name=Karten-Behälter (Stufe 2) -item.oc.UpgradeContainerCard2.name=Karten-Behälter (Stufe 3) -item.oc.UpgradeContainerUpgrade0.name=Upgrade-Behälter (Stufe 1) -item.oc.UpgradeContainerUpgrade1.name=Upgrade-Behälter (Stufe 2) -item.oc.UpgradeContainerUpgrade2.name=Upgrade-Behälter (Stufe 3) -item.oc.UpgradeCrafting.name=Crafting-Upgrade -item.oc.UpgradeDatabase0.name=Datenbank-Upgrade (Stufe 1) -item.oc.UpgradeDatabase1.name=Datenbank-Upgrade (Stufe 2) -item.oc.UpgradeDatabase2.name=Datenbank-Upgrade (Stufe 3) -item.oc.UpgradeExperience.name=Erfahrungs-Upgrade -item.oc.UpgradeGenerator.name=Generator-Upgrade -item.oc.UpgradeHover0.name=Schwebe-Upgrade (Stufe 1) -item.oc.UpgradeHover1.name=Schwebe-Upgrade (Stufe 2) -item.oc.UpgradeInventory.name=Inventar-Upgrade -item.oc.UpgradeInventoryController.name=Inventarbedienungs-Upgrade -item.oc.UpgradeLeash.name=Leinen-Upgrade -item.oc.UpgradeMF.name=MFU -item.oc.UpgradeNavigation.name=Navigations-Upgrade -item.oc.UpgradePiston.name=Kolben-Upgrade -item.oc.UpgradeSign.name=Schild-I/O-Upgrade -item.oc.UpgradeSolarGenerator.name=Solargenerator-Upgrade -item.oc.UpgradeTank.name=Tank-Upgrade -item.oc.UpgradeTankController.name=Tankbedienungs-Upgrade -item.oc.UpgradeTractorBeam.name=Traktorstrahl-Upgrade -item.oc.UpgradeTrading.name=Handels-Upgrade -item.oc.WirelessNetworkCard0.name=Drahtlosnetzwerkkarte (Stufe 1) -item.oc.WirelessNetworkCard1.name=Drahtlosnetzwerkkarte (Stufe 2) -item.oc.WorldSensorCard.name=Weltsensorkarte -item.oc.wrench.name=Schraubenziehschlüssel - -# Entities -entity.oc.Drone.name=Drohne - -# GUI -oc:gui.Analyzer.Address=§6Adresse§f: %s -oc:gui.Analyzer.AddressCopied=Adresse wurde in die Zwischenablage kopiert. -oc:gui.Analyzer.ChargerSpeed=§6Ladegeschwindigkeit§f: %s -oc:gui.Analyzer.ComponentName=§6Komponentenname§f: %s -oc:gui.Analyzer.Components=§6Anzahl verbundener Komponenten§f: %s -oc:gui.Analyzer.CopyToClipboard=In die Zwischenablage kopieren. -oc:gui.Analyzer.LastError=§6Letzter Fehler§f: %s -oc:gui.Analyzer.RobotName=§6Name§f: %s -oc:gui.Analyzer.RobotOwner=§6Besitzer§f: %s -oc:gui.Analyzer.RobotXp=§6Erfahrung§f: %s (Stufe %s) -oc:gui.Analyzer.StoredEnergy=§6Gespeicherte Energie§f: %s -oc:gui.Analyzer.TotalEnergy=§6Insgesamt gespeicherte Energie§f: %s -oc:gui.Analyzer.Users=§6Benutzer§f: %s -oc:gui.Analyzer.WirelessStrength=§6Signalstärke§f: %s -oc:gui.Assembler.Collect=Fertiges Gerät entnehmen -oc:gui.Assembler.Complexity=Komplexität: %s/%s -oc:gui.Assembler.InsertCase=Grundbaustein einlegen -oc:gui.Assembler.InsertCPU=CPU benötigt -oc:gui.Assembler.InsertRAM=RAM benötigt -oc:gui.Assembler.Progress=Fortschritt: %s%% (%s) -oc:gui.Assembler.Run=Zusammenbauen -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Grafikkarte -oc:gui.Assembler.Warning.Inventory=Inventar-Upgrade -oc:gui.Assembler.Warning.Keyboard=Tastatur -oc:gui.Assembler.Warning.OS=Bootbares Medium -oc:gui.Assembler.Warning.Screen=Bildschirm -oc:gui.Assembler.Warnings=§eWarnung§7: Empfohlene Komponenten fehlen. -oc:gui.Chat.NewVersion=Eine neue Version ist verfügbar: %s -oc:gui.Chat.TextureName=§7Texturname ist §a%s§f. -oc:gui.Chat.WarningClassTransformer=Es gab §cFehler§f beim Ausführen des Class-Transformers. Bitte melde dies, zusammen mit deiner (vollständigen!) FML §alatest.log§f/§afml-server-latest.log§f Log-Datei, danke! -oc:gui.Chat.WarningFingerprint=§cWARNUNG§f - ungültige Signatur! Sollte '§a%s§f' sein, aber war '§e%s§f'. Falls du kein Modder bist und die "deobfuscated"-Version des Mods benutzt, solltest du OpenComputers erneut herunterladen, da die JAR, die du benutzt, vermutlich modifiziert wurde. -oc:gui.Chat.WarningLink=Link konnte nicht geöffnet werden: %s -oc:gui.Chat.WarningLuaFallback=Die native Lua-Implementierung ist nicht verfügbar. Computer können ihren Ausführungszustand nicht speichern. Sie werden automatisch neu starten, sobald ein Chunk neu geladen wird. -oc:gui.Chat.WarningProjectRed=Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red. -oc:gui.Chat.WarningRecipes=Es gab Fehler beim Laden eines oder mehrerer Rezepte. Einige Gegenstände können unter Umständen nicht gefertigt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen. -oc:gui.Chat.WarningSimpleComponent=Eine Erweiterung (deine?) welche das §aSimpleComponent§f-Interface verwendet, hat etwas §efalsch gemacht§f. Komponentenlogik konnte nicht eingefügt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen. -oc:gui.Drive.Managed=Managed -oc:gui.Drive.Unmanaged=Unmanaged -oc:gui.Drive.ReadOnlyLock=Sperre -oc:gui.Drive.ReadOnlyLockWarning=§lNur Lesen§r Sperre. Kann nur entfernt werden, wenn das Laufwerk gelöscht wird. -oc:gui.Drive.Warning=§lWarnung§r: Umschalten der Modi führt zum Datenverlust. -oc:gui.Error.ComponentOverflow=Zu viele Komponenten sind mit dem Computer verbunden. -oc:gui.Error.InternalError=Interner Fehler, bitte sieh in der Logdatei nach. Das ist wahrscheinlich ein Bug. -oc:gui.Error.NoCPU=Im Computer ist kein Hauptprozessor (CPU) installiert. -oc:gui.Error.NoEnergy=Nicht genug Energie. -oc:gui.Error.NoRAM=Im Computer ist kein RAM installiert. -oc:gui.Error.OutOfMemory=Nicht genug Arbeitsspeicher. -oc:gui.Manual.Blocks=OpenComputers-Blöcke -oc:gui.Manual.Home=Startseite -oc:gui.Manual.Items=OpenComputers-Gegenstände -oc:gui.Manual.Warning.BlockMissing=Block nicht verfügbar. -oc:gui.Manual.Warning.ImageMissing=Bild nicht gefunden. -oc:gui.Manual.Warning.ItemMissing=Gegenstand nicht verfügbar. -oc:gui.Manual.Warning.OreDictMissing=Ore-Dictionary-Eintrag nicht verfügbar. -oc:gui.Raid.Warning=§4Platten werden beim Einsetzen[nl] gelöscht. Entfernen einer Platte[nl] löscht das gesamte Raid. -oc:gui.Robot.Power=Energie -oc:gui.Robot.TurnOff=Ausschalten -oc:gui.Robot.TurnOn=Einschalten[nl] §7Nutze ein Messgerät, um Fehler zu behandeln.§r -oc:gui.Rack.Back=Hinten -oc:gui.Rack.Bottom=Unten -oc:gui.Rack.Left=Links -oc:gui.Rack.None=Keine -oc:gui.Rack.Right=Rechts -oc:gui.Rack.Enabled=Aktiv -oc:gui.Rack.Disabled=Inaktiv -oc:gui.Rack.Top=Oben -oc:gui.Switch.PacketsPerCycle=Pakete / Zyklus -oc:gui.Switch.QueueSize=Puffergröße -oc:gui.Switch.TransferRate=Taktung -oc:gui.Terminal.InvalidKey=Ungültiger Schlüssel, vermutlich wurde eine andere Fernbedienung an den Server gebunden. -oc:gui.Terminal.OutOfRange=Kein Signal. - -# Containers -oc:container.adapter=Adapter -oc:container.case=Computer -oc:container.charger=Ladestation -oc:container.disassembler=Recycler -oc:container.diskdrive=Diskettenlaufwerk -oc:container.printer=Drucker -oc:container.raid=Raid -oc:container.relay=Relais -oc:container.server=Server -oc:container.rack=Serverschrank -oc:container.tabletwrapper=Tablet - -# Keybinds -key.clipboardPaste=Zwischenablage einfügen - -# Item / Block Tooltips -oc:tooltip.accesspoint=Verhält sich wie ein Switch, aber empfängt zusätzlich Drahtlosnachrichten und leitet Pakete aus dem Festnetz drahtlos weiter. -oc:tooltip.abstractbuscard=Erlaubt es, LIP-Pakete des Abstrakten Busses von §fStargateTech 2§7 zu senden und zu empfangen. -oc:tooltip.acid=Eine hochgiftige Möchtegernflüssigkeit, wird üblicherweise nur von gewissen Piraten konsumiert. Mag sich aber auch zu anderen Zwecken eignen. -oc:tooltip.adapter=Erlaubt es, Blöcke anzusteuern, die keine Komponentenblöcke sind, wie etwa reguläre Minecraft-Blöcke oder Blöcke anderer Mods. -oc:tooltip.alu=Zählt Zahlen zum Zeitvertreib. Klingt komisch, is aber so. -oc:tooltip.analyzer=Erlaubt es, Informationen über Blöcke anzuzeigen, wie zum Bleistift ihre §fAdresse§7 und ihren §fKomponentennamen§7.[nl] Erlaubt zudem, den Fehler anzuzeigen, der zu einem Computerabsturz geführt hat, falls der Computer nicht regulär heruntergefahren wurde. -oc:tooltip.apu=Eine CPU mit integrierter GPU (bzw. IGP), falls du unbedingt den zusätzlichen Kartenslot brauchst.[nl] Unterstützte Komponenten: §f%s§7[nl] Höchstauflösung: §f%sx%s§7[nl] Maximale Farbtiefe: §f%s§7[nl] Operationen/Tick: §f%s§7 -oc:tooltip.assembler=Erlaubt die Fertigung von Robotern und weiteren Geräten aus diversen anderen Computerteilen. -oc:tooltip.cable=Ein billiger Weg, verschiedene Blöcke miteinander zu verbinden. -oc:tooltip.capacitor=Speichert Energie für spätere Verwendung. Kann extrem schnell befüllt und entleert werden. -oc:tooltip.carpetedcapacitor=Speichert Energie für den späteren Gebrauch. Kann sehr schnell befüllt und entleert werden. Lädt auf, wenn Schafe oder Ozelots darauf laufen. -oc:tooltip.cardbase=Wie der Name schon sagt, werden alle Erweiterungskarten hieraus hergestellt. -oc:tooltip.case=Das Computergehäuse ist der essentielle Grundbaustein für einen Computer. §fErweiterungskarten§7, §fRAM§7 und §fFestplatten§7 können in einem Gehäuse installiert werden.[nl] Slots: §f%s§7 -oc:tooltip.chamelium=Rohmaterial für 3D-Drucke. Nicht zum Verzehr geeignet: kann zu Erblindung und zeitweiligem Verlust von Präsenz führen. -oc:tooltip.chameliumblock=Schick und sauber. Praktisch für gefärbte Teile in 3D-Drucken, oder einfach, um deine tolle Basis mit einem schlichten, gefärbten Block zu dekorieren. -oc:tooltip.charger=Lädt Roboter mit Energie aus Kondensatoren auf. Die Ladegeschwindigkeit hängt vom eingehenden §fRedstonesignal§7 ab, wobei kein Signal "nicht laden" und ein Signal mit maximaler Stärke "schnellstmöglich laden" heißt. Erlaubt es auch Tablets zu laden, und ermöglicht Zugriff auf Festplatten in Tablets. -oc:tooltip.circuitboard=Mühsam ernährt sich das Eichhörnchen. Wenn es groß wird, wird es mal eine gedruckte Leiterplatte. -oc:tooltip.controlunit=Klingt wichtig, ist es auch. Man baut daraus immerhin CPUs. Wie könnte es da nicht wichtig sein. -oc:tooltip.componentbus=Diese Erweiterung erlaubt es es Servern, mit noch mehr Komponenten gleichzeitig zu kommunizieren, ähnlich wie CPUs.[nl] Unterstützte Komponenten: §f%s§7 -oc:tooltip.cpu=Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?[nl] Unterstützte Komponenten: §f%s§7 -oc:tooltip.cpu.Architecture=Architektur: §f%s§7 -oc:tooltip.cuttingwire=Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht. -oc:tooltip.datacard0=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit. -oc:tooltip.datacard1=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit. -oc:tooltip.datacard2=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit. -oc:tooltip.debugcard=Kreativ-Modus-Gegenstand, erlaubt es die Welt zu manipulieren um das Testen zu erleichtern. Verwendung auf eigene Gefahr. -oc:tooltip.debugger=Erlaubt, Informationen über OCs internes Netzwerk auszugeben. Nur verwenden, wenn von einem Entwickler dazu aufgefordert. -oc:tooltip.diamondchip=Ein kleines Stück eines einst wunderschönen Diamanten. Er wird niemals wieder so sein, wie er war. -oc:tooltip.disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen! -oc:tooltip.disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Disketten und Festplatten zu fertigen. -oc:tooltip.diskdrive.CC=ComputerCraft-Disketten werden §aauch unterstützt§7. -oc:tooltip.diskdrive=Erlaubt es, Disketten zu lesen und zu beschreiben. Kann in Robotern installiert werden, um später Disketten einlegen zu können. -oc:tooltip.diskdrivemountable=Funktioniert genauso wie ein normales Diskettenlaufwerk, wird aber in einem Serverschrank installiert. -oc:tooltip.diskusage=Festplattennutzung: %s/%s Byte -oc:tooltip.disklocked=Gesperrt von %s -oc:tooltip.diskmodemanaged=Modus: Managed -oc:tooltip.diskmodeunmanaged=Modus: Unmanaged -oc:tooltip.drone=Drohnen sind schlichte, flinke Aufklärungseinheiten mit limitierter Inventargröße. -oc:tooltip.dronecase=Dieses Gehäuse wird verwendet, um Drohnen in der Elektronik-Werkbank zu bauen. Es bietet Platz für wenige Komponenten und kommt mit endsteinbetriebener Levitation. -oc:tooltip.eeprom=Kleiner, programmierbarer Speicher für das BIOS, das Computer zum starten verwenden. -oc:tooltip.fakeendstone=So gut wie echt, kann sogar schweben! -oc:tooltip.geolyzer=Erlaubt es die Härte der Blöcke in der Umgebung abzufragen. Hilfreich um Hologramme der Gegend zu erzeugen, oder um Erze zu entdecken. -oc:tooltip.graphicscard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.[nl] Höchstauflösung: §f%sx%s§7[nl] Maximale Farbtiefe: §f%s§7[nl] Operationen/Tick: §f%s§7 -oc:tooltip.hoverboots=Spring höher, fall tiefer, geh besser. Dies und noch mehr bekommst du mit den neuen, patentierten Schwebestiefeln (TM). -oc:tooltip.inkcartridge=Wird verwendet, um 3D-Drucker zu befüllen. Aus mysteriösen Gründen muss die Kartusche nicht fest im Drucker eingebaut werden. -oc:tooltip.inkcartridgeempty=Diese Kartusche wurde trocken gelegt. Fülle sie mit Farben wieder auf. Oder wirf sie weg. Mir doch egal. -oc:tooltip.internetcard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP-Sockets zu verwenden. -oc:tooltip.interweb=Kann in einer Internetkarte verwendet werden, um Computer mit dem rechtsfreien Raum kommunizieren zu lassen. -oc:tooltip.ironnugget=Ein Nugget, das aus Eisen besteht, darum wird es ja auch Eisennugget genannt, duh... -oc:tooltip.keyboard=Kann an Bildschirmen befestigt werden, um auf ihnen zu tippen. -oc:tooltip.hologram0=Ein Volumendisplay, das beliebige, von Computern festgelegte Voxelstrukturen anzeigt.[nl] Auflösung: §f48x32x48§7 [nl] Maximale Skalierung: §f3x§7 [nl] Farbtiefe: §fMonochrom§7 -oc:tooltip.hologram1=Ein Volumendisplay, das beliebige, von Computern festgelegte Voxelstrukturen anzeigt.[nl] Auflösung: §f48x32x48§7 [nl] Maximale Skalierung: §f4x§7 [nl] Farbtiefe: §fTricolor§7 -oc:tooltip.linkedcard=Diese Karten werden paarweise hergestellt, und können ausschließlich mit ihrer jeweilgen Partnerkarte kommunizieren, dafür allerdings über beliebige Entfernungen, und sogar über Dimensionen hinweg. Der Nachteil ist der hohe Energieverbrauch beim Senden einer Nachricht. -oc:tooltip.linkedcard_channel=§8Kanal: %s§7 -oc:tooltip.manual=Alles, was es über OpenComputers zu wissen gibt. Und mehr. Und das alles zu dem unglaublich niedrigen Preis von... §obitte R dücken, um fortzufahren§7. -oc:tooltip.materialcosts=Halte [§f%s§7] gedrückt für Materialkosten. -oc:tooltip.materials=Materialien: -oc:tooltip.memory=Braucht ein jeder Computer, um zu starten. Je mehr vorhanden, desto komplexere Programme können ausgeführt werden. -oc:tooltip.microchip=Tritt auch unter dem Alias Integrierter Schaltkreis auf. Keine Ahnung, warum das auch mit Redstone klappt, aber es funktioniert. -oc:tooltip.microcontroller=Mikrocontroller sind möglichst einfache Computer. Sie sind dazu gedacht, für Spezialfälle verwendet zu werden, bei denen sie einfach das Programm auf dem in ihnen eingebauten EEPROM ausführen sollen. -oc:tooltip.microcontrollercase=Dieses Gehäuse wird verwendet, um Mikrocontroller in der Elektronik-Werkbank zu bauen. Platziere es in eine solche und füge weitere Komponenten hinzu, um einen Mikrocontroller zu erstellen. -oc:tooltip.motionsensor=Kann Bewegungen sich in der Nähe befindender Lebewesen erkennen. Benötigt eine klare Sichtlinie. -oc:tooltip.nanomachines=Kontrolleinheit und eine Menge Nanomaschinen zur Einnahme, sofern du dich traust. -oc:tooltip.networkcard=Erlaubt es Computern, die über mehrere Blöcke miteinander verbunden sind (z.B. Kabel), mittels Netzwerknachrichten zu kommunizieren. -oc:tooltip.poweracceptor=Energiewandelgeschwindigkeit: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fFactorization Charge§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Wandelt Strom anderer Mods in den internen Energietyp um. Umwandlungsverhältnisse: -oc:tooltip.powerdistributor=Verteilt Energie zwischen mehreren Netzwerken. Nützlich, um Energie mit einem Wandler auf mehrere Subnetze, die voneinander getrennt sein sollen, zu verteilen. -oc:tooltip.present=... für deine Mühen. Öffne dieses Geschenk für eine Chance, §kdolle Dinge§7 zu erhalten![nl]§8Fertige OpenComputers-Gegenstände zur rechten Zeit, und du hast die Chance ein Geschenk zu erhalten.§7 -oc:tooltip.print.BeaconBase=§8Funktioniert als Grundstein für ein Leuchtfeuer. -oc:tooltip.print.LightValue=§8Leuchtkraft: %s. -oc:tooltip.print.RedstoneLevel=§8Redstone-Signalstärke: %s. -oc:tooltip.printedcircuitboard=Hierauf basieren alle Erweiterungskarten, Speicher und so weiter. -oc:tooltip.printer=Erlaubt es, Blöcke aus benutzerdefinierten Formen zu drucken. Verwendet Chamälium und Tintenkartuschen. Muss über einen Computer konfiguriert werden. Von Kindern fern halten. Weil halt. -oc:tooltip.raid=Erlaubt es, drei Festplatten zu einem größeren Dateisystem zusammenzuschließen, welches von allen verbundenen Computern genutzt werden kann. -oc:tooltip.rawcircuitboard=Kann in einer Ofen-kompatiblen Schmelze freier Wahl gehärtet werden. -oc:tooltip.redstone=Erlaubt das Lesen und Ausgeben von Redstonesignalen um den Block herum. Kann von jedem Computer, der mit dem Block verbunden ist, gesteuert werden. Dieser Block ist quasi wie eine externe Redstonekarte. -oc:tooltip.redstonecard.ProjectRed=§fProjectRed§7 wird §aunterstützt§7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 wird §aunterstützt§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 wird §aunterstützt§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 wird §aunterstützt§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 wird §aunterstützt§7. -oc:tooltip.redstonecard=Erlaubt das Lesen und Ausgeben von Redstonesignalen um den Computer oder Roboter herum. -oc:tooltip.relay=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten "hinter" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.b. mittels Netzwerkkarten. -oc:tooltip.robot=Im Gegensatz zu normalen Computern können sich Roboter in der Welt bewegen und ähnlich wie Spieler mit der Welt interagieren. Sie können jedoch §onicht§r§7 mit externen Komponenten interagieren! -# The underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fStufe§7: §a%s§7. -oc:tooltip.robot_storedenergy=§fGespeicherte Energie§7: §a%s§7. -oc:tooltip.screen=Zeigt Text an, gesteuert von Grafikkarten in Computern.[nl] Höchstauflösung: §f%sx%s§7[nl] Maximale Farbtiefe: §f%s§7 -oc:tooltip.server=Ein Server kann wie ein gewöhnliches Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten, muss er in einem Servergehäuse installiert werden. -oc:tooltip.server.Components=Installierte Komponenten: -oc:tooltip.rack=Erlaubt die Installation von bis zu vier Servern oder anderen Serverschrankkomponenten. -oc:tooltip.switch=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten "hinter" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.B. mittels Netzwerkkarten. -oc:tooltip.tablet=Ein Tablet-PC, für Lua unterwegs. Kann durch Sneak-Aktivierung zwangsgestoppt werden. -oc:tooltip.tabletcase=Einfaches Gehäuse für Tablet-PCs. Kann in der Elektronik-Werkbank mit Komponenten bestückt werden, um einen Tablet-PC zu fertigen. -oc:tooltip.terminal=Erlaubt es, einen Server aus der Ferne zu steuern, so lange man sich in Reichweite des Servers befindet. Verhält sich wie Bildschirm und Tastatur in einem. Kann mit Shift-Rechtsklick an einen Server in einem Serverschrank gebunden werden. -oc:tooltip.terminalserver=Das Rückgrat für Fernzugriff. Fernbedienungen können hiermit verbunden werden. Enthält virtuelle Tastatur und Bildschirm. -oc:tooltip.texturepicker=Dieses Werkzeug erlaubt es, eine Zeichenkette anzuzeigen, die die Oberfläche eines Blocks beschreibt und in Form-Definitionen für 3D-Drucker verwendet werden kann. Definiv keine Texturnamen, oh nein. Nix da. -oc:tooltip.tier=§8Stufe %s -oc:tooltip.netsplitter=Kann Netzwerke dynamisch verbinden. Die Konnektivität jeder Seite kann umgeschaltet werden, in dem man "Professionelle Wartungsarbeiten" mit einem Schraubenschlüssel durchführt. Sie kann auch mit einem Redstone-Signal an entsprechenden Seiten invertiert werden. -oc:tooltip.toolong=Halte [§f%s§7] gedrückt für mehr Infos. -oc:tooltip.transistor=Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren. -oc:tooltip.transposer=Ermöglicht automatischen Transfer von Items und Flüssigkeiten zwischen angrenzenden Blöcken. -oc:tooltip.upgradeangel=Erlaubt es Robotern, Blöcke in die Luft zu setzen, selbst wenn kein Referenzblock daneben existiert. -oc:tooltip.upgradebattery=Erhöht die Energiespeicherkapazität eines Roboters, was ihm erlaubt, länger zu arbeiten, ohne erneut aufgeladen werden zu müssen.[nl] Kapazität: §f%s§7 -oc:tooltip.upgradechunkloader=Wenn sich im Wald ein Roboter bewegt, und niemand da ist, ihn zu sehen, bewegt er sich dann wirklich? Dieses Upgrade stellt sicher, dass dem so ist. Es hält den Chunk, in dem sich der Roboter befindet, geladen, verbraucht jedoch Energie, während es aktiv ist. -oc:tooltip.upgradecontainercard=Dieses Behälter-Upgrade erlaubt es, eine Karte in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen.[nl] Maximale Stufe: §f%s§7 -oc:tooltip.upgradecontainerupgrade=Dieses Behälter-Upgrade erlaubt es, ein Upgrade in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen.[nl] Maximale Stufe: §f%s§7 -oc:tooltip.upgradecrafting=Ermöglicht Robotern, in dem oberen linken Bereich ihres Inventars Dinge zu fertigen. Gegenstände müssen so angeordnet sein, wie sie es in einer Werkbank wären. -oc:tooltip.upgradedatabase=Diese Upgrade erlaubt es, Gegenstandsinformationen - für spätere Verwendung in anderen Komponenten - zu speichern.[nl] Maximale Einträge: §f%s§7 -oc:tooltip.upgradeexperience=Dieses Upgrade erlaubt es Robotern, durch verschiedene Aktionen Erfahrung zu sammeln. Je mehr Erfahrung ein Roboter hat, desto mehr Energie kann er speichern, desto schneller kann er Blöcke abbauen und desto effizienter kann er mit Werkzeugen umgehen. -oc:tooltip.upgradegenerator=Kann verwendet werden, um unterwegs Energie aus Brennstoffen zu erzeugen. Verbraucht Gegenstände über einen ihrem Brennwert gemäßen Zeitraum.[nl] §fEffizienz§7: §a%s%%§7 -oc:tooltip.upgradehover=Erlaubt Robotern höher zu fliegen, ohne an Wänden klettern zu müssen.[nl] Maximalhöhe: §f%s§7 -oc:tooltip.upgradeinventory=Dieses Upgrade gibt Robotern ein internes Inventar. Ohne ein solches Upgrade können Roboter keine Gegenstände verwahren. -oc:tooltip.upgradeinventorycontroller=Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Inventaren zu interagieren, und erlaubt es ihm, das angelegte Werkzeug mit einem Gegenstand in seinem Inventar auszutauschen. -oc:tooltip.upgrademf=Erlaubt es Adaptern, mit Blöcken zu interagieren, die etwas weiter weg sind. -oc:tooltip.upgrademf.Linked=§fVerbindung hergestellt§7 -oc:tooltip.upgrademf.Unlinked=§fKeine Verbindung§7 -oc:tooltip.upgradeleash=Erlaubt es manchen Geräten, wie etwa Drohnen, B- *getuschel* Verzeihung. Mir wurde soeben zugetragen, dass es tatsächlich dazu verwendet werden kann, Tiere an die Leine zu legen. Sogar viele Viecher. -oc:tooltip.upgradenavigation=Erlaubt es Robotern, ihre Position und Ausrichtung zu bestimmen. Die Position ist relativ zur Mitte der Karte, die in diesem Upgrade verbaut wurde. -oc:tooltip.upgradepiston=Dieses Upgrade ist sehr drückend. Es macht es möglich Blöcke zu verschieben, ähnlich dem Kolben. Es kann jedoch §lkeine§7 Entities bewegen. -oc:tooltip.upgradesign=Erlaubt das Lesen und Schreiben von Text auf Schildern. -oc:tooltip.upgradesolargenerator=Kann verwendet werden, um unterwegs Energie aus Sonnenlicht zu generieren. Benötigt eine ungehinderte Sicht zum Himmel über dem Roboter. Generiert Energie mit %s%% der Geschwindigkeit eines Stirlingmotors. -oc:tooltip.upgradetank=Dieses Upgrade gibt Robotern einen internen Tank. Ohne ein solches Upgrade können Roboter keine Flüssigkeiten verwahren. -oc:tooltip.upgradetankcontroller=Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Tanks zu interagieren, und erlaubt es ihm, Flüssigkeiten in und aus sich im Inventar befindlichen Tank-Gegenständen zu pumpen. -oc:tooltip.upgradetractorbeam=Stattet den Roboter mit unglaublich fortschrittlicher Technologie - Kosename: "Gegenstandsmagnet" - aus. Erlaubt es dem Roboter, Gegenstände, innerhalb von 3 Blöcken um sich herum, einzusammeln. -oc:tooltip.waypoint=Ein Orientierungpunkt für Geräte mit einem Navigations-Upgrade. -oc:tooltip.wirelessnetworkcard=Erlaubt das drahtlose Senden von Netzwerknachrichten, zusätzlich zu normalen. Drahtlose Nachrichten werden nur gesendet, wenn eine §fSignalstärke§7 festgelegt wurde! -oc:tooltip.worldsensorcard=Erlaubt es, Informationen über die Welt auszulesen, wie etwa Gravitation und ob die Atmosphäre atembar ist. Verwendung von Messergebnissen auf eigene Gefahr. Der Hersteller übernimmt keinerlei Garantie. -oc:tooltip.wrench=Eine Mischung aus Schraubenzieher und Schraubenschlüssel, einfach zu benutzen, aber schwer zu meistern. - -#Achievements -achievement.oc.adapter=Steck mich ein -achievement.oc.adapter.desc=Interagiert mit Blöcken aus anderen Mods, und sogar Vanilla-Minecraft! -achievement.oc.assembler=Wundervoll -achievement.oc.assembler.desc=Zeit, die Welt zu erobern! -achievement.oc.cable=Kein gewöhnliches Kabel -achievement.oc.cable.desc=Mit patentierter Anti-Kabelsalat-Technologie. -achievement.oc.capacitor=Batterien enthalten -achievement.oc.capacitor.desc=Achtung: Kurzschlussgefahr. -achievement.oc.card=Wir akzeptieren Karten -achievement.oc.card.desc=Dreimal falsche PIN-Eingabe. Karte wird einbehalten. -achievement.oc.case=Desktop-Computer -achievement.oc.case.desc=Desktop (und Betriebssystem) separat erhältlich. -achievement.oc.charger=Die Spannung steigt -achievement.oc.charger.desc=Und auflad- verdammt, schon wieder das Redstone-Signal vergessen. -achievement.oc.chip=Technischer Fortschritt -achievement.oc.chip.desc=Besser als Elekrtonenröhren. -achievement.oc.cpu=Übertaktet -achievement.oc.cpu.desc=Endlich ein Nutzen für die ungenutzten Prozessorzyklen. -achievement.oc.disassembler=Nochmal von vorn -achievement.oc.disassembler.desc=Falls deine brillianten Ideen am Ende doch nicht so brilliant sind. -achievement.oc.diskDrive=*klick brumm brumm brumm ratter* -achievement.oc.diskDrive.desc=Weniger Speicherplatz, aber welch ein wundervolles Geräusch. -achievement.oc.drone=Startbereit -achievement.oc.drone.desc=Keep calm and nuke it from orbit. -achievement.oc.eeprom=Es kann nur einen geben -achievement.oc.eeprom.desc=Pro Computer, genau genommen. Für eine deterministische Boot-Reihenfolge, weißt du? -achievement.oc.floppy=Der Eine Ri- Datenspeicher -achievement.oc.floppy.desc=Nicht zu verwechseln mit Flappy. -achievement.oc.geolyzer=Gezielte Rohstoffsuche -achievement.oc.geolyzer.desc=Umweltfreundlicher als Fracking, versprochen. -achievement.oc.graphicsCard=LastGen -achievement.oc.graphicsCard.desc=The way it's meant to be... äh... rendered. Ja. So ungefähr. -achievement.oc.hdd=Hotdog-Dealer -achievement.oc.hdd.desc=Moment, das stimmt nicht ganz. Warte, gleich hab' ich's... -achievement.oc.hologram=Eine höhere Dimension -achievement.oc.hologram.desc=Weil 2D so langweilig ist. Oder ist es das wirklich? -achievement.oc.keyboard=SchmutzFänger3000 -achievement.oc.keyboard.desc=Es wird davon abgeraten, sie umzudrehen und zu schütteln, auch wenn es noch so verlockend ist. -achievement.oc.microcontroller=Kleine Schwester -achievement.oc.microcontroller.desc=Das kleine Geschwisterkind des Computers. -achievement.oc.motionSensor=Got the Moves -achievement.oc.motionSensor.desc=Like Steve Swagger. -achievement.oc.networkCard=Fernleitung -achievement.oc.networkCard.desc=Perfekt um diese transitiven Beziehungen aufrecht zu erhalten. -achievement.oc.openOS=Boot -achievement.oc.openOS.desc=Das Eine Betrieb- wie, was meinst du, der Witz wird alt? -achievement.oc.powerDistributor=Strom-Sharing -achievement.oc.powerDistributor.desc=ElektroVZ ist so 2010. -achievement.oc.rack=Multifunktionsschrank -achievement.oc.rack.desc=Wird unter Anderem in Kantinen genutzt, um gebrauchte Tabletts zu sammeln. -achievement.oc.raid=Suche Team -achievement.oc.raid.desc=Heroic plzkthx. -achievement.oc.ram=Random Access Memories -achievement.oc.ram.desc=Congratulations, you're Doin' It Right. -achievement.oc.redstoneCard=Kontakt -achievement.oc.redstoneCard.desc=Zeit für analoge Signale. -achievement.oc.redstoneIO=Außenseiter -achievement.oc.redstoneIO.desc=Redstone-Signale, dort wo du sie brauchst. -achievement.oc.robot=Beep Boop -achievement.oc.robot.desc=VERNICHTEN! -achievement.oc.screen=Hast du schon versucht, ihn aus- und wieder anzuschalten? -achievement.oc.screen.desc=Nein, ernsthaft. Ein Redstone-Impuls kann wirklich einen Bildschirm an- und ausschalten. -achievement.oc.server=Dediziert -achievement.oc.server.desc=Hier kommen die Cloud-Dienste. -achievement.oc.switch=Paketdienst -achievement.oc.switch.desc=Nicht für Hinkelsteine verwenden, die könnten sonst beim Aussortieren verloren gehen. -achievement.oc.tablet=Verschluckbare Kleinteile -achievement.oc.tablet.desc=Von Kindern fernhalten, um unerwünschte Kreditkartenbelastungen zu vermeiden. -achievement.oc.transistor=Schöne Grüße an Red! -achievement.oc.transistor.desc=Jetzt kannst du dir den Soundtrack anhören. -achievement.oc.wirelessNetworkCard=Das WLAN - Unendliche Weiten -achievement.oc.wirelessNetworkCard.desc=Netzwerke, die nie ein Paket zuvor gesehen hat. - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s ist zu gierig geworden. -death.attack.oc.nanomachinesOverload.2=%s erlitt einen Nervenzusammenbruch. -death.attack.oc.nanomachinesOverload.3=Die Nanomaschinen von %s gerieten wohl außer Kontrolle. Ups. -death.attack.oc.nanomachinesHungry.1=%s wurde von Nanomaschinen verspeist. -death.attack.oc.nanomachinesHungry.2=%s hat wohl seine Nanomaschinen nicht gefüttert. -death.attack.oc.nanomachinesHungry.3=%s wurde verdaut. - -# NEI Integration -nei.options.inventory.oredict=OreDictionary-Namen anzeigen -nei.options.inventory.oredict.true=True -nei.options.inventory.oredict.false=False -nei.usage.oc.Manual=Handbuch öffnen - -# Waila Integration -option.oc.address=Adresse -option.oc.componentName=Komponentenname -option.oc.energy=Energie diff --git a/src/main/resources/assets/opencomputers/lang/de_de.json b/src/main/resources/assets/opencomputers/lang/de_de.json new file mode 100644 index 0000000000..17a71d3fec --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/de_de.json @@ -0,0 +1,458 @@ +{ + "tile.oc.adapter": "Adapter", + "tile.oc.assembler": "Elektronik-Werkbank", + "tile.oc.cable": "Kabel", + "tile.oc.capacitor": "Kondensator", + "tile.oc.carpetedcapacitor": "Teppichkondensator", + "tile.oc.case1": "Computergehäuse (Stufe 1)", + "tile.oc.case2": "Computergehäuse (Stufe 2)", + "tile.oc.case3": "Computergehäuse (Stufe 3)", + "tile.oc.caseCreative": "Computergehäuse (Kreativ)", + "tile.oc.chameliumBlock": "Chamälium-Block", + "tile.oc.charger": "Ladestation", + "tile.oc.disassembler": "Recycler", + "tile.oc.diskDrive": "Diskettenlaufwerk", + "tile.oc.endstone": "Endstein", + "tile.oc.geolyzer": "Geolyzer", + "tile.oc.hologram1": "Hologrammprojektor (Stufe 1)", + "tile.oc.hologram2": "Hologrammprojektor (Stufe 2)", + "tile.oc.keyboard": "Tastatur", + "tile.oc.microcontroller": "Mikrocontroller", + "tile.oc.motionSensor": "Bewegungsmelder", + "tile.oc.netSplitter": "Net Splitter", + "tile.oc.powerConverter": "Leistungswandler", + "tile.oc.powerDistributor": "Stromverteiler", + "tile.oc.print": "3D-Druck", + "tile.oc.printer": "3D-Drucker", + "tile.oc.raid": "Raid", + "tile.oc.redstone": "Redstone-I/O", + "tile.oc.relay": "Relais", + "tile.oc.robot": "Roboter", + "tile.oc.robotAfterimage": "Roboter", + "tile.oc.screen1": "Bildschirm (Stufe 1)", + "tile.oc.screen2": "Bildschirm (Stufe 2)", + "tile.oc.screen3": "Bildschirm (Stufe 3)", + "tile.oc.rack": "Serverschrank", + "tile.oc.transposer": "Transposer", + "tile.oc.waypoint": "Wegpunkt", + "item.oc.AbstractBusCard": "Abstrakter-Bus-Karte", + "item.oc.Acid": "Grog", + "item.oc.ALU": "Arithmetisch-logische Einheit (ALU)", + "item.oc.Analyzer": "Messgerät", + "item.oc.APU0": "Beschleunigter Prozessor (APU) (Stufe 1)", + "item.oc.APU1": "Beschleunigter Prozessor (APU) (Stufe 2)", + "item.oc.APU2": "Beschleunigter Prozessor (APU) (Kreativ)", + "item.oc.ArrowKeys": "Pfeiltasten", + "item.oc.ButtonGroup": "Tastengruppe", + "item.oc.CardBase": "Kartenbasis", + "item.oc.Chamelium": "Chamälium", + "item.oc.CircuitBoard": "Leiterplatte", + "item.oc.ComponentBus0": "Komponentenschnittstelle (Stufe 1)", + "item.oc.ComponentBus1": "Komponentenschnittstelle (Stufe 2)", + "item.oc.ComponentBus2": "Komponentenschnittstelle (Stufe 3)", + "item.oc.componentbus3": "Komponentenschnittstelle (Kreativ)", + "item.oc.ControlUnit": "Steuerwerk (CU)", + "item.oc.CPU0": "Hauptprozessor (CPU) (Stufe 1)", + "item.oc.CPU1": "Hauptprozessor (CPU) (Stufe 2)", + "item.oc.CPU2": "Hauptprozessor (CPU) (Stufe 3)", + "item.oc.CuttingWire": "Schneidedraht", + "item.oc.DataCard0": "Datenkarte (Stufe 1)", + "item.oc.DataCard1": "Datenkarte (Stufe 2)", + "item.oc.DataCard2": "Datenkarte (Stufe 3)", + "item.oc.DebugCard": "Debug-Karte", + "item.oc.Debugger": "Netzwerk-Debugger", + "item.oc.DiamondChip": "Diamantsplitter", + "item.oc.Disk": "Platte", + "item.oc.DiskDriveMountable": "Diskettenlaufwerk", + "item.oc.Drone": "Drohne", + "item.oc.DroneCase0": "Drohnengehäuse (Stufe 1)", + "item.oc.DroneCase1": "Drohnengehäuse (Stufe 2)", + "item.oc.DroneCase3": "Drohnengehäuse (Kreativ)", + "item.oc.eeprom": "EEPROM", + "item.oc.FloppyDisk": "Diskette", + "item.oc.GraphicsCard0": "Grafikkarte (Stufe 1)", + "item.oc.GraphicsCard1": "Grafikkarte (Stufe 2)", + "item.oc.GraphicsCard2": "Grafikkarte (Stufe 3)", + "item.oc.HardDiskDrive0": "Festplatte (Stufe 1)", + "item.oc.HardDiskDrive1": "Festplatte (Stufe 2)", + "item.oc.HardDiskDrive2": "Festplatte (Stufe 3)", + "item.oc.hoverBoots": "Schwebestiefel", + "item.oc.InkCartridge": "Tintenkartusche", + "item.oc.InkCartridgeEmpty": "Tintenkartusche (Leer)", + "item.oc.InternetCard": "Internetkarte", + "item.oc.Interweb": "Internetz", + "item.oc.IronNugget": "Eisennugget", + "item.oc.LinkedCard": "Verknüpfte Karte", + "item.oc.Manual": "OpenComputers-Handbuch", + "item.oc.Memory0": "Speicher (Stufe 1)", + "item.oc.Memory1": "Speicher (Stufe 1.5)", + "item.oc.Memory2": "Speicher (Stufe 2)", + "item.oc.Memory3": "Speicher (Stufe 2.5)", + "item.oc.Memory4": "Speicher (Stufe 3)", + "item.oc.Memory5": "Speicher (Stufe 3.5)", + "item.oc.Microchip0": "Mikrochip (Stufe 1)", + "item.oc.Microchip1": "Mikrochip (Stufe 2)", + "item.oc.Microchip2": "Mikrochip (Stufe 3)", + "item.oc.MicrocontrollerCase0": "Mikrocontroller-Gehäuse (Stufe 1)", + "item.oc.MicrocontrollerCase1": "Mikrocontroller-Gehäuse (Stufe 2)", + "item.oc.MicrocontrollerCase3": "Mikrocontroller-Gehäuse (Kreativ)", + "item.oc.Nanomachines": "Nanomaschinen", + "item.oc.NetworkCard": "Netzwerkkarte", + "item.oc.NumPad": "Ziffernblock", + "item.oc.Present": "Ein kleines Etwas...", + "item.oc.PrintedCircuitBoard": "Gedruckte Leiterplatte (PCB)", + "item.oc.RawCircuitBoard": "Leiterplattenrohling", + "item.oc.RedstoneCard0": "Redstonekarte (Stufe 1)", + "item.oc.RedstoneCard1": "Redstonekarte (Stufe 2)", + "item.oc.Server0": "Server (Stufe 1)", + "item.oc.Server1": "Server (Stufe 2)", + "item.oc.Server2": "Server (Stufe 3)", + "item.oc.Server3": "Server (Kreativ)", + "item.oc.Tablet": "Tablet", + "item.oc.TabletCase0": "Tablet-Gehäuse (Stufe 1)", + "item.oc.TabletCase1": "Tablet-Gehäuse (Stufe 2)", + "item.oc.TabletCase3": "Tablet-Gehäuse (Kreativ)", + "item.oc.Terminal": "Fernbedienung", + "item.oc.TerminalServer": "Terminalserver", + "item.oc.TexturePicker": "Texturenwähler", + "item.oc.Transistor": "Transistor", + "item.oc.UpgradeAngel": "Schwebe-Upgrade", + "item.oc.UpgradeBattery0": "Akku-Upgrade (Stufe 1)", + "item.oc.UpgradeBattery1": "Akku-Upgrade (Stufe 2)", + "item.oc.UpgradeBattery2": "Akku-Upgrade (Stufe 3)", + "item.oc.UpgradeChunkloader": "Chunkloader-Upgrade", + "item.oc.UpgradeContainerCard0": "Karten-Behälter (Stufe 1)", + "item.oc.UpgradeContainerCard1": "Karten-Behälter (Stufe 2)", + "item.oc.UpgradeContainerCard2": "Karten-Behälter (Stufe 3)", + "item.oc.UpgradeContainerUpgrade0": "Upgrade-Behälter (Stufe 1)", + "item.oc.UpgradeContainerUpgrade1": "Upgrade-Behälter (Stufe 2)", + "item.oc.UpgradeContainerUpgrade2": "Upgrade-Behälter (Stufe 3)", + "item.oc.UpgradeCrafting": "Crafting-Upgrade", + "item.oc.UpgradeDatabase0": "Datenbank-Upgrade (Stufe 1)", + "item.oc.UpgradeDatabase1": "Datenbank-Upgrade (Stufe 2)", + "item.oc.UpgradeDatabase2": "Datenbank-Upgrade (Stufe 3)", + "item.oc.UpgradeExperience": "Erfahrungs-Upgrade", + "item.oc.UpgradeGenerator": "Generator-Upgrade", + "item.oc.UpgradeHover0": "Schwebe-Upgrade (Stufe 1)", + "item.oc.UpgradeHover1": "Schwebe-Upgrade (Stufe 2)", + "item.oc.UpgradeInventory": "Inventar-Upgrade", + "item.oc.UpgradeInventoryController": "Inventarbedienungs-Upgrade", + "item.oc.UpgradeLeash": "Leinen-Upgrade", + "item.oc.UpgradeMF": "MFU", + "item.oc.UpgradeNavigation": "Navigations-Upgrade", + "item.oc.UpgradePiston": "Kolben-Upgrade", + "item.oc.UpgradeSign": "Schild-I/O-Upgrade", + "item.oc.UpgradeSolarGenerator": "Solargenerator-Upgrade", + "item.oc.UpgradeTank": "Tank-Upgrade", + "item.oc.UpgradeTankController": "Tankbedienungs-Upgrade", + "item.oc.UpgradeTractorBeam": "Traktorstrahl-Upgrade", + "item.oc.UpgradeTrading": "Handels-Upgrade", + "item.oc.WirelessNetworkCard0": "Drahtlosnetzwerkkarte (Stufe 1)", + "item.oc.WirelessNetworkCard1": "Drahtlosnetzwerkkarte (Stufe 2)", + "item.oc.WorldSensorCard": "Weltsensorkarte", + "item.oc.wrench": "Schraubenziehschlüssel", + "entity.oc.Drone.name": "Drohne", + "oc:gui.Analyzer.Address": "§6Adresse§f: %s", + "oc:gui.Analyzer.AddressCopied": "Adresse wurde in die Zwischenablage kopiert.", + "oc:gui.Analyzer.ChargerSpeed": "§6Ladegeschwindigkeit§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Komponentenname§f: %s", + "oc:gui.Analyzer.Components": "§6Anzahl verbundener Komponenten§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "In die Zwischenablage kopieren.", + "oc:gui.Analyzer.LastError": "§6Letzter Fehler§f: %s", + "oc:gui.Analyzer.RobotName": "§6Name§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Besitzer§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Erfahrung§f: %s (Stufe %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Gespeicherte Energie§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Insgesamt gespeicherte Energie§f: %s", + "oc:gui.Analyzer.Users": "§6Benutzer§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Signalstärke§f: %s", + "oc:gui.Assembler.Collect": "Fertiges Gerät entnehmen", + "oc:gui.Assembler.Complexity": "Komplexität: %s/%s", + "oc:gui.Assembler.InsertCase": "Grundbaustein einlegen", + "oc:gui.Assembler.InsertCPU": "CPU benötigt", + "oc:gui.Assembler.InsertRAM": "RAM benötigt", + "oc:gui.Assembler.Progress": "Fortschritt: %s%% (%s)", + "oc:gui.Assembler.Run": "Zusammenbauen", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Grafikkarte", + "oc:gui.Assembler.Warning.Inventory": "Inventar-Upgrade", + "oc:gui.Assembler.Warning.Keyboard": "Tastatur", + "oc:gui.Assembler.Warning.OS": "Bootbares Medium", + "oc:gui.Assembler.Warning.Screen": "Bildschirm", + "oc:gui.Assembler.Warnings": "§eWarnung§7: Empfohlene Komponenten fehlen.", + "oc:gui.Chat.NewVersion": "Eine neue Version ist verfügbar: %s", + "oc:gui.Chat.TextureName": "§7Texturname ist §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "Es gab §cFehler§f beim Ausführen des Class-Transformers. Bitte melde dies, zusammen mit deiner (vollständigen!) FML §alatest.log§f/§afml-server-latest.log§f Log-Datei, danke!", + "oc:gui.Chat.WarningFingerprint": "§cWARNUNG§f - ungültige Signatur! Sollte '§a%s§f' sein, aber war '§e%s§f'. Falls du kein Modder bist und die \"deobfuscated\"-Version des Mods benutzt, solltest du OpenComputers erneut herunterladen, da die JAR, die du benutzt, vermutlich modifiziert wurde.", + "oc:gui.Chat.WarningLink": "Link konnte nicht geöffnet werden: %s", + "oc:gui.Chat.WarningLuaFallback": "Die native Lua-Implementierung ist nicht verfügbar. Computer können ihren Ausführungszustand nicht speichern. Sie werden automatisch neu starten, sobald ein Chunk neu geladen wird.", + "oc:gui.Chat.WarningProjectRed": "Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red.", + "oc:gui.Chat.WarningRecipes": "Es gab Fehler beim Laden eines oder mehrerer Rezepte. Einige Gegenstände können unter Umständen nicht gefertigt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen.", + "oc:gui.Chat.WarningSimpleComponent": "Eine Erweiterung (deine?) welche das §aSimpleComponent§f-Interface verwendet, hat etwas §efalsch gemacht§f. Komponentenlogik konnte nicht eingefügt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen.", + "oc:gui.Drive.Managed": "Managed", + "oc:gui.Drive.Unmanaged": "Unmanaged", + "oc:gui.Drive.ReadOnlyLock": "Sperre", + "oc:gui.Drive.ReadOnlyLockWarning": "§lNur Lesen§r Sperre. Kann nur entfernt werden, wenn das Laufwerk gelöscht wird.", + "oc:gui.Drive.Warning": "§lWarnung§r: Umschalten der Modi führt zum Datenverlust.", + "oc:gui.Error.ComponentOverflow": "Zu viele Komponenten sind mit dem Computer verbunden.", + "oc:gui.Error.InternalError": "Interner Fehler, bitte sieh in der Logdatei nach. Das ist wahrscheinlich ein Bug.", + "oc:gui.Error.NoCPU": "Im Computer ist kein Hauptprozessor (CPU) installiert.", + "oc:gui.Error.NoEnergy": "Nicht genug Energie.", + "oc:gui.Error.NoRAM": "Im Computer ist kein RAM installiert.", + "oc:gui.Error.OutOfMemory": "Nicht genug Arbeitsspeicher.", + "oc:gui.Manual.Blocks": "OpenComputers-Blöcke", + "oc:gui.Manual.Home": "Startseite", + "oc:gui.Manual.Items": "OpenComputers-Gegenstände", + "oc:gui.Manual.Warning.BlockMissing": "Block nicht verfügbar.", + "oc:gui.Manual.Warning.ImageMissing": "Bild nicht gefunden.", + "oc:gui.Manual.Warning.ItemMissing": "Gegenstand nicht verfügbar.", + "oc:gui.Manual.Warning.OreDictMissing": "Ore-Dictionary-Eintrag nicht verfügbar.", + "oc:gui.Raid.Warning": "§4Platten werden beim Einsetzen\ngelöscht. Entfernen einer Platte\nlöscht das gesamte Raid.", + "oc:gui.Robot.Power": "Energie", + "oc:gui.Robot.TurnOff": "Ausschalten", + "oc:gui.Robot.TurnOn": "Einschalten\n§7Nutze ein Messgerät, um Fehler zu behandeln.§r", + "oc:gui.Rack.Back": "Hinten", + "oc:gui.Rack.Bottom": "Unten", + "oc:gui.Rack.Left": "Links", + "oc:gui.Rack.None": "Keine", + "oc:gui.Rack.Right": "Rechts", + "oc:gui.Rack.Enabled": "Aktiv", + "oc:gui.Rack.Disabled": "Inaktiv", + "oc:gui.Rack.Top": "Oben", + "oc:gui.Switch.PacketsPerCycle": "Pakete / Zyklus", + "oc:gui.Switch.QueueSize": "Puffergröße", + "oc:gui.Switch.TransferRate": "Taktung", + "oc:gui.Terminal.InvalidKey": "Ungültiger Schlüssel, vermutlich wurde eine andere Fernbedienung an den Server gebunden.", + "oc:gui.Terminal.OutOfRange": "Kein Signal.", + "oc:container.adapter": "Adapter", + "oc:container.case": "Computer", + "oc:container.charger": "Ladestation", + "oc:container.disassembler": "Recycler", + "oc:container.diskdrive": "Diskettenlaufwerk", + "oc:container.printer": "Drucker", + "oc:container.raid": "Raid", + "oc:container.relay": "Relais", + "oc:container.server": "Server", + "oc:container.rack": "Serverschrank", + "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.clipboardPaste": "Zwischenablage einfügen", + "oc:tooltip.accesspoint": "Verhält sich wie ein Switch, aber empfängt zusätzlich Drahtlosnachrichten und leitet Pakete aus dem Festnetz drahtlos weiter.", + "oc:tooltip.abstractbuscard": "Erlaubt es, LIP-Pakete des Abstrakten Busses von §fStargateTech 2§7 zu senden und zu empfangen.", + "oc:tooltip.acid": "Eine hochgiftige Möchtegernflüssigkeit, wird üblicherweise nur von gewissen Piraten konsumiert. Mag sich aber auch zu anderen Zwecken eignen.", + "oc:tooltip.adapter": "Erlaubt es, Blöcke anzusteuern, die keine Komponentenblöcke sind, wie etwa reguläre Minecraft-Blöcke oder Blöcke anderer Mods.", + "oc:tooltip.alu": "Zählt Zahlen zum Zeitvertreib. Klingt komisch, is aber so.", + "oc:tooltip.analyzer": "Erlaubt es, Informationen über Blöcke anzuzeigen, wie zum Bleistift ihre §fAdresse§7 und ihren §fKomponentennamen§7.\nErlaubt zudem, den Fehler anzuzeigen, der zu einem Computerabsturz geführt hat, falls der Computer nicht regulär heruntergefahren wurde.", + "oc:tooltip.apu": "Eine CPU mit integrierter GPU (bzw. IGP), falls du unbedingt den zusätzlichen Kartenslot brauchst.\nUnterstützte Komponenten: §f%s§7\nHöchstauflösung: §f%sx%s§7\nMaximale Farbtiefe: §f%s§7\nOperationen/Tick: §f%s§7", + "oc:tooltip.assembler": "Erlaubt die Fertigung von Robotern und weiteren Geräten aus diversen anderen Computerteilen.", + "oc:tooltip.cable": "Ein billiger Weg, verschiedene Blöcke miteinander zu verbinden.", + "oc:tooltip.capacitor": "Speichert Energie für spätere Verwendung. Kann extrem schnell befüllt und entleert werden.", + "oc:tooltip.carpetedcapacitor": "Speichert Energie für den späteren Gebrauch. Kann sehr schnell befüllt und entleert werden. Lädt auf, wenn Schafe oder Ozelots darauf laufen.", + "oc:tooltip.cardbase": "Wie der Name schon sagt, werden alle Erweiterungskarten hieraus hergestellt.", + "oc:tooltip.case": "Das Computergehäuse ist der essentielle Grundbaustein für einen Computer. §fErweiterungskarten§7, §fRAM§7 und §fFestplatten§7 können in einem Gehäuse installiert werden.\nSlots: §f%s§7", + "oc:tooltip.chamelium": "Rohmaterial für 3D-Drucke. Nicht zum Verzehr geeignet: kann zu Erblindung und zeitweiligem Verlust von Präsenz führen.", + "oc:tooltip.chameliumblock": "Schick und sauber. Praktisch für gefärbte Teile in 3D-Drucken, oder einfach, um deine tolle Basis mit einem schlichten, gefärbten Block zu dekorieren.", + "oc:tooltip.charger": "Lädt Roboter mit Energie aus Kondensatoren auf. Die Ladegeschwindigkeit hängt vom eingehenden §fRedstonesignal§7 ab, wobei kein Signal \"nicht laden\" und ein Signal mit maximaler Stärke \"schnellstmöglich laden\" heißt. Erlaubt es auch Tablets zu laden, und ermöglicht Zugriff auf Festplatten in Tablets.", + "oc:tooltip.circuitboard": "Mühsam ernährt sich das Eichhörnchen. Wenn es groß wird, wird es mal eine gedruckte Leiterplatte.", + "oc:tooltip.controlunit": "Klingt wichtig, ist es auch. Man baut daraus immerhin CPUs. Wie könnte es da nicht wichtig sein.", + "oc:tooltip.componentbus": "Diese Erweiterung erlaubt es es Servern, mit noch mehr Komponenten gleichzeitig zu kommunizieren, ähnlich wie CPUs.\nUnterstützte Komponenten: §f%s§7", + "oc:tooltip.cpu": "Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?\nUnterstützte Komponenten: §f%s§7", + "oc:tooltip.cpu.Architecture": "Architektur: §f%s§7", + "oc:tooltip.cuttingwire": "Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht.", + "oc:tooltip.datacard0": "Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.", + "oc:tooltip.datacard1": "Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.", + "oc:tooltip.datacard2": "Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.", + "oc:tooltip.debugcard": "Kreativ-Modus-Gegenstand, erlaubt es die Welt zu manipulieren um das Testen zu erleichtern. Verwendung auf eigene Gefahr.", + "oc:tooltip.debugger": "Erlaubt, Informationen über OCs internes Netzwerk auszugeben. Nur verwenden, wenn von einem Entwickler dazu aufgefordert.", + "oc:tooltip.diamondchip": "Ein kleines Stück eines einst wunderschönen Diamanten. Er wird niemals wieder so sein, wie er war.", + "oc:tooltip.disassembler": "Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!", + "oc:tooltip.disk": "Sehr einfaches Speichermedium, das verwendet werden kann, um Disketten und Festplatten zu fertigen.", + "oc:tooltip.diskdrive.CC": "ComputerCraft-Disketten werden §aauch unterstützt§7.", + "oc:tooltip.diskdrive": "Erlaubt es, Disketten zu lesen und zu beschreiben. Kann in Robotern installiert werden, um später Disketten einlegen zu können.", + "oc:tooltip.diskdrivemountable": "Funktioniert genauso wie ein normales Diskettenlaufwerk, wird aber in einem Serverschrank installiert.", + "oc:tooltip.diskusage": "Festplattennutzung: %s/%s Byte", + "oc:tooltip.disklocked": "Gesperrt von %s", + "oc:tooltip.diskmodemanaged": "Modus: Managed", + "oc:tooltip.diskmodeunmanaged": "Modus: Unmanaged", + "oc:tooltip.drone": "Drohnen sind schlichte, flinke Aufklärungseinheiten mit limitierter Inventargröße.", + "oc:tooltip.dronecase": "Dieses Gehäuse wird verwendet, um Drohnen in der Elektronik-Werkbank zu bauen. Es bietet Platz für wenige Komponenten und kommt mit endsteinbetriebener Levitation.", + "oc:tooltip.eeprom": "Kleiner, programmierbarer Speicher für das BIOS, das Computer zum starten verwenden.", + "oc:tooltip.fakeendstone": "So gut wie echt, kann sogar schweben!", + "oc:tooltip.geolyzer": "Erlaubt es die Härte der Blöcke in der Umgebung abzufragen. Hilfreich um Hologramme der Gegend zu erzeugen, oder um Erze zu entdecken.", + "oc:tooltip.graphicscard": "Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.\nHöchstauflösung: §f%sx%s§7\nMaximale Farbtiefe: §f%s§7\nOperationen/Tick: §f%s§7", + "oc:tooltip.hoverboots": "Spring höher, fall tiefer, geh besser. Dies und noch mehr bekommst du mit den neuen, patentierten Schwebestiefeln (TM).", + "oc:tooltip.inkcartridge": "Wird verwendet, um 3D-Drucker zu befüllen. Aus mysteriösen Gründen muss die Kartusche nicht fest im Drucker eingebaut werden.", + "oc:tooltip.inkcartridgeempty": "Diese Kartusche wurde trocken gelegt. Fülle sie mit Farben wieder auf. Oder wirf sie weg. Mir doch egal.", + "oc:tooltip.internetcard": "Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP-Sockets zu verwenden.", + "oc:tooltip.interweb": "Kann in einer Internetkarte verwendet werden, um Computer mit dem rechtsfreien Raum kommunizieren zu lassen.", + "oc:tooltip.ironnugget": "Ein Nugget, das aus Eisen besteht, darum wird es ja auch Eisennugget genannt, duh...", + "oc:tooltip.keyboard": "Kann an Bildschirmen befestigt werden, um auf ihnen zu tippen.", + "oc:tooltip.hologram0": "Ein Volumendisplay, das beliebige, von Computern festgelegte Voxelstrukturen anzeigt.\nAuflösung: §f48x32x48§7\nMaximale Skalierung: §f3x§7\nFarbtiefe: §fMonochrom§7", + "oc:tooltip.hologram1": "Ein Volumendisplay, das beliebige, von Computern festgelegte Voxelstrukturen anzeigt.\nAuflösung: §f48x32x48§7\nMaximale Skalierung: §f4x§7\nFarbtiefe: §fTricolor§7", + "oc:tooltip.linkedcard": "Diese Karten werden paarweise hergestellt, und können ausschließlich mit ihrer jeweilgen Partnerkarte kommunizieren, dafür allerdings über beliebige Entfernungen, und sogar über Dimensionen hinweg. Der Nachteil ist der hohe Energieverbrauch beim Senden einer Nachricht.", + "oc:tooltip.linkedcard_channel": "§8Kanal: %s§7", + "oc:tooltip.manual": "Alles, was es über OpenComputers zu wissen gibt. Und mehr. Und das alles zu dem unglaublich niedrigen Preis von... §obitte R dücken, um fortzufahren§7.", + "oc:tooltip.materialcosts": "Halte [§f%s§7] gedrückt für Materialkosten.", + "oc:tooltip.materials": "Materialien:", + "oc:tooltip.memory": "Braucht ein jeder Computer, um zu starten. Je mehr vorhanden, desto komplexere Programme können ausgeführt werden.", + "oc:tooltip.microchip": "Tritt auch unter dem Alias Integrierter Schaltkreis auf. Keine Ahnung, warum das auch mit Redstone klappt, aber es funktioniert.", + "oc:tooltip.microcontroller": "Mikrocontroller sind möglichst einfache Computer. Sie sind dazu gedacht, für Spezialfälle verwendet zu werden, bei denen sie einfach das Programm auf dem in ihnen eingebauten EEPROM ausführen sollen.", + "oc:tooltip.microcontrollercase": "Dieses Gehäuse wird verwendet, um Mikrocontroller in der Elektronik-Werkbank zu bauen. Platziere es in eine solche und füge weitere Komponenten hinzu, um einen Mikrocontroller zu erstellen.", + "oc:tooltip.motionsensor": "Kann Bewegungen sich in der Nähe befindender Lebewesen erkennen. Benötigt eine klare Sichtlinie.", + "oc:tooltip.nanomachines": "Kontrolleinheit und eine Menge Nanomaschinen zur Einnahme, sofern du dich traust.", + "oc:tooltip.networkcard": "Erlaubt es Computern, die über mehrere Blöcke miteinander verbunden sind (z.B. Kabel), mittels Netzwerknachrichten zu kommunizieren.", + "oc:tooltip.poweracceptor": "Energiewandelgeschwindigkeit: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fFactorization Charge§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Wandelt Strom anderer Mods in den internen Energietyp um. Umwandlungsverhältnisse:", + "oc:tooltip.powerdistributor": "Verteilt Energie zwischen mehreren Netzwerken. Nützlich, um Energie mit einem Wandler auf mehrere Subnetze, die voneinander getrennt sein sollen, zu verteilen.", + "oc:tooltip.present": "... für deine Mühen. Öffne dieses Geschenk für eine Chance, §kdolle Dinge§7 zu erhalten!\n§8Fertige OpenComputers-Gegenstände zur rechten Zeit, und du hast die Chance ein Geschenk zu erhalten.§7", + "oc:tooltip.print.BeaconBase": "§8Funktioniert als Grundstein für ein Leuchtfeuer.", + "oc:tooltip.print.LightValue": "§8Leuchtkraft: %s.", + "oc:tooltip.print.RedstoneLevel": "§8Redstone-Signalstärke: %s.", + "oc:tooltip.printedcircuitboard": "Hierauf basieren alle Erweiterungskarten, Speicher und so weiter.", + "oc:tooltip.printer": "Erlaubt es, Blöcke aus benutzerdefinierten Formen zu drucken. Verwendet Chamälium und Tintenkartuschen. Muss über einen Computer konfiguriert werden. Von Kindern fern halten. Weil halt.", + "oc:tooltip.raid": "Erlaubt es, drei Festplatten zu einem größeren Dateisystem zusammenzuschließen, welches von allen verbundenen Computern genutzt werden kann.", + "oc:tooltip.rawcircuitboard": "Kann in einer Ofen-kompatiblen Schmelze freier Wahl gehärtet werden.", + "oc:tooltip.redstone": "Erlaubt das Lesen und Ausgeben von Redstonesignalen um den Block herum. Kann von jedem Computer, der mit dem Block verbunden ist, gesteuert werden. Dieser Block ist quasi wie eine externe Redstonekarte.", + "oc:tooltip.redstonecard.ProjectRed": "§fProjectRed§7 wird §aunterstützt§7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 wird §aunterstützt§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 wird §aunterstützt§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 wird §aunterstützt§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 wird §aunterstützt§7.", + "oc:tooltip.redstonecard": "Erlaubt das Lesen und Ausgeben von Redstonesignalen um den Computer oder Roboter herum.", + "oc:tooltip.relay": "Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten \"hinter\" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.b. mittels Netzwerkkarten.", + "oc:tooltip.robot": "Im Gegensatz zu normalen Computern können sich Roboter in der Welt bewegen und ähnlich wie Spieler mit der Welt interagieren. Sie können jedoch §onicht§r§7 mit externen Komponenten interagieren!", + "oc:tooltip.robot_level": "§fStufe§7: §a%s§7.", + "oc:tooltip.robot_storedenergy": "§fGespeicherte Energie§7: §a%s§7.", + "oc:tooltip.screen": "Zeigt Text an, gesteuert von Grafikkarten in Computern.\nHöchstauflösung: §f%sx%s§7\nMaximale Farbtiefe: §f%s§7", + "oc:tooltip.server": "Ein Server kann wie ein gewöhnliches Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten, muss er in einem Servergehäuse installiert werden.", + "oc:tooltip.server.Components": "Installierte Komponenten:", + "oc:tooltip.rack": "Erlaubt die Installation von bis zu vier Servern oder anderen Serverschrankkomponenten.", + "oc:tooltip.switch": "Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten \"hinter\" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.B. mittels Netzwerkkarten.", + "oc:tooltip.tablet": "Ein Tablet-PC, für Lua unterwegs. Kann durch Sneak-Aktivierung zwangsgestoppt werden.", + "oc:tooltip.tabletcase": "Einfaches Gehäuse für Tablet-PCs. Kann in der Elektronik-Werkbank mit Komponenten bestückt werden, um einen Tablet-PC zu fertigen.", + "oc:tooltip.terminal": "Erlaubt es, einen Server aus der Ferne zu steuern, so lange man sich in Reichweite des Servers befindet. Verhält sich wie Bildschirm und Tastatur in einem. Kann mit Shift-Rechtsklick an einen Server in einem Serverschrank gebunden werden.", + "oc:tooltip.terminalserver": "Das Rückgrat für Fernzugriff. Fernbedienungen können hiermit verbunden werden. Enthält virtuelle Tastatur und Bildschirm.", + "oc:tooltip.texturepicker": "Dieses Werkzeug erlaubt es, eine Zeichenkette anzuzeigen, die die Oberfläche eines Blocks beschreibt und in Form-Definitionen für 3D-Drucker verwendet werden kann. Definiv keine Texturnamen, oh nein. Nix da.", + "oc:tooltip.tier": "§8Stufe %s", + "oc:tooltip.netsplitter": "Kann Netzwerke dynamisch verbinden. Die Konnektivität jeder Seite kann umgeschaltet werden, in dem man \"Professionelle Wartungsarbeiten\" mit einem Schraubenschlüssel durchführt. Sie kann auch mit einem Redstone-Signal an entsprechenden Seiten invertiert werden.", + "oc:tooltip.toolong": "Halte [§f%s§7] gedrückt für mehr Infos.", + "oc:tooltip.transistor": "Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren.", + "oc:tooltip.transposer": "Ermöglicht automatischen Transfer von Items und Flüssigkeiten zwischen angrenzenden Blöcken.", + "oc:tooltip.upgradeangel": "Erlaubt es Robotern, Blöcke in die Luft zu setzen, selbst wenn kein Referenzblock daneben existiert.", + "oc:tooltip.upgradebattery": "Erhöht die Energiespeicherkapazität eines Roboters, was ihm erlaubt, länger zu arbeiten, ohne erneut aufgeladen werden zu müssen.\nKapazität: §f%s§7", + "oc:tooltip.upgradechunkloader": "Wenn sich im Wald ein Roboter bewegt, und niemand da ist, ihn zu sehen, bewegt er sich dann wirklich? Dieses Upgrade stellt sicher, dass dem so ist. Es hält den Chunk, in dem sich der Roboter befindet, geladen, verbraucht jedoch Energie, während es aktiv ist.", + "oc:tooltip.upgradecontainercard": "Dieses Behälter-Upgrade erlaubt es, eine Karte in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen.\nMaximale Stufe: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Dieses Behälter-Upgrade erlaubt es, ein Upgrade in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen.\nMaximale Stufe: §f%s§7", + "oc:tooltip.upgradecrafting": "Ermöglicht Robotern, in dem oberen linken Bereich ihres Inventars Dinge zu fertigen. Gegenstände müssen so angeordnet sein, wie sie es in einer Werkbank wären.", + "oc:tooltip.upgradedatabase": "Diese Upgrade erlaubt es, Gegenstandsinformationen - für spätere Verwendung in anderen Komponenten - zu speichern.\nMaximale Einträge: §f%s§7", + "oc:tooltip.upgradeexperience": "Dieses Upgrade erlaubt es Robotern, durch verschiedene Aktionen Erfahrung zu sammeln. Je mehr Erfahrung ein Roboter hat, desto mehr Energie kann er speichern, desto schneller kann er Blöcke abbauen und desto effizienter kann er mit Werkzeugen umgehen.", + "oc:tooltip.upgradegenerator": "Kann verwendet werden, um unterwegs Energie aus Brennstoffen zu erzeugen. Verbraucht Gegenstände über einen ihrem Brennwert gemäßen Zeitraum.\n§fEffizienz§7: §a%s%%§7", + "oc:tooltip.upgradehover": "Erlaubt Robotern höher zu fliegen, ohne an Wänden klettern zu müssen.\nMaximalhöhe: §f%s§7", + "oc:tooltip.upgradeinventory": "Dieses Upgrade gibt Robotern ein internes Inventar. Ohne ein solches Upgrade können Roboter keine Gegenstände verwahren.", + "oc:tooltip.upgradeinventorycontroller": "Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Inventaren zu interagieren, und erlaubt es ihm, das angelegte Werkzeug mit einem Gegenstand in seinem Inventar auszutauschen.", + "oc:tooltip.upgrademf": "Erlaubt es Adaptern, mit Blöcken zu interagieren, die etwas weiter weg sind.", + "oc:tooltip.upgrademf.Linked": "§fVerbindung hergestellt§7", + "oc:tooltip.upgrademf.Unlinked": "§fKeine Verbindung§7", + "oc:tooltip.upgradeleash": "Erlaubt es manchen Geräten, wie etwa Drohnen, B- *getuschel* Verzeihung. Mir wurde soeben zugetragen, dass es tatsächlich dazu verwendet werden kann, Tiere an die Leine zu legen. Sogar viele Viecher.", + "oc:tooltip.upgradenavigation": "Erlaubt es Robotern, ihre Position und Ausrichtung zu bestimmen. Die Position ist relativ zur Mitte der Karte, die in diesem Upgrade verbaut wurde.", + "oc:tooltip.upgradepiston": "Dieses Upgrade ist sehr drückend. Es macht es möglich Blöcke zu verschieben, ähnlich dem Kolben. Es kann jedoch §lkeine§7 Entities bewegen.", + "oc:tooltip.upgradesign": "Erlaubt das Lesen und Schreiben von Text auf Schildern.", + "oc:tooltip.upgradesolargenerator": "Kann verwendet werden, um unterwegs Energie aus Sonnenlicht zu generieren. Benötigt eine ungehinderte Sicht zum Himmel über dem Roboter. Generiert Energie mit %s%% der Geschwindigkeit eines Stirlingmotors.", + "oc:tooltip.upgradetank": "Dieses Upgrade gibt Robotern einen internen Tank. Ohne ein solches Upgrade können Roboter keine Flüssigkeiten verwahren.", + "oc:tooltip.upgradetankcontroller": "Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Tanks zu interagieren, und erlaubt es ihm, Flüssigkeiten in und aus sich im Inventar befindlichen Tank-Gegenständen zu pumpen.", + "oc:tooltip.upgradetractorbeam": "Stattet den Roboter mit unglaublich fortschrittlicher Technologie - Kosename: \"Gegenstandsmagnet\" - aus. Erlaubt es dem Roboter, Gegenstände, innerhalb von 3 Blöcken um sich herum, einzusammeln.", + "oc:tooltip.waypoint": "Ein Orientierungpunkt für Geräte mit einem Navigations-Upgrade.", + "oc:tooltip.wirelessnetworkcard": "Erlaubt das drahtlose Senden von Netzwerknachrichten, zusätzlich zu normalen. Drahtlose Nachrichten werden nur gesendet, wenn eine §fSignalstärke§7 festgelegt wurde!", + "oc:tooltip.worldsensorcard": "Erlaubt es, Informationen über die Welt auszulesen, wie etwa Gravitation und ob die Atmosphäre atembar ist. Verwendung von Messergebnissen auf eigene Gefahr. Der Hersteller übernimmt keinerlei Garantie.", + "oc:tooltip.wrench": "Eine Mischung aus Schraubenzieher und Schraubenschlüssel, einfach zu benutzen, aber schwer zu meistern.", + "achievement.oc.adapter": "Steck mich ein", + "achievement.oc.adapter.desc": "Interagiert mit Blöcken aus anderen Mods, und sogar Vanilla-Minecraft!", + "achievement.oc.assembler": "Wundervoll", + "achievement.oc.assembler.desc": "Zeit, die Welt zu erobern!", + "achievement.oc.cable": "Kein gewöhnliches Kabel", + "achievement.oc.cable.desc": "Mit patentierter Anti-Kabelsalat-Technologie.", + "achievement.oc.capacitor": "Batterien enthalten", + "achievement.oc.capacitor.desc": "Achtung: Kurzschlussgefahr.", + "achievement.oc.card": "Wir akzeptieren Karten", + "achievement.oc.card.desc": "Dreimal falsche PIN-Eingabe. Karte wird einbehalten.", + "achievement.oc.case": "Desktop-Computer", + "achievement.oc.case.desc": "Desktop (und Betriebssystem) separat erhältlich.", + "achievement.oc.charger": "Die Spannung steigt", + "achievement.oc.charger.desc": "Und auflad- verdammt, schon wieder das Redstone-Signal vergessen.", + "achievement.oc.chip": "Technischer Fortschritt", + "achievement.oc.chip.desc": "Besser als Elekrtonenröhren.", + "achievement.oc.cpu": "Übertaktet", + "achievement.oc.cpu.desc": "Endlich ein Nutzen für die ungenutzten Prozessorzyklen.", + "achievement.oc.disassembler": "Nochmal von vorn", + "achievement.oc.disassembler.desc": "Falls deine brillianten Ideen am Ende doch nicht so brilliant sind.", + "achievement.oc.diskDrive": "*klick brumm brumm brumm ratter*", + "achievement.oc.diskDrive.desc": "Weniger Speicherplatz, aber welch ein wundervolles Geräusch.", + "achievement.oc.drone": "Startbereit", + "achievement.oc.drone.desc": "Keep calm and nuke it from orbit.", + "achievement.oc.eeprom": "Es kann nur einen geben", + "achievement.oc.eeprom.desc": "Pro Computer, genau genommen. Für eine deterministische Boot-Reihenfolge, weißt du?", + "achievement.oc.floppy": "Der Eine Ri- Datenspeicher", + "achievement.oc.floppy.desc": "Nicht zu verwechseln mit Flappy.", + "achievement.oc.geolyzer": "Gezielte Rohstoffsuche", + "achievement.oc.geolyzer.desc": "Umweltfreundlicher als Fracking, versprochen.", + "achievement.oc.graphicsCard": "LastGen", + "achievement.oc.graphicsCard.desc": "The way it's meant to be... äh... rendered. Ja. So ungefähr.", + "achievement.oc.hdd": "Hotdog-Dealer", + "achievement.oc.hdd.desc": "Moment, das stimmt nicht ganz. Warte, gleich hab' ich's...", + "achievement.oc.hologram": "Eine höhere Dimension", + "achievement.oc.hologram.desc": "Weil 2D so langweilig ist. Oder ist es das wirklich?", + "achievement.oc.keyboard": "SchmutzFänger3000", + "achievement.oc.keyboard.desc": "Es wird davon abgeraten, sie umzudrehen und zu schütteln, auch wenn es noch so verlockend ist.", + "achievement.oc.microcontroller": "Kleine Schwester", + "achievement.oc.microcontroller.desc": "Das kleine Geschwisterkind des Computers.", + "achievement.oc.motionSensor": "Got the Moves", + "achievement.oc.motionSensor.desc": "Like Steve Swagger.", + "achievement.oc.networkCard": "Fernleitung", + "achievement.oc.networkCard.desc": "Perfekt um diese transitiven Beziehungen aufrecht zu erhalten.", + "achievement.oc.openOS": "Boot", + "achievement.oc.openOS.desc": "Das Eine Betrieb- wie, was meinst du, der Witz wird alt?", + "achievement.oc.powerDistributor": "Strom-Sharing", + "achievement.oc.powerDistributor.desc": "ElektroVZ ist so 2010.", + "achievement.oc.rack": "Multifunktionsschrank", + "achievement.oc.rack.desc": "Wird unter Anderem in Kantinen genutzt, um gebrauchte Tabletts zu sammeln.", + "achievement.oc.raid": "Suche Team", + "achievement.oc.raid.desc": "Heroic plzkthx.", + "achievement.oc.ram": "Random Access Memories", + "achievement.oc.ram.desc": "Congratulations, you're Doin' It Right.", + "achievement.oc.redstoneCard": "Kontakt", + "achievement.oc.redstoneCard.desc": "Zeit für analoge Signale.", + "achievement.oc.redstoneIO": "Außenseiter", + "achievement.oc.redstoneIO.desc": "Redstone-Signale, dort wo du sie brauchst.", + "achievement.oc.robot": "Beep Boop", + "achievement.oc.robot.desc": "VERNICHTEN!", + "achievement.oc.screen": "Hast du schon versucht, ihn aus- und wieder anzuschalten?", + "achievement.oc.screen.desc": "Nein, ernsthaft. Ein Redstone-Impuls kann wirklich einen Bildschirm an- und ausschalten.", + "achievement.oc.server": "Dediziert", + "achievement.oc.server.desc": "Hier kommen die Cloud-Dienste.", + "achievement.oc.switch": "Paketdienst", + "achievement.oc.switch.desc": "Nicht für Hinkelsteine verwenden, die könnten sonst beim Aussortieren verloren gehen.", + "achievement.oc.tablet": "Verschluckbare Kleinteile", + "achievement.oc.tablet.desc": "Von Kindern fernhalten, um unerwünschte Kreditkartenbelastungen zu vermeiden.", + "achievement.oc.transistor": "Schöne Grüße an Red!", + "achievement.oc.transistor.desc": "Jetzt kannst du dir den Soundtrack anhören.", + "achievement.oc.wirelessNetworkCard": "Das WLAN - Unendliche Weiten", + "achievement.oc.wirelessNetworkCard.desc": "Netzwerke, die nie ein Paket zuvor gesehen hat.", + "death.attack.oc.nanomachinesOverload.1": "%s ist zu gierig geworden.", + "death.attack.oc.nanomachinesOverload.2": "%s erlitt einen Nervenzusammenbruch.", + "death.attack.oc.nanomachinesOverload.3": "Die Nanomaschinen von %s gerieten wohl außer Kontrolle. Ups.", + "death.attack.oc.nanomachinesHungry.1": "%s wurde von Nanomaschinen verspeist.", + "death.attack.oc.nanomachinesHungry.2": "%s hat wohl seine Nanomaschinen nicht gefüttert.", + "death.attack.oc.nanomachinesHungry.3": "%s wurde verdaut.", + "nei.options.inventory.oredict": "OreDictionary-Namen anzeigen", + "nei.options.inventory.oredict.true": "True", + "nei.options.inventory.oredict.false": "False", + "nei.usage.oc.Manual": "Handbuch öffnen", + "option.oc.address": "Adresse", + "option.oc.componentName": "Komponentenname", + "option.oc.energy": "Energie" +} diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang deleted file mode 100644 index 67527659fa..0000000000 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ /dev/null @@ -1,484 +0,0 @@ -# This is the english master file for localizations. It will always be the most -# up-to-date version, so other localizations should be based on this one. -# Use [nl] to for a line break. - -# Blocks -tile.oc.adapter.name=Adapter -tile.oc.assembler.name=Electronics Assembler -tile.oc.cable.name=Cable -tile.oc.capacitor.name=Capacitor -tile.oc.carpetedcapacitor.name=Carpeted Capacitor -tile.oc.case1.name=Computer Case (Tier 1) -tile.oc.case2.name=Computer Case (Tier 2) -tile.oc.case3.name=Computer Case (Tier 3) -tile.oc.casecreative.name=Computer Case (Creative) -tile.oc.chameliumblock.name=Block of Chamelium -tile.oc.charger.name=Charger -tile.oc.disassembler.name=Disassembler -tile.oc.diskdrive.name=Disk Drive -tile.oc.endstone.name=End Stone -tile.oc.geolyzer.name=Geolyzer -tile.oc.hologram1.name=Hologram Projector (Tier 1) -tile.oc.hologram2.name=Hologram Projector (Tier 2) -tile.oc.keyboard.name=Keyboard -tile.oc.microcontroller.name=Microcontroller -tile.oc.motionsensor.name=Motion Sensor -tile.oc.netsplitter.name=Net Splitter -tile.oc.powerconverter.name=Power Converter -tile.oc.powerdistributor.name=Power Distributor -tile.oc.print.name=3D Print -tile.oc.printer.name=3D Printer -tile.oc.raid.name=Raid -tile.oc.redstone.name=Redstone I/O -tile.oc.relay.name=Relay -tile.oc.robot.name=Robot -tile.oc.robotafterimage.name=Robot -tile.oc.screen1.name=Screen (Tier 1) -tile.oc.screen2.name=Screen (Tier 2) -tile.oc.screen3.name=Screen (Tier 3) -tile.oc.rack.name=Rack -tile.oc.transposer.name=Transposer -tile.oc.waypoint.name=Waypoint - -# Items -item.oc.abstractbuscard.name=Abstract Bus Card -item.oc.acid.name=Grog -item.oc.alu.name=Arithmetic Logic Unit (ALU) -item.oc.analyzer.name=Analyzer -item.oc.apu0.name=Accelerated Processing Unit (APU) (Tier 2) -item.oc.apu1.name=Accelerated Processing Unit (APU) (Tier 3) -item.oc.apu2.name=Accelerated Processing Unit (APU) (Creative) -item.oc.arrowkeys.name=Arrow Keys -item.oc.buttongroup.name=Button Group -item.oc.cardbase.name=Card Base -item.oc.chamelium.name=Chamelium -item.oc.circuitboard.name=Circuit Board -item.oc.componentbus0.name=Component Bus (Tier 1) -item.oc.componentbus1.name=Component Bus (Tier 2) -item.oc.componentbus2.name=Component Bus (Tier 3) -item.oc.componentbus3.name=Component Bus (Creative) -item.oc.controlunit.name=Control Unit (CU) -item.oc.cpu0.name=Central Processing Unit (CPU) (Tier 1) -item.oc.cpu1.name=Central Processing Unit (CPU) (Tier 2) -item.oc.cpu2.name=Central Processing Unit (CPU) (Tier 3) -item.oc.cuttingwire.name=Cutting Wire -item.oc.datacard0.name=Data Card (Tier 1) -item.oc.datacard1.name=Data Card (Tier 2) -item.oc.datacard2.name=Data Card (Tier 3) -item.oc.debugcard.name=Debug Card -item.oc.debugger.name=Network Debugger -item.oc.diamondchip.name=Diamond Chip -item.oc.disk.name=Disk Platter -item.oc.diskdrivemountable.name=Disk Drive -item.oc.drone.name=Drone -item.oc.dronecase0.name=Drone Case (Tier 1) -item.oc.dronecase1.name=Drone Case (Tier 2) -item.oc.dronecase3.name=Drone Case (Creative) -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=Floppy Disk -item.oc.graphicscard0.name=Graphics Card (Tier 1) -item.oc.graphicscard1.name=Graphics Card (Tier 2) -item.oc.graphicscard2.name=Graphics Card (Tier 3) -item.oc.harddiskdrive0.name=Hard Disk Drive (Tier 1) -item.oc.harddiskdrive1.name=Hard Disk Drive (Tier 2) -item.oc.harddiskdrive2.name=Hard Disk Drive (Tier 3) -item.oc.hoverboots.name=Hover Boots -item.oc.inkcartridge.name=Ink Cartridge -item.oc.inkcartridgeempty.name=Ink Cartridge (Empty) -item.oc.internetcard.name=Internet Card -item.oc.interweb.name=Interweb -item.oc.ironnugget.name=Iron Nugget -item.oc.linkedcard.name=Linked Card -item.oc.manual.name=OpenComputers Manual -item.oc.memory0.name=Memory (Tier 1) -item.oc.memory1.name=Memory (Tier 1.5) -item.oc.memory2.name=Memory (Tier 2) -item.oc.memory3.name=Memory (Tier 2.5) -item.oc.memory4.name=Memory (Tier 3) -item.oc.memory5.name=Memory (Tier 3.5) -item.oc.microchip0.name=Microchip (Tier 1) -item.oc.microchip1.name=Microchip (Tier 2) -item.oc.microchip2.name=Microchip (Tier 3) -item.oc.microcontrollercase0.name=Microcontroller Case (Tier 1) -item.oc.microcontrollercase1.name=Microcontroller Case (Tier 2) -item.oc.microcontrollercase3.name=Microcontroller Case (Creative) -item.oc.nanomachines.name=Nanomachines -item.oc.networkcard.name=Network Card -item.oc.numpad.name=Numeric Keypad -item.oc.present.name=A little something... -item.oc.printedcircuitboard.name=Printed Circuit Board (PCB) -item.oc.rawcircuitboard.name=Raw Circuit Board -item.oc.redstonecard0.name=Redstone Card (Tier 1) -item.oc.redstonecard1.name=Redstone Card (Tier 2) -item.oc.server0.name=Server (Tier 1) -item.oc.server1.name=Server (Tier 2) -item.oc.server2.name=Server (Tier 3) -item.oc.server3.name=Server (Creative) -item.oc.tablet.name=Tablet -item.oc.tabletcase0.name=Tablet Case (Tier 1) -item.oc.tabletcase1.name=Tablet Case (Tier 2) -item.oc.tabletcase3.name=Tablet Case (Creative) -item.oc.terminal.name=Remote Terminal -item.oc.terminalserver.name=Terminal Server -item.oc.texturepicker.name=Texture Picker -item.oc.transistor.name=Transistor -item.oc.upgradeangel.name=Angel Upgrade -item.oc.upgradebattery0.name=Battery Upgrade (Tier 1) -item.oc.upgradebattery1.name=Battery Upgrade (Tier 2) -item.oc.upgradebattery2.name=Battery Upgrade (Tier 3) -item.oc.upgradechunkloader.name=Chunkloader Upgrade -item.oc.upgradecontainercard0.name=Card Container (Tier 1) -item.oc.upgradecontainercard1.name=Card Container (Tier 2) -item.oc.upgradecontainercard2.name=Card Container (Tier 3) -item.oc.upgradecontainerupgrade0.name=Upgrade Container (Tier 1) -item.oc.upgradecontainerupgrade1.name=Upgrade Container (Tier 2) -item.oc.upgradecontainerupgrade2.name=Upgrade Container (Tier 3) -item.oc.upgradecrafting.name=Crafting Upgrade -item.oc.upgradedatabase0.name=Database Upgrade (Tier 1) -item.oc.upgradedatabase1.name=Database Upgrade (Tier 2) -item.oc.upgradedatabase2.name=Database Upgrade (Tier 3) -item.oc.upgradeexperience.name=Experience Upgrade -item.oc.upgradegenerator.name=Generator Upgrade -item.oc.upgradehover0.name=Hover Upgrade (Tier 1) -item.oc.upgradehover1.name=Hover Upgrade (Tier 2) -item.oc.upgradeinventory.name=Inventory Upgrade -item.oc.upgradeinventorycontroller.name=Inventory Controller Upgrade -item.oc.upgradeleash.name=Leash Upgrade -item.oc.upgrademf.name=MFU -item.oc.upgradenavigation.name=Navigation Upgrade -item.oc.upgradepiston.name=Piston Upgrade -item.oc.upgradestickypiston.name=Sticky Piston Upgrade -item.oc.upgradesign.name=Sign I/O Upgrade -item.oc.upgradesolargenerator.name=Solar Generator Upgrade -item.oc.upgradetank.name=Tank Upgrade -item.oc.upgradetankcontroller.name=Tank Controller Upgrade -item.oc.upgradetractorbeam.name=Tractor Beam Upgrade -item.oc.upgradetrading.name=Trading Upgrade -item.oc.wirelessnetworkcard0.name=Wireless Network Card (Tier 1) -item.oc.wirelessnetworkcard1.name=Wireless Network Card (Tier 2) -item.oc.worldsensorcard.name=World Sensor Card -item.oc.wrench.name=Scrench - -# Entities -entity.oc.Drone.name=Drone - -# GUI -oc:gui.Analyzer.Address=§6Address§f: %s -oc:gui.Analyzer.AddressCopied=Address copied to clipboard. -oc:gui.Analyzer.ChargerSpeed=§6Charge speed§f: %s -oc:gui.Analyzer.ComponentName=§6Component name§f: %s -oc:gui.Analyzer.Components=§6Number of connected components§f: %s -oc:gui.Analyzer.CopyToClipboard=Click to copy to clipboard. -oc:gui.Analyzer.LastError=§6Last error§f: %s -oc:gui.Analyzer.RobotName=§6Name§f: %s -oc:gui.Analyzer.RobotOwner=§6Owner§f: %s -oc:gui.Analyzer.RobotXp=§6Experience§f: %s (Level %s) -oc:gui.Analyzer.StoredEnergy=§6Stored energy§f: %s -oc:gui.Analyzer.TotalEnergy=§6Total stored energy§f: %s -oc:gui.Analyzer.Users=§6Users§f: %s -oc:gui.Analyzer.WirelessStrength=§6Signal strength§f: %s -oc:gui.Assembler.Collect=Collect output -oc:gui.Assembler.Complexity=Complexity: %s/%s -oc:gui.Assembler.InsertCase=Insert a base part -oc:gui.Assembler.InsertCPU=Insert a CPU -oc:gui.Assembler.InsertRAM=Insert some RAM -oc:gui.Assembler.Progress=Progress: %s%% (%s) -oc:gui.Assembler.Run=Assemble -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Graphics Card -oc:gui.Assembler.Warning.Inventory=Inventory Upgrade -oc:gui.Assembler.Warning.Keyboard=Keyboard -oc:gui.Assembler.Warning.OS=Bootable Medium -oc:gui.Assembler.Warning.Screen=Screen -oc:gui.Assembler.Warnings=§eWarning§7: Recommended components are missing. -oc:gui.Chat.NewVersion=A new version is available: %s -oc:gui.Chat.TextureName=§7Texture name is §a%s§f. -oc:gui.Chat.WarningClassTransformer=There were §cerrors§f running the class transformer. Please report this, together with your (full!) FML §alatest.log§f/§afml-server-latest.log§f logfile, thank you! -oc:gui.Chat.WarningFingerprint=§cWARNING§f - fingerprint mismatch! Expected '§a%s§f' but got '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with. -oc:gui.Chat.WarningLink=Could not open link: %s -oc:gui.Chat.WarningLuaFallback=Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads. -oc:gui.Chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red. -oc:gui.Chat.WarningRecipes=There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information. -oc:gui.Chat.WarningSimpleComponent=An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information. -oc:gui.Drive.Managed=Managed -oc:gui.Drive.Unmanaged=Unmanaged -oc:gui.Drive.ReadOnlyLock=Lock -oc:gui.Drive.ReadOnlyLockWarning=§lRead Only§r lock. Cannot be removed unless the drive is wiped. -oc:gui.Drive.Warning=§lWarning§r: switching modes will result in a loss of all data currently stored on the disk! -oc:gui.Error.ComponentOverflow=Too many components connected to the computer. -oc:gui.Error.InternalError=Internal error, please see the log file. This is probably a bug. -oc:gui.Error.NoCPU=No CPU is installed in the computer. -oc:gui.Error.NoEnergy=Not enough energy. -oc:gui.Error.NoRAM=No RAM is installed in the computer. -oc:gui.Error.OutOfMemory=Out of memory. -oc:gui.Manual.Blocks=OpenComputers Blocks -oc:gui.Manual.Home=Home -oc:gui.Manual.Items=OpenComputers Items -oc:gui.Manual.Warning.BlockMissing=Block unavailable. -oc:gui.Manual.Warning.ImageMissing=Image not found. -oc:gui.Manual.Warning.ItemMissing=Item unavailable. -oc:gui.Manual.Warning.OreDictMissing=Ore dictionary entry unavailable. -oc:gui.Raid.Warning=§4Adding a disk wipes it.[nl] Removing a disk wipes the raid. -oc:gui.Robot.Power=Energy -oc:gui.Robot.TurnOff=Turn off -oc:gui.Robot.TurnOn=Turn on[nl]§7Use an Analyzer to troubleshoot errors.§r -oc:gui.Rack.Back=Back -oc:gui.Rack.Bottom=Bottom -oc:gui.Rack.Left=Left -oc:gui.Rack.None=None -oc:gui.Rack.Right=Right -oc:gui.Rack.Enabled=Enabled -oc:gui.Rack.Disabled=Disabled -oc:gui.Rack.RelayModeTooltip=Relay Mode -oc:gui.Rack.Top=Top -oc:gui.Switch.PacketsPerCycle=Packets / cycle -oc:gui.Switch.QueueSize=Queue size -oc:gui.Switch.TransferRate=Cycle rate -oc:gui.Terminal.InvalidKey=Invalid key, most likely another terminal has been bound to the server. -oc:gui.Terminal.OutOfRange=No signal. - -# Containers -oc:container.adapter=Adapter -oc:container.case=Computer -oc:container.charger=Charger -oc:container.disassembler=Disassembler -oc:container.diskdrive=Disk Drive -oc:container.printer=Printer -oc:container.raid=Raid -oc:container.relay=Relay -oc:container.server=Server -oc:container.rack=Rack -oc:container.tabletwrapper=Tablet - -# Keybinds -key.clipboardPaste=Paste Clipboard - -# Item / Block Tooltips -oc:tooltip.abstractbuscard=Allows interacting with §fStargateTech 2§7's abstract bus by sending and receiving LIP packets. -oc:tooltip.acid=A highly toxic pseudo-liquid, usually only consumed by certain pirates. May prove to be useful in other ways, too, however. -oc:tooltip.adapter=Used to control non-component blocks, such as vanilla blocks or blocks from other mods. -oc:tooltip.alu=Adds numbers so you don't have to. It might be better this way. -oc:tooltip.analyzer=Used to display information about blocks, such as their §faddress§7 and §fcomponent name§7.[nl] Also displays the error that caused a computer to crash if it did not shut down normally. -oc:tooltip.apu=This is a CPU with an integrated GPU (or IGP), when you just need that extra card slot.[nl] Supported components: §f%s§7[nl] Maximum resolution: §f%sx%s§7[nl] Maximum color depth: §f%s§7[nl] Operations/tick: §f%s§7 -oc:tooltip.assembler=Allows constructing robots and other devices from a number of different computer parts. -oc:tooltip.cable=A cheap way of connecting blocks. -oc:tooltip.capacitor=Stores energy for later use. Can be filled and emptied very quickly. -oc:tooltip.carpetedcapacitor=Stores energy for later use. Can be filled and emptied very quickly. Charges when Sheep or Ocelots walk on it -oc:tooltip.cardbase=As the name indicates, this is the basic building block for all expansion cards. -oc:tooltip.case=The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.[nl] Slots: §f%s§7 -oc:tooltip.chamelium=Raw material for 3D prints. Do not swallow: may lead to blindness and temporary lack of presence. -oc:tooltip.chameliumblock=Nice and clean. Handy for tinted shapes in 3D prints, or just for having a clean, colored block to decorate your fancy base with. -oc:tooltip.charger=Transfers energy from capacitors into adjacent robots and drones. The transfer rate depends on the incoming §fredstone signal§7, where no signal means don't charge devices, and maximum strength means charge at full speed. Can also be used to charge tablets and access hard drives in tablets. -oc:tooltip.circuitboard=Now we're getting somewhere. Can be etched to obtain a printed circuit board. -oc:tooltip.controlunit=This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important. -oc:tooltip.componentbus=This expansion allows servers to communicate with more components at the same time, similar to how CPUs do.[nl] Supported components: §f%s§7 -oc:tooltip.cpu=An essential component of all computers. The clock rate is a bit unreliable, but what do you expect when it runs on a pocket sundial?[nl] Supported components: §f%s§7 -oc:tooltip.cpu.Architecture=Architecture: §f%s§7 -oc:tooltip.cuttingwire=Used to cut clay blocks into circuit board shape. Breaks after one use, which probably makes it the most inefficient tool ever. -oc:tooltip.datacard0=Provides a couple of advanced algorithms such as hashing and deflate/inflate. -oc:tooltip.datacard1=Provides a couple of advanced algorithms such as hashing, AES encryption and deflate/inflate. -oc:tooltip.datacard2=Provides a couple of advanced algorithms such as hashing, AES encryption, elliptic curve cryptography and deflate/inflate. -oc:tooltip.debugcard=Creative mode item, allows manipulating the world to make testing easier. Use at your own peril. -oc:tooltip.debugger=Can be used to output debug information on OC's internal network grid. Only use if so instructed by a dev. -oc:tooltip.diamondchip=A small piece of a once radiant diamond. It will never be the same again. -oc:tooltip.disassembler=Separates items into their original components. §lWarning§7: returned items have a %s%% chance of breaking in the process! -oc:tooltip.disk=Primitive medium that can be used to build persistent storage devices. -oc:tooltip.diskdrive.CC=ComputerCraft floppies are §asupported§7. -oc:tooltip.diskdrive=Allows reading and writing floppies. Can be installed in robots to allow inserting floppies later on. -oc:tooltip.diskdrivemountable=Provides the same functionality as a normal disk drive, but must be installed in a rack. -oc:tooltip.diskusage=Disk usage: %s/%s Byte -oc:tooltip.disklocked=Locked by: %s -oc:tooltip.diskmodemanaged=Mode: Managed -oc:tooltip.diskmodeunmanaged=Mode: Unmanaged -oc:tooltip.drone=Drones are light-weight, fast reconnaissance units with limited cargo space. -oc:tooltip.dronecase=This casing is used to build Drones in the assembler. It has room for a small amount of components and provides endstone-powered levitation. -oc:tooltip.eeprom=Small, programmable storage that contains the BIOS computers use to boot. -oc:tooltip.fakeendstone=Almost as good as the real thing, even emulates its floatiness! -oc:tooltip.geolyzer=Allows scanning the surrounding area's blocks' hardness. This information can be useful for generating holograms of the area or for detecting ores. -oc:tooltip.graphicscard=Used to change what's displayed on screens.[nl] Maximum resolution: §f%sx%s§7[nl] Maximum color depth: §f%s§7[nl] Operations/tick: §f%s§7 -oc:tooltip.hoverboots=Jump higher, fall deeper, walk better. This and more, with the new and patented Hover Boots (TM). -oc:tooltip.inkcartridge=Used to refill ink in 3D printers. For mysterious reasons it does not have to remain in the printer. -oc:tooltip.inkcartridgeempty=This ink cartridge has been sucked dry. Refill it using dyes. Or throw it away. See if I care. -oc:tooltip.internetcard=This card allows making HTTP requests and using real TCP sockets. -oc:tooltip.interweb=Congratulations, you win one (1) interweb. You can connect to it using an Internet Card. Beware: don't feed the trolls. -oc:tooltip.ironnugget=A nugget made of iron, that's why it's called an Iron Nugget, duh... -oc:tooltip.keyboard=Can be attached to screens to allow typing on them. -oc:tooltip.hologram0=A volumetric display that can be controlled by computers to display arbitrary voxel structures.[nl] Resolution: §f48x32x48§7 [nl] Maximum scale: §f3x§7 [nl] Color depth: §fMonochrome§7 -oc:tooltip.hologram1=A volumetric display that can be controlled by computers to display arbitrary voxel structures.[nl] Resolution: §f48x32x48§7 [nl] Maximum scale: §f4x§7 [nl] Color depth: §fTricolor§7 -oc:tooltip.linkedcard=These are crafted in pairs, and can only communicate with their partner card. However, they can communicate across any distance, and even across dimensions. The energy required to send a message is fairly high, though. -oc:tooltip.linkedcard_channel=§8Channel: %s§7 -oc:tooltip.manual=All the information you could possibly need about OpenComputers. And more. For the unbelievably low price of... §oplease press R to continue§7. -oc:tooltip.memory=Required to get computers to run. The more you have, the more complex the programs you can run. -oc:tooltip.microchip=The chip formerly known as Integrated Circuit. I have no idea why this works with redstone, but it does. -oc:tooltip.microcontroller=Microcontrollers are computers boiled down to the essentials. They are intended to take care of very specific tasks, running only a single program that is provided on the EEPROM built into them.[nl] §cCan not connect to external components.§7 -oc:tooltip.microcontrollercase=Base component for building microcontrollers. Place it into an assembler to add further components and assemble a microcontroller. -oc:tooltip.motionsensor=Can detect movement of nearby living beings. Requires clear line-of-sight. -oc:tooltip.nanomachines=Control unit and a bunch of nanomachines for ingestion, if you dare. -oc:tooltip.networkcard=Allows distant computers connected by other blocks (such as cable) to communicate by sending messages to each other. -oc:tooltip.poweracceptor=Energy conversion speed: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fFactorization Charge§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Converts power from other mods to the internal energy type. Conversion rates: -oc:tooltip.powerdistributor=Distributes energy among different networks. This is useful for sharing power fed into your system from one converter among different sub-networks that should remain separate. -oc:tooltip.present=... for your troubles. Open this present for a chance to receive some §kphat lewt§7![nl]§8Craft OpenComputers items when the time is right for a chance to receive a present.§7 -oc:tooltip.print.BeaconBase=§8Works as a beacon base. -oc:tooltip.print.LightValue=§8Light emitted: %s. -oc:tooltip.print.RedstoneLevel=§8Redstone output: %s. -oc:tooltip.printedcircuitboard=The basic building block for expansion cards and memory and such. -oc:tooltip.printer=Allows printing blocks of user-defined shapes using Chamelium and Ink Cartridges. Must be configured using a computer. Keep away from small children. Because reasons. -oc:tooltip.raid=Allows combining three hard drives into one larger file system that can be used by all connected computers. -oc:tooltip.rawcircuitboard=Can be hardened in any furnace compatible oven. -oc:tooltip.redstone=Allows reading and emitting redstone signals around the block. Can be controlled by any computer the block is connected to. This is basically like an external redstone card. -oc:tooltip.redstonecard.Charset=§fSimpleLogic§7 is §asupported§7. -oc:tooltip.redstonecard.ProjectRed=§fProjectRed§7 is §asupported§7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 is §asupported§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 is §asupported§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 is §asupported§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 is §asupported§7. -oc:tooltip.redstonecard=Allows reading and emitting redstone signals around the computer or robot. -oc:tooltip.relay=Allows connecting different networks to each other. Only network messages will be passed along, components will not be visible through this. Use this to separate networks while still allowing communication using Network Cards, for example. -oc:tooltip.robot=Unlike computers, robots can move around and interact with the world much like a player can.[nl] §cCan not connect to external components.§7 -# the underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fLevel§7: §a%s§7 -oc:tooltip.robot_storedenergy=§fStored energy§7: §a%s§7 -oc:tooltip.screen=Display text, controlled by a Graphics Card in a Case.[nl] Maximum resolution: §f%sx%s§7[nl] Maximum color depth: §f%s§7 -oc:tooltip.server=This is a server, there are many like it, but this one can be upgraded with components much like a computer case can be. It can be run by inserting it into a server rack. -oc:tooltip.server.Components=Installed components: -oc:tooltip.rack=Allows the installation of up to four servers or other rack mountables. -oc:tooltip.tablet=A tablet computer, for fresh Lua on the go. Can be forced to shut down by sneak-activating it. -oc:tooltip.tabletcase=Basic case for tablets. Place it into the assembler to add in components and create a tablet computer. -oc:tooltip.terminal=Allows controlling a server remotely, as long as you are in range of it. Acts like a portable screen and keyboard. Shift-right-click a server in a server rack to bind the terminal to it. -oc:tooltip.terminalserver=Backend to which Remote Terminals can be connected to provide remote control. Houses a virtual screen and keyboard. -oc:tooltip.texturepicker=This tool allows showing a string describing a block's surface, for use in 3D printer shape definitions. Totally not texture names, nope. No sir. -oc:tooltip.tier=§8Tier %s -oc:tooltip.netsplitter=Acts as a dynamic connector. Connectivity of each side can be toggled by hitting it with a wrench. Connectivity of all sides can be inverted by applying a redstone signal. -oc:tooltip.toolong=Hold [§f%s§7] for a detailed tooltip. -oc:tooltip.transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job. -oc:tooltip.transposer=Allows automated transferral of items and fluids between adjacent inventories and fluid containers. -oc:tooltip.upgradeangel=Allows robots to place blocks in thin air, even if there is no point of reference. -oc:tooltip.upgradebattery=Increase the amount of energy a device can store, allowing it work longer without having to be recharged. [nl] Capacity: §f%s§7 -oc:tooltip.upgradechunkloader=If a robot moves in a forest and no one is around to see it, does it really move? This upgrades makes sure it does. It keeps the chunk a device is in loaded, but continually consumes energy while active. -oc:tooltip.upgradecontainercard=This container upgrade allows dynamically installing and removing a card from an assembled device. [nl] Maximum Tier: §f%s§7 -oc:tooltip.upgradecontainerupgrade=This container upgrade allows dynamically installing and removing another upgrade from an assembled device. [nl] Maximum Tier: §f%s§7 -oc:tooltip.upgradecrafting=Enables robots to use the top left area of their inventory for crafting objects. Items have to be aligned as they would be in a crafting table. -oc:tooltip.upgradedatabase=This upgrade allows storing item stack information for later retrieval and use by other components.[nl] Supported entries: §f%s§7 -oc:tooltip.upgradeexperience=This upgrade allows robots to accumulate experience by performing various operations. The more experience they have, the more energy they can store, the faster they can harvest blocks and the more efficiently they can use tools. -oc:tooltip.upgradegenerator=Can be used to generate energy from fuel on the go. Burns items to generate energy over time, based on their fuel value.[nl] §fEfficiency§7: §a%s%%§7 -oc:tooltip.upgradehover=This upgrade allows robots to fly higher above the ground without having to climb walls.[nl] Maximum height: §f%s§7 -oc:tooltip.upgradeinventory=This upgrade provides inventory space to a robot or drone. Without one of these, they will not be able to store items internally. -oc:tooltip.upgradeinventorycontroller=This upgrade allows robots and drones more control in how it interact with external inventories, and allows robots to swap their equipped tool with an item in their inventory. -oc:tooltip.upgrademf=Allows adapters to access blocks they are not adjacent to. -oc:tooltip.upgrademf.Linked=§fConnection established§7 -oc:tooltip.upgrademf.Unlinked=§fNo connection§7 -oc:tooltip.upgradeleash=Allows some devices, such as drones, to bind Isaa- excuse me... *chatter* My apologies. I'm just being told this is actually used to put animals on a leash. Multiple animals, even. Odd. -oc:tooltip.upgradenavigation=Can be used to determine the position and orientation of a device. The position is relative to the center of the map that was used to craft this upgrade. -oc:tooltip.upgradepiston=This upgrade is very pushy. It allows moving blocks, similar to a piston. -oc:tooltip.upgradestickypiston=This upgrade can push and pull. It allows moving blocks, similar to a sticky piston. It does §lnot§7 pull entities, however. -oc:tooltip.upgradesign=Allows reading text on and writing text to signs. -oc:tooltip.upgradesolargenerator=Can be used to generate energy from sunlight on the go. Requires a clear line of sight to the sky above the device. Generates energy at %s%% of the speed of a Stirling Engine. -oc:tooltip.upgradetank=This upgrade provides a tank for fluid storage for robots and drones. Without one of these, they will not be able to store fluids internally. -oc:tooltip.upgradetankcontroller=This upgrade allows robots and drones more control in how they interact with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory. -oc:tooltip.upgradetractorbeam=Equips a device with extremely advanced technology, nicknamed the "Item Magnet". Allows the device to pick up items anywhere within 3 blocks of its location. -oc:tooltip.upgradetrading=Allows robots and drones to trade with villagers. -oc:tooltip.waypoint=Provides a point of reference to devices with a navigation upgrade. -oc:tooltip.wirelessnetworkcard=Allows wireless sending of network messages in addition to normal ones. You can adjust the §fsignal strength§7 to control how far messages are sent. Higher signal strength results in higher energy consumption. -oc:tooltip.worldsensorcard=Allows reading out information about the world, such as its gravity and whether it has a breathable atmosphere. Use results at own risk. The manufacturer takes no responsibility for bodily or material harm caused by decisions made upon the cards' outputs. We have lawyers. And money. Don't even try. -oc:tooltip.Wrench=A hybrid of Screwdriver and Wrench, this tool is easy to learn, but hard to master. - -#Achievements -achievement.oc.adapter=Plug In Baby -achievement.oc.adapter.desc=Interact with blocks from other mods and even vanilla Minecraft! -achievement.oc.assembler=Wonderful -achievement.oc.assembler.desc=Time to take over the world! -achievement.oc.cable=Not a Dirty Wire -achievement.oc.cable.desc=With patented anti-cable-spaghetti technology. -achievement.oc.capacitor=Batteries included -achievement.oc.capacitor.desc=You cannot stop it. -achievement.oc.card=We Accept Cards -achievement.oc.card.desc=For your convenience. No ulterior motive, promise. -achievement.oc.case=In Case of Trouble -achievement.oc.case.desc=Because cuboid towers are the best. -achievement.oc.charger=All right, let's do this -achievement.oc.charger.desc=Chaaaaaaaaaarg- dang, forgot the redstone signal again. -achievement.oc.chip=All the Small Things -achievement.oc.chip.desc=Because vacuum tubes are so yesteryear. -achievement.oc.cpu=Overclocked -achievement.oc.cpu.desc=Time to make good use of those computing cycles. -achievement.oc.disassembler=Scratch That -achievement.oc.disassembler.desc=In case one of your brilliant ideas turns out to be not that brilliant after all. -achievement.oc.diskDrive=Roundabout -achievement.oc.diskDrive.desc=Inferior capacity but such delicious sound. -achievement.oc.drone=Fly Away -achievement.oc.drone.desc=Keep calm and nuke it from orbit. -achievement.oc.eeprom=There can be only one -achievement.oc.eeprom.desc=Per computer, that is. For deterministic boot order, you know? -achievement.oc.floppy=The One Ri- Disk -achievement.oc.floppy.desc=Not to be confused with Flappy. -achievement.oc.geolyzer=Down to Earth -achievement.oc.geolyzer.desc=It has extraordinary qualities. -achievement.oc.graphicsCard=LastGen -achievement.oc.graphicsCard.desc=The way it's meant to be... uh... rendered. Yeah. That. -achievement.oc.hdd=Hot Dog Dealer -achievement.oc.hdd.desc=No wait, that's not what that meant. Hang on, almost got it... -achievement.oc.hologram=Next Dimension -achievement.oc.hologram.desc=Because 2D is boring. Or is it? -achievement.oc.keyboard=DirtCatcher3000 -achievement.oc.keyboard.desc=It is highly recommended to resist the urge to flip them around and shake them. -achievement.oc.microcontroller=Little Sister -achievement.oc.microcontroller.desc=The small sibling of computers. -achievement.oc.motionSensor=Got the Moves -achievement.oc.motionSensor.desc=Like Steve Swagger. -achievement.oc.networkCard=Now We're Talking! -achievement.oc.networkCard.desc=Keep in touch with those distant relatives via transitive relations. -achievement.oc.openOS=Boot -achievement.oc.openOS.desc=One OS to - wait, I used that one already? Dang. -achievement.oc.powerDistributor=Sharing is Caring -achievement.oc.powerDistributor.desc=When you need some help balancing all that power. -achievement.oc.rack=Dat Rack -achievement.oc.rack.desc=I don't know what you're thinking of, I clearly meant the server rack. -achievement.oc.raid=LFG -achievement.oc.raid.desc=Heroic plzkthx. -achievement.oc.ram=Random Access Memories -achievement.oc.ram.desc=Congratulations, you're Doin' It Right. -achievement.oc.redstoneCard=Contact -achievement.oc.redstoneCard.desc=Time to go analog. -achievement.oc.redstoneIO=The Outsider -achievement.oc.redstoneIO.desc=Taking redstone signals where you want them. -achievement.oc.robot=Beep Boop -achievement.oc.robot.desc=EXTERMINATE! -achievement.oc.screen=Have you tried turning it off and on again? -achievement.oc.screen.desc=No seriously. A redstone pulse can toggle a screen's power, after all. -achievement.oc.server=Dedicated -achievement.oc.server.desc=Cloud services, here we come. -achievement.oc.switch=Complex Topology -achievement.oc.switch.desc=Avoid fragile goods due to possibility of dropped packets. -achievement.oc.tablet=Do Not Swallow -achievement.oc.tablet.desc=Also keep away from small children to avoid unexpected overdrawing of your credit card. -achievement.oc.transistor=Tell Red I said "Hi." -achievement.oc.transistor.desc=Create a Transistor to get started. Then listen to the soundtrack. No need to thank me. -achievement.oc.wirelessNetworkCard=Signals -achievement.oc.wirelessNetworkCard.desc=Time to go where no packet has gone before. - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s got too greedy. -death.attack.oc.nanomachinesOverload.2=%s had a nervous breakdown. -death.attack.oc.nanomachinesOverload.3=The nanomachines of %s went out of control. -death.attack.oc.nanomachinesHungry.1=%s was eaten by nanomachines. -death.attack.oc.nanomachinesHungry.2=%s didn't keep their nanomachines fed. -death.attack.oc.nanomachinesHungry.3=%s has been digested. - -# NEI Integration -nei.options.inventory.oredict=Show OreDictionary names -nei.options.inventory.oredict.true=True -nei.options.inventory.oredict.false=False -nei.usage.oc.Manual=Open Manual - -# Waila Integration -option.oc.address=Address -option.oc.componentName=Component Name -option.oc.energy=Energy diff --git a/src/main/resources/assets/opencomputers/lang/en_us.json b/src/main/resources/assets/opencomputers/lang/en_us.json new file mode 100644 index 0000000000..8fb23ab819 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/en_us.json @@ -0,0 +1,459 @@ +{ + "tile.oc.adapter": "Adapter", + "tile.oc.assembler": "Electronics Assembler", + "tile.oc.cable": "Cable", + "tile.oc.capacitor": "Capacitor", + "tile.oc.carpetedcapacitor": "Carpeted Capacitor", + "tile.oc.case1": "Computer Case (Tier 1)", + "tile.oc.case2": "Computer Case (Tier 2)", + "tile.oc.case3": "Computer Case (Tier 3)", + "tile.oc.casecreative": "Computer Case (Creative)", + "tile.oc.chameliumblock": "Block of Chamelium", + "tile.oc.charger": "Charger", + "tile.oc.disassembler": "Disassembler", + "tile.oc.diskdrive": "Disk Drive", + "tile.oc.endstone": "End Stone", + "tile.oc.geolyzer": "Geolyzer", + "tile.oc.hologram1": "Hologram Projector (Tier 1)", + "tile.oc.hologram2": "Hologram Projector (Tier 2)", + "tile.oc.keyboard": "Keyboard", + "tile.oc.microcontroller": "Microcontroller", + "tile.oc.motionsensor": "Motion Sensor", + "tile.oc.netsplitter": "Net Splitter", + "tile.oc.powerconverter": "Power Converter", + "tile.oc.powerdistributor": "Power Distributor", + "tile.oc.print": "3D Print", + "tile.oc.printer": "3D Printer", + "tile.oc.raid": "Raid", + "tile.oc.redstone": "Redstone I/O", + "tile.oc.relay": "Relay", + "tile.oc.robot": "Robot", + "tile.oc.robotafterimage": "Robot", + "tile.oc.screen1": "Screen (Tier 1)", + "tile.oc.screen2": "Screen (Tier 2)", + "tile.oc.screen3": "Screen (Tier 3)", + "tile.oc.rack": "Rack", + "tile.oc.transposer": "Transposer", + "tile.oc.waypoint": "Waypoint", + "item.oc.abstractbuscard": "Abstract Bus Card", + "item.oc.acid": "Grog", + "item.oc.alu": "Arithmetic Logic Unit (ALU)", + "item.oc.analyzer": "Analyzer", + "item.oc.apu0": "Accelerated Processing Unit (APU) (Tier 2)", + "item.oc.apu1": "Accelerated Processing Unit (APU) (Tier 3)", + "item.oc.apu2": "Accelerated Processing Unit (APU) (Creative)", + "item.oc.arrowkeys": "Arrow Keys", + "item.oc.buttongroup": "Button Group", + "item.oc.cardbase": "Card Base", + "item.oc.chamelium": "Chamelium", + "item.oc.circuitboard": "Circuit Board", + "item.oc.componentbus0": "Component Bus (Tier 1)", + "item.oc.componentbus1": "Component Bus (Tier 2)", + "item.oc.componentbus2": "Component Bus (Tier 3)", + "item.oc.componentbus3": "Component Bus (Creative)", + "item.oc.controlunit": "Control Unit (CU)", + "item.oc.cpu0": "Central Processing Unit (CPU) (Tier 1)", + "item.oc.cpu1": "Central Processing Unit (CPU) (Tier 2)", + "item.oc.cpu2": "Central Processing Unit (CPU) (Tier 3)", + "item.oc.cuttingwire": "Cutting Wire", + "item.oc.datacard0": "Data Card (Tier 1)", + "item.oc.datacard1": "Data Card (Tier 2)", + "item.oc.datacard2": "Data Card (Tier 3)", + "item.oc.debugcard": "Debug Card", + "item.oc.debugger": "Network Debugger", + "item.oc.diamondchip": "Diamond Chip", + "item.oc.disk": "Disk Platter", + "item.oc.diskdrivemountable": "Disk Drive", + "item.oc.drone": "Drone", + "item.oc.dronecase0": "Drone Case (Tier 1)", + "item.oc.dronecase1": "Drone Case (Tier 2)", + "item.oc.dronecase3": "Drone Case (Creative)", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "Floppy Disk", + "item.oc.graphicscard0": "Graphics Card (Tier 1)", + "item.oc.graphicscard1": "Graphics Card (Tier 2)", + "item.oc.graphicscard2": "Graphics Card (Tier 3)", + "item.oc.harddiskdrive0": "Hard Disk Drive (Tier 1)", + "item.oc.harddiskdrive1": "Hard Disk Drive (Tier 2)", + "item.oc.harddiskdrive2": "Hard Disk Drive (Tier 3)", + "item.oc.hoverboots": "Hover Boots", + "item.oc.inkcartridge": "Ink Cartridge", + "item.oc.inkcartridgeempty": "Ink Cartridge (Empty)", + "item.oc.internetcard": "Internet Card", + "item.oc.interweb": "Interweb", + "item.oc.ironnugget": "Iron Nugget", + "item.oc.linkedcard": "Linked Card", + "item.oc.manual": "OpenComputers Manual", + "item.oc.memory0": "Memory (Tier 1)", + "item.oc.memory1": "Memory (Tier 1.5)", + "item.oc.memory2": "Memory (Tier 2)", + "item.oc.memory3": "Memory (Tier 2.5)", + "item.oc.memory4": "Memory (Tier 3)", + "item.oc.memory5": "Memory (Tier 3.5)", + "item.oc.microchip0": "Microchip (Tier 1)", + "item.oc.microchip1": "Microchip (Tier 2)", + "item.oc.microchip2": "Microchip (Tier 3)", + "item.oc.microcontrollercase0": "Microcontroller Case (Tier 1)", + "item.oc.microcontrollercase1": "Microcontroller Case (Tier 2)", + "item.oc.microcontrollercase3": "Microcontroller Case (Creative)", + "item.oc.nanomachines": "Nanomachines", + "item.oc.networkcard": "Network Card", + "item.oc.numpad": "Numeric Keypad", + "item.oc.present": "A little something...", + "item.oc.printedcircuitboard": "Printed Circuit Board (PCB)", + "item.oc.rawcircuitboard": "Raw Circuit Board", + "item.oc.redstonecard0": "Redstone Card (Tier 1)", + "item.oc.redstonecard1": "Redstone Card (Tier 2)", + "item.oc.server0": "Server (Tier 1)", + "item.oc.server1": "Server (Tier 2)", + "item.oc.server2": "Server (Tier 3)", + "item.oc.server3": "Server (Creative)", + "item.oc.tablet": "Tablet", + "item.oc.tabletcase0": "Tablet Case (Tier 1)", + "item.oc.tabletcase1": "Tablet Case (Tier 2)", + "item.oc.tabletcase3": "Tablet Case (Creative)", + "item.oc.terminal": "Remote Terminal", + "item.oc.terminalserver": "Terminal Server", + "item.oc.texturepicker": "Texture Picker", + "item.oc.transistor": "Transistor", + "item.oc.upgradeangel": "Angel Upgrade", + "item.oc.upgradebattery0": "Battery Upgrade (Tier 1)", + "item.oc.upgradebattery1": "Battery Upgrade (Tier 2)", + "item.oc.upgradebattery2": "Battery Upgrade (Tier 3)", + "item.oc.upgradechunkloader": "Chunkloader Upgrade", + "item.oc.upgradecontainercard0": "Card Container (Tier 1)", + "item.oc.upgradecontainercard1": "Card Container (Tier 2)", + "item.oc.upgradecontainercard2": "Card Container (Tier 3)", + "item.oc.upgradecontainerupgrade0": "Upgrade Container (Tier 1)", + "item.oc.upgradecontainerupgrade1": "Upgrade Container (Tier 2)", + "item.oc.upgradecontainerupgrade2": "Upgrade Container (Tier 3)", + "item.oc.upgradecrafting": "Crafting Upgrade", + "item.oc.upgradedatabase0": "Database Upgrade (Tier 1)", + "item.oc.upgradedatabase1": "Database Upgrade (Tier 2)", + "item.oc.upgradedatabase2": "Database Upgrade (Tier 3)", + "item.oc.upgradeexperience": "Experience Upgrade", + "item.oc.upgradegenerator": "Generator Upgrade", + "item.oc.upgradehover0": "Hover Upgrade (Tier 1)", + "item.oc.upgradehover1": "Hover Upgrade (Tier 2)", + "item.oc.upgradeinventory": "Inventory Upgrade", + "item.oc.upgradeinventorycontroller": "Inventory Controller Upgrade", + "item.oc.upgradeleash": "Leash Upgrade", + "item.oc.upgrademf": "MFU", + "item.oc.upgradenavigation": "Navigation Upgrade", + "item.oc.upgradepiston": "Piston Upgrade", + "item.oc.upgradestickypiston": "Sticky Piston Upgrade", + "item.oc.upgradesign": "Sign I/O Upgrade", + "item.oc.upgradesolargenerator": "Solar Generator Upgrade", + "item.oc.upgradetank": "Tank Upgrade", + "item.oc.upgradetankcontroller": "Tank Controller Upgrade", + "item.oc.upgradetractorbeam": "Tractor Beam Upgrade", + "item.oc.upgradetrading": "Trading Upgrade", + "item.oc.wirelessnetworkcard0": "Wireless Network Card (Tier 1)", + "item.oc.wirelessnetworkcard1": "Wireless Network Card (Tier 2)", + "item.oc.worldsensorcard": "World Sensor Card", + "item.oc.wrench": "Scrench", + "entity.oc.Drone": "Drone", + "oc:gui.Analyzer.Address": "§6Address§f: %s", + "oc:gui.Analyzer.AddressCopied": "Address copied to clipboard.", + "oc:gui.Analyzer.ChargerSpeed": "§6Charge speed§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Component name§f: %s", + "oc:gui.Analyzer.Components": "§6Number of connected components§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "Click to copy to clipboard.", + "oc:gui.Analyzer.LastError": "§6Last error§f: %s", + "oc:gui.Analyzer.RobotName": "§6Name§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Owner§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Experience§f: %s (Level %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Stored energy§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Total stored energy§f: %s", + "oc:gui.Analyzer.Users": "§6Users§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Signal strength§f: %s", + "oc:gui.Assembler.Collect": "Collect output", + "oc:gui.Assembler.Complexity": "Complexity: %s/%s", + "oc:gui.Assembler.InsertCase": "Insert a base part", + "oc:gui.Assembler.InsertCPU": "Insert a CPU", + "oc:gui.Assembler.InsertRAM": "Insert some RAM", + "oc:gui.Assembler.Progress": "Progress: %s%% (%s)", + "oc:gui.Assembler.Run": "Assemble", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Graphics Card", + "oc:gui.Assembler.Warning.Inventory": "Inventory Upgrade", + "oc:gui.Assembler.Warning.Keyboard": "Keyboard", + "oc:gui.Assembler.Warning.OS": "Bootable Medium", + "oc:gui.Assembler.Warning.Screen": "Screen", + "oc:gui.Assembler.Warnings": "§eWarning§7: Recommended components are missing.", + "oc:gui.Chat.NewVersion": "A new version is available: %s", + "oc:gui.Chat.TextureName": "§7Texture name is §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "There were §cerrors§f running the class transformer. Please report this, together with your (full!) FML §alatest.log§f/§afml-server-latest.log§f logfile, thank you!", + "oc:gui.Chat.WarningFingerprint": "§cWARNING§f - fingerprint mismatch! Expected '§a%s§f' but got '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with.", + "oc:gui.Chat.WarningLink": "Could not open link: %s", + "oc:gui.Chat.WarningLuaFallback": "Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads.", + "oc:gui.Chat.WarningProjectRed": "You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red.", + "oc:gui.Chat.WarningRecipes": "There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information.", + "oc:gui.Chat.WarningSimpleComponent": "An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information.", + "oc:gui.Drive.Managed": "Managed", + "oc:gui.Drive.Unmanaged": "Unmanaged", + "oc:gui.Drive.ReadOnlyLock": "Lock", + "oc:gui.Drive.ReadOnlyLockWarning": "§lRead Only§r lock. Cannot be removed unless the drive is wiped.", + "oc:gui.Drive.Warning": "§lWarning§r: switching modes will result in a loss of all data currently stored on the disk!", + "oc:gui.Error.ComponentOverflow": "Too many components connected to the computer.", + "oc:gui.Error.InternalError": "Internal error, please see the log file. This is probably a bug.", + "oc:gui.Error.NoCPU": "No CPU is installed in the computer.", + "oc:gui.Error.NoEnergy": "Not enough energy.", + "oc:gui.Error.NoRAM": "No RAM is installed in the computer.", + "oc:gui.Error.OutOfMemory": "Out of memory.", + "oc:gui.Manual.Blocks": "OpenComputers Blocks", + "oc:gui.Manual.Home": "Home", + "oc:gui.Manual.Items": "OpenComputers Items", + "oc:gui.Manual.Warning.BlockMissing": "Block unavailable.", + "oc:gui.Manual.Warning.ImageMissing": "Image not found.", + "oc:gui.Manual.Warning.ItemMissing": "Item unavailable.", + "oc:gui.Manual.Warning.OreDictMissing": "Ore dictionary entry unavailable.", + "oc:gui.Raid.Warning": "§4Adding a disk wipes it.\nRemoving a disk wipes the raid.", + "oc:gui.Robot.Power": "Energy", + "oc:gui.Robot.TurnOff": "Turn off", + "oc:gui.Robot.TurnOn": "Turn on\n§7Use an Analyzer to troubleshoot errors.§r", + "oc:gui.Rack.Back": "Back", + "oc:gui.Rack.Bottom": "Bottom", + "oc:gui.Rack.Left": "Left", + "oc:gui.Rack.None": "None", + "oc:gui.Rack.Right": "Right", + "oc:gui.Rack.Enabled": "Enabled", + "oc:gui.Rack.Disabled": "Disabled", + "oc:gui.Rack.RelayModeTooltip": "Relay Mode", + "oc:gui.Rack.Top": "Top", + "oc:gui.Switch.PacketsPerCycle": "Packets / cycle", + "oc:gui.Switch.QueueSize": "Queue size", + "oc:gui.Switch.TransferRate": "Cycle rate", + "oc:gui.Terminal.InvalidKey": "Invalid key, most likely another terminal has been bound to the server.", + "oc:gui.Terminal.OutOfRange": "No signal.", + "oc:container.adapter": "Adapter", + "oc:container.case": "Computer", + "oc:container.charger": "Charger", + "oc:container.disassembler": "Disassembler", + "oc:container.diskdrive": "Disk Drive", + "oc:container.printer": "Printer", + "oc:container.raid": "Raid", + "oc:container.relay": "Relay", + "oc:container.server": "Server", + "oc:container.rack": "Rack", + "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.clipboardPaste": "Paste Clipboard", + "oc:tooltip.abstractbuscard": "Allows interacting with §fStargateTech 2§7's abstract bus by sending and receiving LIP packets.", + "oc:tooltip.acid": "A highly toxic pseudo-liquid, usually only consumed by certain pirates. May prove to be useful in other ways, too, however.", + "oc:tooltip.adapter": "Used to control non-component blocks, such as vanilla blocks or blocks from other mods.", + "oc:tooltip.alu": "Adds numbers so you don't have to. It might be better this way.", + "oc:tooltip.analyzer": "Used to display information about blocks, such as their §faddress§7 and §fcomponent name§7.\nAlso displays the error that caused a computer to crash if it did not shut down normally.", + "oc:tooltip.apu": "This is a CPU with an integrated GPU (or IGP), when you just need that extra card slot.\nSupported components: §f%s§7\nMaximum resolution: §f%sx%s§7\nMaximum color depth: §f%s§7\nOperations/tick: §f%s§7", + "oc:tooltip.assembler": "Allows constructing robots and other devices from a number of different computer parts.", + "oc:tooltip.cable": "A cheap way of connecting blocks.", + "oc:tooltip.capacitor": "Stores energy for later use. Can be filled and emptied very quickly.", + "oc:tooltip.carpetedcapacitor": "Stores energy for later use. Can be filled and emptied very quickly. Charges when Sheep or Ocelots walk on it", + "oc:tooltip.cardbase": "As the name indicates, this is the basic building block for all expansion cards.", + "oc:tooltip.case": "The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.\nSlots: §f%s§7", + "oc:tooltip.chamelium": "Raw material for 3D prints. Do not swallow: may lead to blindness and temporary lack of presence.", + "oc:tooltip.chameliumblock": "Nice and clean. Handy for tinted shapes in 3D prints, or just for having a clean, colored block to decorate your fancy base with.", + "oc:tooltip.charger": "Transfers energy from capacitors into adjacent robots and drones. The transfer rate depends on the incoming §fredstone signal§7, where no signal means don't charge devices, and maximum strength means charge at full speed. Can also be used to charge tablets and access hard drives in tablets.", + "oc:tooltip.circuitboard": "Now we're getting somewhere. Can be etched to obtain a printed circuit board.", + "oc:tooltip.controlunit": "This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important.", + "oc:tooltip.componentbus": "This expansion allows servers to communicate with more components at the same time, similar to how CPUs do.\nSupported components: §f%s§7", + "oc:tooltip.cpu": "An essential component of all computers. The clock rate is a bit unreliable, but what do you expect when it runs on a pocket sundial?\nSupported components: §f%s§7", + "oc:tooltip.cpu.Architecture": "Architecture: §f%s§7", + "oc:tooltip.cuttingwire": "Used to cut clay blocks into circuit board shape. Breaks after one use, which probably makes it the most inefficient tool ever.", + "oc:tooltip.datacard0": "Provides a couple of advanced algorithms such as hashing and deflate/inflate.", + "oc:tooltip.datacard1": "Provides a couple of advanced algorithms such as hashing, AES encryption and deflate/inflate.", + "oc:tooltip.datacard2": "Provides a couple of advanced algorithms such as hashing, AES encryption, elliptic curve cryptography and deflate/inflate.", + "oc:tooltip.debugcard": "Creative mode item, allows manipulating the world to make testing easier. Use at your own peril.", + "oc:tooltip.debugger": "Can be used to output debug information on OC's internal network grid. Only use if so instructed by a dev.", + "oc:tooltip.diamondchip": "A small piece of a once radiant diamond. It will never be the same again.", + "oc:tooltip.disassembler": "Separates items into their original components. §lWarning§7: returned items have a %s%% chance of breaking in the process!", + "oc:tooltip.disk": "Primitive medium that can be used to build persistent storage devices.", + "oc:tooltip.diskdrive.CC": "ComputerCraft floppies are §asupported§7.", + "oc:tooltip.diskdrive": "Allows reading and writing floppies. Can be installed in robots to allow inserting floppies later on.", + "oc:tooltip.diskdrivemountable": "Provides the same functionality as a normal disk drive, but must be installed in a rack.", + "oc:tooltip.diskusage": "Disk usage: %s/%s Byte", + "oc:tooltip.disklocked": "Locked by: %s", + "oc:tooltip.diskmodemanaged": "Mode: Managed", + "oc:tooltip.diskmodeunmanaged": "Mode: Unmanaged", + "oc:tooltip.drone": "Drones are light-weight, fast reconnaissance units with limited cargo space.", + "oc:tooltip.dronecase": "This casing is used to build Drones in the assembler. It has room for a small amount of components and provides endstone-powered levitation.", + "oc:tooltip.eeprom": "Small, programmable storage that contains the BIOS computers use to boot.", + "oc:tooltip.fakeendstone": "Almost as good as the real thing, even emulates its floatiness!", + "oc:tooltip.geolyzer": "Allows scanning the surrounding area's blocks' hardness. This information can be useful for generating holograms of the area or for detecting ores.", + "oc:tooltip.graphicscard": "Used to change what's displayed on screens.\nMaximum resolution: §f%sx%s§7\nMaximum color depth: §f%s§7\nOperations/tick: §f%s§7", + "oc:tooltip.hoverboots": "Jump higher, fall deeper, walk better. This and more, with the new and patented Hover Boots (TM).", + "oc:tooltip.inkcartridge": "Used to refill ink in 3D printers. For mysterious reasons it does not have to remain in the printer.", + "oc:tooltip.inkcartridgeempty": "This ink cartridge has been sucked dry. Refill it using dyes. Or throw it away. See if I care.", + "oc:tooltip.internetcard": "This card allows making HTTP requests and using real TCP sockets.", + "oc:tooltip.interweb": "Congratulations, you win one (1) interweb. You can connect to it using an Internet Card. Beware: don't feed the trolls.", + "oc:tooltip.ironnugget": "A nugget made of iron, that's why it's called an Iron Nugget, duh...", + "oc:tooltip.keyboard": "Can be attached to screens to allow typing on them.", + "oc:tooltip.hologram0": "A volumetric display that can be controlled by computers to display arbitrary voxel structures.\nResolution: §f48x32x48§7\nMaximum scale: §f3x§7\nColor depth: §fMonochrome§7", + "oc:tooltip.hologram1": "A volumetric display that can be controlled by computers to display arbitrary voxel structures.\nResolution: §f48x32x48§7\nMaximum scale: §f4x§7\nColor depth: §fTricolor§7", + "oc:tooltip.linkedcard": "These are crafted in pairs, and can only communicate with their partner card. However, they can communicate across any distance, and even across dimensions. The energy required to send a message is fairly high, though.", + "oc:tooltip.linkedcard_channel": "§8Channel: %s§7", + "oc:tooltip.manual": "All the information you could possibly need about OpenComputers. And more. For the unbelievably low price of... §oplease press R to continue§7.", + "oc:tooltip.memory": "Required to get computers to run. The more you have, the more complex the programs you can run.", + "oc:tooltip.microchip": "The chip formerly known as Integrated Circuit. I have no idea why this works with redstone, but it does.", + "oc:tooltip.microcontroller": "Microcontrollers are computers boiled down to the essentials. They are intended to take care of very specific tasks, running only a single program that is provided on the EEPROM built into them.\n§cCan not connect to external components.§7", + "oc:tooltip.microcontrollercase": "Base component for building microcontrollers. Place it into an assembler to add further components and assemble a microcontroller.", + "oc:tooltip.motionsensor": "Can detect movement of nearby living beings. Requires clear line-of-sight.", + "oc:tooltip.nanomachines": "Control unit and a bunch of nanomachines for ingestion, if you dare.", + "oc:tooltip.networkcard": "Allows distant computers connected by other blocks (such as cable) to communicate by sending messages to each other.", + "oc:tooltip.poweracceptor": "Energy conversion speed: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fFactorization Charge§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Converts power from other mods to the internal energy type. Conversion rates:", + "oc:tooltip.powerdistributor": "Distributes energy among different networks. This is useful for sharing power fed into your system from one converter among different sub-networks that should remain separate.", + "oc:tooltip.present": "... for your troubles. Open this present for a chance to receive some §kphat lewt§7!\n§8Craft OpenComputers items when the time is right for a chance to receive a present.§7", + "oc:tooltip.print.BeaconBase": "§8Works as a beacon base.", + "oc:tooltip.print.LightValue": "§8Light emitted: %s.", + "oc:tooltip.print.RedstoneLevel": "§8Redstone output: %s.", + "oc:tooltip.printedcircuitboard": "The basic building block for expansion cards and memory and such.", + "oc:tooltip.printer": "Allows printing blocks of user-defined shapes using Chamelium and Ink Cartridges. Must be configured using a computer. Keep away from small children. Because reasons.", + "oc:tooltip.raid": "Allows combining three hard drives into one larger file system that can be used by all connected computers.", + "oc:tooltip.rawcircuitboard": "Can be hardened in any furnace compatible oven.", + "oc:tooltip.redstone": "Allows reading and emitting redstone signals around the block. Can be controlled by any computer the block is connected to. This is basically like an external redstone card.", + "oc:tooltip.redstonecard.Charset": "§fSimpleLogic§7 is §asupported§7.", + "oc:tooltip.redstonecard.ProjectRed": "§fProjectRed§7 is §asupported§7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 is §asupported§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 is §asupported§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 is §asupported§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 is §asupported§7.", + "oc:tooltip.redstonecard": "Allows reading and emitting redstone signals around the computer or robot.", + "oc:tooltip.relay": "Allows connecting different networks to each other. Only network messages will be passed along, components will not be visible through this. Use this to separate networks while still allowing communication using Network Cards, for example.", + "oc:tooltip.robot": "Unlike computers, robots can move around and interact with the world much like a player can.\n§cCan not connect to external components.§7", + "oc:tooltip.robot_level": "§fLevel§7: §a%s§7", + "oc:tooltip.robot_storedenergy": "§fStored energy§7: §a%s§7", + "oc:tooltip.screen": "Display text, controlled by a Graphics Card in a Case.\nMaximum resolution: §f%sx%s§7\nMaximum color depth: §f%s§7", + "oc:tooltip.server": "This is a server, there are many like it, but this one can be upgraded with components much like a computer case can be. It can be run by inserting it into a server rack.", + "oc:tooltip.server.Components": "Installed components:", + "oc:tooltip.rack": "Allows the installation of up to four servers or other rack mountables.", + "oc:tooltip.tablet": "A tablet computer, for fresh Lua on the go. Can be forced to shut down by sneak-activating it.", + "oc:tooltip.tabletcase": "Basic case for tablets. Place it into the assembler to add in components and create a tablet computer.", + "oc:tooltip.terminal": "Allows controlling a server remotely, as long as you are in range of it. Acts like a portable screen and keyboard. Shift-right-click a server in a server rack to bind the terminal to it.", + "oc:tooltip.terminalserver": "Backend to which Remote Terminals can be connected to provide remote control. Houses a virtual screen and keyboard.", + "oc:tooltip.texturepicker": "This tool allows showing a string describing a block's surface, for use in 3D printer shape definitions. Totally not texture names, nope. No sir.", + "oc:tooltip.tier": "§8Tier %s", + "oc:tooltip.netsplitter": "Acts as a dynamic connector. Connectivity of each side can be toggled by hitting it with a wrench. Connectivity of all sides can be inverted by applying a redstone signal.", + "oc:tooltip.toolong": "Hold [§f%s§7] for a detailed tooltip.", + "oc:tooltip.transistor": "A basic element in most other computer parts. It's a bit twisted, but it does the job.", + "oc:tooltip.transposer": "Allows automated transferral of items and fluids between adjacent inventories and fluid containers.", + "oc:tooltip.upgradeangel": "Allows robots to place blocks in thin air, even if there is no point of reference.", + "oc:tooltip.upgradebattery": "Increase the amount of energy a device can store, allowing it work longer without having to be recharged.\nCapacity: §f%s§7", + "oc:tooltip.upgradechunkloader": "If a robot moves in a forest and no one is around to see it, does it really move? This upgrades makes sure it does. It keeps the chunk a device is in loaded, but continually consumes energy while active.", + "oc:tooltip.upgradecontainercard": "This container upgrade allows dynamically installing and removing a card from an assembled device.\nMaximum Tier: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "This container upgrade allows dynamically installing and removing another upgrade from an assembled device.\nMaximum Tier: §f%s§7", + "oc:tooltip.upgradecrafting": "Enables robots to use the top left area of their inventory for crafting objects. Items have to be aligned as they would be in a crafting table.", + "oc:tooltip.upgradedatabase": "This upgrade allows storing item stack information for later retrieval and use by other components.\nSupported entries: §f%s§7", + "oc:tooltip.upgradeexperience": "This upgrade allows robots to accumulate experience by performing various operations. The more experience they have, the more energy they can store, the faster they can harvest blocks and the more efficiently they can use tools.", + "oc:tooltip.upgradegenerator": "Can be used to generate energy from fuel on the go. Burns items to generate energy over time, based on their fuel value.\n§fEfficiency§7: §a%s%%§7", + "oc:tooltip.upgradehover": "This upgrade allows robots to fly higher above the ground without having to climb walls.\nMaximum height: §f%s§7", + "oc:tooltip.upgradeinventory": "This upgrade provides inventory space to a robot or drone. Without one of these, they will not be able to store items internally.", + "oc:tooltip.upgradeinventorycontroller": "This upgrade allows robots and drones more control in how it interact with external inventories, and allows robots to swap their equipped tool with an item in their inventory.", + "oc:tooltip.upgrademf": "Allows adapters to access blocks they are not adjacent to.", + "oc:tooltip.upgrademf.Linked": "§fConnection established§7", + "oc:tooltip.upgrademf.Unlinked": "§fNo connection§7", + "oc:tooltip.upgradeleash": "Allows some devices, such as drones, to bind Isaa- excuse me... *chatter* My apologies. I'm just being told this is actually used to put animals on a leash. Multiple animals, even. Odd.", + "oc:tooltip.upgradenavigation": "Can be used to determine the position and orientation of a device. The position is relative to the center of the map that was used to craft this upgrade.", + "oc:tooltip.upgradepiston": "This upgrade is very pushy. It allows moving blocks, similar to a piston.", + "oc:tooltip.upgradestickypiston": "This upgrade can push and pull. It allows moving blocks, similar to a sticky piston. It does §lnot§7 pull entities, however.", + "oc:tooltip.upgradesign": "Allows reading text on and writing text to signs.", + "oc:tooltip.upgradesolargenerator": "Can be used to generate energy from sunlight on the go. Requires a clear line of sight to the sky above the device. Generates energy at %s%% of the speed of a Stirling Engine.", + "oc:tooltip.upgradetank": "This upgrade provides a tank for fluid storage for robots and drones. Without one of these, they will not be able to store fluids internally.", + "oc:tooltip.upgradetankcontroller": "This upgrade allows robots and drones more control in how they interact with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory.", + "oc:tooltip.upgradetractorbeam": "Equips a device with extremely advanced technology, nicknamed the \"Item Magnet\". Allows the device to pick up items anywhere within 3 blocks of its location.", + "oc:tooltip.upgradetrading": "Allows robots and drones to trade with villagers.", + "oc:tooltip.waypoint": "Provides a point of reference to devices with a navigation upgrade.", + "oc:tooltip.wirelessnetworkcard": "Allows wireless sending of network messages in addition to normal ones. You can adjust the §fsignal strength§7 to control how far messages are sent. Higher signal strength results in higher energy consumption.", + "oc:tooltip.worldsensorcard": "Allows reading out information about the world, such as its gravity and whether it has a breathable atmosphere. Use results at own risk. The manufacturer takes no responsibility for bodily or material harm caused by decisions made upon the cards' outputs. We have lawyers. And money. Don't even try.", + "oc:tooltip.Wrench": "A hybrid of Screwdriver and Wrench, this tool is easy to learn, but hard to master.", + "achievement.oc.adapter": "Plug In Baby", + "achievement.oc.adapter.desc": "Interact with blocks from other mods and even vanilla Minecraft!", + "achievement.oc.assembler": "Wonderful", + "achievement.oc.assembler.desc": "Time to take over the world!", + "achievement.oc.cable": "Not a Dirty Wire", + "achievement.oc.cable.desc": "With patented anti-cable-spaghetti technology.", + "achievement.oc.capacitor": "Batteries included", + "achievement.oc.capacitor.desc": "You cannot stop it.", + "achievement.oc.card": "We Accept Cards", + "achievement.oc.card.desc": "For your convenience. No ulterior motive, promise.", + "achievement.oc.case": "In Case of Trouble", + "achievement.oc.case.desc": "Because cuboid towers are the best.", + "achievement.oc.charger": "All right, let's do this", + "achievement.oc.charger.desc": "Chaaaaaaaaaarg- dang, forgot the redstone signal again.", + "achievement.oc.chip": "All the Small Things", + "achievement.oc.chip.desc": "Because vacuum tubes are so yesteryear.", + "achievement.oc.cpu": "Overclocked", + "achievement.oc.cpu.desc": "Time to make good use of those computing cycles.", + "achievement.oc.disassembler": "Scratch That", + "achievement.oc.disassembler.desc": "In case one of your brilliant ideas turns out to be not that brilliant after all.", + "achievement.oc.diskDrive": "Roundabout", + "achievement.oc.diskDrive.desc": "Inferior capacity but such delicious sound.", + "achievement.oc.drone": "Fly Away", + "achievement.oc.drone.desc": "Keep calm and nuke it from orbit.", + "achievement.oc.eeprom": "There can be only one", + "achievement.oc.eeprom.desc": "Per computer, that is. For deterministic boot order, you know?", + "achievement.oc.floppy": "The One Ri- Disk", + "achievement.oc.floppy.desc": "Not to be confused with Flappy.", + "achievement.oc.geolyzer": "Down to Earth", + "achievement.oc.geolyzer.desc": "It has extraordinary qualities.", + "achievement.oc.graphicsCard": "LastGen", + "achievement.oc.graphicsCard.desc": "The way it's meant to be... uh... rendered. Yeah. That.", + "achievement.oc.hdd": "Hot Dog Dealer", + "achievement.oc.hdd.desc": "No wait, that's not what that meant. Hang on, almost got it...", + "achievement.oc.hologram": "Next Dimension", + "achievement.oc.hologram.desc": "Because 2D is boring. Or is it?", + "achievement.oc.keyboard": "DirtCatcher3000", + "achievement.oc.keyboard.desc": "It is highly recommended to resist the urge to flip them around and shake them.", + "achievement.oc.microcontroller": "Little Sister", + "achievement.oc.microcontroller.desc": "The small sibling of computers.", + "achievement.oc.motionSensor": "Got the Moves", + "achievement.oc.motionSensor.desc": "Like Steve Swagger.", + "achievement.oc.networkCard": "Now We're Talking!", + "achievement.oc.networkCard.desc": "Keep in touch with those distant relatives via transitive relations.", + "achievement.oc.openOS": "Boot", + "achievement.oc.openOS.desc": "One OS to - wait, I used that one already? Dang.", + "achievement.oc.powerDistributor": "Sharing is Caring", + "achievement.oc.powerDistributor.desc": "When you need some help balancing all that power.", + "achievement.oc.rack": "Dat Rack", + "achievement.oc.rack.desc": "I don't know what you're thinking of, I clearly meant the server rack.", + "achievement.oc.raid": "LFG", + "achievement.oc.raid.desc": "Heroic plzkthx.", + "achievement.oc.ram": "Random Access Memories", + "achievement.oc.ram.desc": "Congratulations, you're Doin' It Right.", + "achievement.oc.redstoneCard": "Contact", + "achievement.oc.redstoneCard.desc": "Time to go analog.", + "achievement.oc.redstoneIO": "The Outsider", + "achievement.oc.redstoneIO.desc": "Taking redstone signals where you want them.", + "achievement.oc.robot": "Beep Boop", + "achievement.oc.robot.desc": "EXTERMINATE!", + "achievement.oc.screen": "Have you tried turning it off and on again?", + "achievement.oc.screen.desc": "No seriously. A redstone pulse can toggle a screen's power, after all.", + "achievement.oc.server": "Dedicated", + "achievement.oc.server.desc": "Cloud services, here we come.", + "achievement.oc.switch": "Complex Topology", + "achievement.oc.switch.desc": "Avoid fragile goods due to possibility of dropped packets.", + "achievement.oc.tablet": "Do Not Swallow", + "achievement.oc.tablet.desc": "Also keep away from small children to avoid unexpected overdrawing of your credit card.", + "achievement.oc.transistor": "Tell Red I said \"Hi.\"", + "achievement.oc.transistor.desc": "Create a Transistor to get started. Then listen to the soundtrack. No need to thank me.", + "achievement.oc.wirelessNetworkCard": "Signals", + "achievement.oc.wirelessNetworkCard.desc": "Time to go where no packet has gone before.", + "death.attack.oc.nanomachinesOverload.1": "%s got too greedy.", + "death.attack.oc.nanomachinesOverload.2": "%s had a nervous breakdown.", + "death.attack.oc.nanomachinesOverload.3": "The nanomachines of %s went out of control.", + "death.attack.oc.nanomachinesHungry.1": "%s was eaten by nanomachines.", + "death.attack.oc.nanomachinesHungry.2": "%s didn't keep their nanomachines fed.", + "death.attack.oc.nanomachinesHungry.3": "%s has been digested.", + "nei.options.inventory.oredict": "Show OreDictionary names", + "nei.options.inventory.oredict.true": "True", + "nei.options.inventory.oredict.false": "False", + "nei.usage.oc.Manual": "Open Manual", + "option.oc.address": "Address", + "option.oc.componentName": "Component Name", + "option.oc.energy": "Energy" +} diff --git a/src/main/resources/assets/opencomputers/lang/fr_FR.lang b/src/main/resources/assets/opencomputers/lang/fr_FR.lang deleted file mode 100644 index eac2d692d2..0000000000 --- a/src/main/resources/assets/opencomputers/lang/fr_FR.lang +++ /dev/null @@ -1,359 +0,0 @@ -# Blocks, items, gui, containers, keybinds and tooltips only -# -# - -# Blocks -tile.oc.accesspoint.name=Point d'accès -tile.oc.adapter.name=Adaptateur -tile.oc.assembler.name=Assembleur électronique -tile.oc.cable.name=Câble -tile.oc.capacitor.name=Capaciteur -tile.oc.case1.name=Boitier (Niveau 1) -tile.oc.case2.name=Boitier (Niveau 2) -tile.oc.case3.name=Boitier (Niveau 3) -tile.oc.casecreative.name=Boitier (Créatif) -tile.oc.chameliumblock.name=Bloc de Chamélium -tile.oc.charger.name=Chargeur -tile.oc.disassembler.name=Désassembleur -tile.oc.diskdrive.name=Lecteur de disquettes -tile.oc.endstone.name=Pierre du néant -tile.oc.geolyzer.name=Géoliseur -tile.oc.hologram1.name=Projecteur d'hologramme (Niveau 1) -tile.oc.hologram2.name=Projecteur d'hologramme (Niveau 2) -tile.oc.keyboard.name=Clavier -tile.oc.microcontroller.name=Micro-contrôleur -tile.oc.motionsensor.name=Détecteur de mouvement -tile.oc.netsplitter.name=Répartiteur réseau -tile.oc.powerconverter.name=Convertisseur énergétique -tile.oc.powerdistributor.name=Distributeur énergétique -tile.oc.print.name=Impression 3D -tile.oc.printer.name=Imprimante 3D -tile.oc.raid.name=Raid -tile.oc.redstone.name=Redstone E/S -tile.oc.relay.name=Relai -tile.oc.robot.name=Robot -tile.oc.robotafterimage.name=Robot -tile.oc.screen1.name=Ecran (Niveau 1) -tile.oc.screen2.name=Ecran (Niveau 2) -tile.oc.screen3.name=Ecran (Niveau 3) -tile.oc.rack.name=Support de serveur -tile.oc.switch.name=Routeur -tile.oc.transposer.name=Transposeur -tile.oc.waypoint.name=Point de passage - -# Items -item.oc.abstractbuscard.name=Carte de bus abstraite -item.oc.acid.name=Grog -item.oc.alu.name=Unité de Logique Arithmétique (ULA) -item.oc.analyzer.name=Analyseur -item.oc.apu0.name=Unité de Traitement Accélérée (UTA) (Niveau 1) -item.oc.apu1.name=Unité de Traitement Accélérée (UTA) (Niveau 2) -item.oc.apu2.name=Unité de Traitement Accélérée (UTA) (Créatif) -item.oc.arrowkeys.name=Touches directionnelles -item.oc.buttongroup.name=Groupe de boutons -item.oc.cardbase.name=Base de carte -item.oc.chamelium.name=Chamélium -item.oc.circuitboard.name=Plaque de circuit imprimé -item.oc.componentbus0.name=Bus des composants (Niveau 1) -item.oc.componentbus1.name=Bus des composants (Niveau 2) -item.oc.componentbus2.name=Bus des composants (Niveau 3) -item.oc.controlunit.name=Unité de contrôle (UC) -item.oc.cpu0.name=Processeur (Niveau 1) -item.oc.cpu1.name=Processeur (Niveau 2) -item.oc.cpu2.name=Processeur (Niveau 3) -item.oc.cuttingwire.name=Fil de coupe -item.oc.debugcard.name=Carte de débogueur -item.oc.debugger.name=Débogueur réseau -item.oc.disk.name=Disque -item.oc.drone.name=Drone -item.oc.dronecase0.name=Boitier de drone (Niveau 1) -item.oc.dronecase1.name=Boitier de drone (Niveau 2) -item.oc.dronecase3.name=Boitier de drone (Créatif) -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=Disquette -item.oc.graphicscard0.name=Carte graphique (Niveau 1) -item.oc.graphicscard1.name=Carte graphique (Niveau 2) -item.oc.graphicscard2.name=Carte graphique (Niveau 3) -item.oc.harddiskdrive0.name=Disque dur (Niveau 1) -item.oc.harddiskdrive1.name=Disque dur (Niveau 2) -item.oc.harddiskdrive2.name=Disque dur (Niveau 3) -item.oc.hoverboots.name=Bottes de vol plané -item.oc.inkcartridge.name=Cartouche d'encre -item.oc.inkcartridgeempty.name=Cartouche d'encre (Vide) -item.oc.internetcard.name=Carte internet -item.oc.interweb.name=Interweb -item.oc.ironnugget.name=Pépite de fer -item.oc.linkedcard.name=Carte liée -item.oc.manual.name=Manuel d'OpenComputers -item.oc.memory0.name=Mémoire (Niveau 1) -item.oc.memory1.name=Mémoire (Niveau 1.5) -item.oc.memory2.name=Mémoire (Niveau 2) -item.oc.memory3.name=Mémoire (Niveau 2.5) -item.oc.memory4.name=Mémoire (Niveau 3) -item.oc.memory5.name=Mémoire (Niveau 3.5) -item.oc.microchip0.name=Puce électronique (Niveau 1) -item.oc.microchip1.name=Puce électronique (Niveau 2) -item.oc.microchip2.name=Puce électronique (Niveau 3) -item.oc.microcontrollercase0.name=Boitier de microcontrôleur (Niveau 1) -item.oc.microcontrollercase1.name=Boitier de microcontrôleur (Niveau 2) -item.oc.microcontrollercase3.name=Boitier de microcontrôleur (Créatif) -item.oc.networkcard.name=Carte réseau -item.oc.numpad.name=Pavé numérique -item.oc.present.name=Un petit quelque chose... -item.oc.printedcircuitboard.name=Circuit imprimé -item.oc.rawcircuitboard.name=Plaque de circuit imprimé brute -item.oc.redstonecard0.name=Carte de Redstone (Niveau 1) -item.oc.redstonecard1.name=Carte de Redstone (Niveau 2) -item.oc.server0.name=Serveur (Niveau 1) -item.oc.server1.name=Serveur (Niveau 2) -item.oc.server2.name=Serveur (Niveau 3) -item.oc.server3.name=Serveur (Créatif) -item.oc.tablet.name=Tablette -item.oc.tabletcase.name=Boitier de tablette (Niveau 1) -item.oc.tabletcase.name=Boitier de tablette (Niveau 2) -item.oc.tabletcase.name=Boitier de tablette (Créatif) -item.oc.terminal.name=Terminal à distance -item.oc.texturepicker.name=Ramasseur de texture -item.oc.transistor.name=Transistor -item.oc.upgradeangel.name=Amélioration ange -item.oc.upgradebattery0.name=Amélioration batterie (Niveau 1) -item.oc.upgradebattery1.name=Amélioration batterie (Niveau 2) -item.oc.upgradebattery2.name=Amélioration batterie (Niveau 3) -item.oc.upgradechunkloader.name=Amélioration chargement de chunk -item.oc.upgradecontainercard0.name=Conteneur de carte (Niveau 1) -item.oc.upgradecontainercard1.name=Conteneur de carte (Niveau 2) -item.oc.upgradecontainercard2.name=Conteneur de carte (Niveau 3) -item.oc.upgradecontainerupgrade0.name=Amélioration de conteneur (Niveau 1) -item.oc.upgradecontainerupgrade1.name=Amélioration de conteneur (Niveau 2) -item.oc.upgradecontainerupgrade2.name=Amélioration de conteneur (Niveau 3) -item.oc.upgradecrafting.name=Amélioration d'artisanat -item.oc.upgradedatabase0.name=Amélioration de base de données (Niveau 1) -item.oc.upgradedatabase1.name=Amélioration de base de données (Niveau 2) -item.oc.upgradedatabase2.name=Amélioration de base de données (Niveau 3) -item.oc.upgradeexperience.name=Amélioration d'expérience -item.oc.upgradegenerator.name=Amélioration de générateur -item.oc.upgradehover0.name=Amélioration de vol plané (Niveau 1) -item.oc.upgradehover1.name=Amélioration de vol plané (Niveau 2) -item.oc.upgradeinventory.name=Amélioration d'inventaire -item.oc.upgradeinventorycontroller.name=Amélioration du contrôleur d'inventaire -item.oc.upgradeleash.name=Amélioration de laisse -item.oc.upgradenavigation.name=Amélioration de navigation -item.oc.upgradepiston.name=Amélioration de piston -item.oc.upgradesign.name=Amélioration de panneau d'E/S -item.oc.upgradesolargenerator.name=Amélioration du générateur solaire -item.oc.upgradetank.name=Amélioration de réservoir -item.oc.upgradetankcontroller.name=Amélioration du contrôleur de réservoir -item.oc.upgradetractorbeam.name=Amélioration du rayon tracteur -item.oc.wirelessnetworkcard0.name=Carte de réseau sans-fils (Niveau 1) -item.oc.wirelessnetworkcard1.name=Carte de réseau sans-fils (Niveau 2) -item.oc.worldsensorcard.name=Carte de capteur du monde -item.oc.wrench.name=Crisseur - -# Entities -entity.oc.Drone.name=Drone - -# GUI -oc:gui.Analyzer.Address=§6Addresse§f: %s -oc:gui.Analyzer.AddressCopied=Adresse copiée dans le presse-papier. -oc:gui.Analyzer.ChargerSpeed=§6Vitesse de charge§f: %s -oc:gui.Analyzer.ComponentName=§6Nom du composant§f: %s -oc:gui.Analyzer.Components=§6Nombre de composants connectés§f: %s -oc:gui.Analyzer.LastError=§6Dernière erreur§f: %s -oc:gui.Analyzer.RobotName=§6Nom§f: %s -oc:gui.Analyzer.RobotOwner=§6Propriétaire§f: %s -oc:gui.Analyzer.RobotXp=§6Expérience§f: %s -oc:gui.Analyzer.StoredEnergy=§6Energie stockée§f: %s -oc:gui.Analyzer.TotalEnergy=§6Energie totale§f: %s -oc:gui.Analyzer.Users=§6Utilisateurs§f: %s -oc:gui.Analyzer.WirelessStrength=§6Force du signal§f: %s -oc:gui.Assembler.Collect=Collectez la sortie -oc:gui.Assembler.Complexity=Complexité: %s/%s -oc:gui.Assembler.InsertCase=Insérez une partie de base -oc:gui.Assembler.InsertCPU=Insérez une UCP -oc:gui.Assembler.InsertRAM=Insérez un peu de mémoire -oc:gui.Assembler.Progress=Progression: %s%% (%s) -oc:gui.Assembler.Run=Assemble -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Carte graphique -oc:gui.Assembler.Warning.Inventory=Amélioration d'inventaire -oc:gui.Assembler.Warning.Keyboard=Clavier -oc:gui.Assembler.Warning.OS=Moyen de démarrage -oc:gui.Assembler.Warning.Screen=Ecran -oc:gui.Assembler.Warnings=§eALERTE§7: Les composants recommandés sont manquant. -oc:gui.Chat.NewVersion=une nouvelle version est disponible: %s -oc:gui.Chat.TextureName=§7Le nom de la texture est §a%s§f. -oc:gui.Chat.WarningClassTransformer=Il y a §cerrors§f lancement dans le transformeur de classe. Veuillez vérifier ceci, en même temps que votre fichier log (plein) FML §alatest.log§f/§afml-server-latest.log§f, Merci ! -oc:gui.Chat.WarningFingerprint=§cALERTE§f - Disparité d'empreinte digitale! Attendu '§a%s§f' mais eu '§e%s§f'. À moins que vous ne soyez un modder et que vous exécutiez la version deobfuscated du mod, Il est §lfortement§f recommendé de re-télécharger OpenComputers, parce que le JAR que vous utilisez peut avoir été falsifié. -oc:gui.Chat.WarningLink=Impossible d'ouvrir le lien : %s -oc:gui.Chat.WarningLuaFallback=Les bibliothèques natives Lua ne sont pas disponibles, les ordinateurs ne pourront pas persister en leur état. Ils réamorceront sur la recharge des chunks. -oc:gui.Chat.WarningProjectRed=Vous utilisez une version de Project: Red qui est incompatible avec OpenComputers. Essayez de mettre à jour votre version de Project: Red. -oc:gui.Chat.WarningRecipes=il y a des erreurs au lancement de recettes. Certains éléments doivent être infabricables. Veuillez vérifier votre fichier log pour plus d'information. -oc:gui.Chat.WarningSimpleComponent=Un ajout (le votre ?) utilisant l'interface §aComposant Simple§f produit §equelquechose de mauvais§f. Le composant logique ne peut être inséré. Veuillez vérifier votre fichier log pour plus d'information. -oc:gui.Error.ComponentOverflow=Beaucoup trop de composants connectés à l'ordinateur. -oc:gui.Error.InternalError=Erreur interne, Veuillez lire le fichier log. Ceci est probablement une erreur. -oc:gui.Error.NoCPU=Pas d'UCP installé dans cet ordinateur. -oc:gui.Error.NoEnergy=Pas assez d'énergie. -oc:gui.Error.NoRAM=pas de mémoire installée dans cet ordinateur. -oc:gui.Error.OutOfMemory=Mémoire saturée. -oc:gui.Manual.Blocks=Blocs d'OpenComputers -oc:gui.Manual.Home=Accueil -oc:gui.Manual.Items=Éléments d'OpenComputers -oc:gui.Manual.Warning.BlockMissing=Bloc manquant. -oc:gui.Manual.Warning.ImageMissing=Image manquante. -oc:gui.Manual.Warning.ItemMissing=Élément manquant. -oc:gui.Manual.Warning.OreDictMissing=Entrée au dictionnaire de minerai manquante. -oc:gui.Raid.Warning=§4L'ajout d'un disque le nettoie.[nl] L'enlèvement d'un disque nettoie le raid. -oc:gui.Robot.Power=Énergie -oc:gui.Robot.TurnOff=Éteindre -oc:gui.Robot.TurnOn=Allumer -oc:gui.Rack.None=Aucun -oc:gui.Rack.Back=Arrière -oc:gui.Rack.Bottom=Bas -oc:gui.Rack.Left=Gauche -oc:gui.Rack.Right=Droite -oc:gui.Rack.Top=Haut -oc:gui.Switch.TransferRate=Vitesse du cycle -oc:gui.Switch.PacketsPerCycle=paquets / cycle -oc:gui.Switch.QueueSize=Taille de la file -oc:gui.Terminal.InvalidKey=Clef invalide, très probablement qu'un autre terminal a dû nécessité le serveur. -oc:gui.Terminal.OutOfRange=Pas de signal. - -# Containers -oc:container.accesspoint=Point d'accès -oc:container.adapter=Adapteur -oc:container.case=Ordinateur -oc:container.charger=Chargeur -oc:container.disassembler=Désassembleur -oc:container.diskdrive=Lecteur disque -oc:container.printer=Imprimante -oc:container.raid=Raid -oc:container.server=Serveur -oc:container.rack=Support de serveur -oc:container.switch=Routeur -oc:container.tabletwrapper=Tablette - -# Keybinds -key.materialCosts=Montrer les coût en matériaux -key.clipboardPaste=Coller le presse-papier - -# Item / Block Tooltips -oc:tooltip.accesspoint=Fonctionne comme un routeur, de plus il recois les paquets sans-fil et il transmet les paquets filaires au réseau sans-fil. -oc:tooltip.abstractbuscard=Il permet d'interagir avec le bus abstait de §fStargateTech 2§7 lors de l'envoi et de la reception de paquets LIP. -oc:tooltip.acid=Un produit semi-liquide très toxique, uniquement bu par certains pirates. Grâce à ses propriétés corrosives, il est très utile à la gravure de circuits imprimés. -oc:tooltip.adapter=Utilisé pour contrôler des blocs n'étant pas des composants d'ordinateurs, comme des blocs Vanilla ou d'autres mods. -oc:tooltip.alu=Ajoute des nombres pour que vous n'ayez pas à le faire. C'est peut-être mieux comme ça. -oc:tooltip.analyzer=Utilisé pour afficher des informations sur des blocs, comme leur §faddresse§7 et leur §fnom de composant§7.[nl] Affiche aussi les erreurs en cas de crash d'un ordinateur. -oc:tooltip.apu=Ceci est une UTC avec une UTG intégrée (ou UTI), lorsque vous avez juste besoin d'un emplacement de carte supplémentaire.[nl] Composants supportés : §f%s§7[nl] Résolution maximum: §f%sx%s§7[nl] profondeur de couleur maxi: §f%s§7[nl] Opérations/tic: §f%s§7 -oc:tooltip.assembler=Il permet de construire des robots et d'autres dispositifs d'un certain nombre de pièces informatiques différentes. -oc:tooltip.cable=Un moyen peu coûteux pour relier les ordinateurs entre eux. -oc:tooltip.capacitor=Stocke l'énergie pour une utilisation ultérieure. Peut être chargé et déchargé très rapidement. -oc:tooltip.cardbase=Comme son nom l'indique, c'est le bloc de construction basique pour toutes les cartes. -oc:tooltip.case=La tour est le bloc principal de l'ordinateur, et héberge ses §fcartes§7, sa §fMémoire RAM§7 et ses §fdisques durs§7.[nl] Emplacements: §f%s§7 -oc:tooltip.chamelium=Matière première pour impression 3D. Ne pas avaler : Peut mener à la cécité et au manque de présence provisoire. -oc:tooltip.chameliumblock=Joli et propre. Pratique pour formes teintées dans les impressions 3D, ou juste pour avoir un bloc propre, coloré, pour décorer votre base fantaisiste avec. -oc:tooltip.charger=Transfère l'énergie depuis des capaciteurs pour les robots adjacents. La puissance du transfert dépends du §fsignal de redstone§7, ou une absence de signal ne charge pas les robots, et un signal maximum charge les robots le plus vite possible. -oc:tooltip.circuitboard=Hé ben, on y arrive. Peut être gravé pour obtenir un circuit imprimé. -oc:tooltip.controlunit=C'est une unité qui... controle... des trucs. On en a besoin pour faire un processeur. Donc, ouais, super important. -oc:tooltip.componentbus=Cette extention autorise aux serveurs de communiquer avec plus composants en même temps, Semblable à ce que les UCP font.[nl] Composants supportés : §f%s§7 -oc:tooltip.cpu=Un composant essentiel pour tout ordinateur. La fréquence d'horloge est un peu douteuse, mais, vous vous attendiez à quoi avec un cadran solaire de poche ? -oc:tooltip.cpu.Architecture=Architecture: §f%s§7 -oc:tooltip.cuttingwire=Utilisé pour couper des blocs d'argile en plaque de circuit imprimé. Se casse après utilisation, ce qui rends cet outil le moins efficace jamais utilisé. -oc:tooltip.debugcard=Element en mode créatif, il autorise la manipulation du monde pour faciliter les tests. Utilisation à votre propre péril. -oc:tooltip.debugger=Peut être utilisé pour sortir les information de débogue sur la grille réseau interne de OC. A utiliser seulement si instruit par un dev. -oc:tooltip.disassembler=Sépare les éléments dans leur composants d'origine. §lAttention§7: les éléments ainsi récupérés ont %s%% de chance de ce briser dans le processus ! -oc:tooltip.disk=Moyen primitif qui peut être utilisé pour faire des appareils de stockage persistants. -oc:tooltip.diskdrive.CC=Les disquettes ComputerCraft sont §asupportées§7. -oc:tooltip.diskdrive=Permet de lire et d"écrire des disquettes. Peut être installé dans les robots pour permettre d'insérer des disquettes plus tard. -oc:tooltip.drone=Les drones sont de légère unité de reconnaissance rapide avec un espace de chargement limité. -oc:tooltip.dronecase=Ce boitier est utilisé pour construire des Drones dans l'assembleur. Il a des emplacement pour une petite quantité de composant et fournit la lévitation en pierre de néant alimentée. -oc:tooltip.eeprom=Petit, stockage programmable qui contient le BIOS que les ordinateurs utilisent pour démarrer. -oc:tooltip.fakeendstone=Presque aussi bon que la chose réelle, imite même sa flottabilité ! -oc:tooltip.geolyzer=Permet de scanner la dureté des blocs des environs. Ces informations peuvent être utiles pour produire un hologramme de la zone ou pour détecter les minerais. -oc:tooltip.graphicscard=Utilisé pour changer ce qui est affiché sur écran.[nl] Résolution maximum: §f%sx%s§7[nl] Couleurs maximales: §f%s§7[nl] Operations/tick: §f%s§7 -oc:tooltip.hoverboots=Sautez plus haut, tombez plus bas, marchez mieux. Ceci et plus, avec les nouvelles Bottes de vol plané (TM). -oc:tooltip.inkcartridge=Utilisé pour remplir d'encre les imprimantes 3D. Pour des raisons mystérieuses, elle ne doit pas rester dans l'imprimante. -oc:tooltip.inkcartridgeempty=Cette cartouche d'encre a été vidé totalement. Remplissez la avec des couleurs. Ou jetez la. Voyez comme je m'en soucie. -oc:tooltip.internetcard=Cette carte permet de faire des requêtes HTTP et utiliser de véritable TCP sockets. -oc:tooltip.interweb=Félicitation, vous avez gagné un (1) interweb. Vous pouvez y connecter en utilisant une carte internet. Prenez garde : n'alimentez pas les trolls. -oc:tooltip.ironnugget=Une pépite de fer. D'où son nom. -oc:tooltip.keyboard=Peut être attaché à un écran pour permettre la saisie. -oc:tooltip.hologram0=Un affichage volumétrique qui peut être contrôlé par des ordinateurs pour montrer des structures de voxel arbitraires.[nl] Résolution : §f48x32x48§7 [nl] Echelle maxi : §f3x§7 [nl] Colorimétrie : §fMonochrome§7 -oc:tooltip.hologram1=Un affichage volumétrique qui peut être contrôlé par des ordinateurs pour montrer des structures de voxel arbitraires.[nl] Résolution : §f48x32x48§7 [nl] Echelle maxi : §f4x§7 [nl] Colorimétrie : §fTricolore§7 -oc:tooltip.linkedcard=Elles sont fabriqué par paire et ne peuvent communiquer qu'avec leur Carte partenaire. Cependant, elles peuvent communiquer à travers n'importe quelle distance et même à travers les dimensions. L'énergie exigée pour envoyer un message est assez haute malgès tout. -oc:tooltip.linkedcard_channel=§8Canal : %s§7 -oc:tooltip.manual=Toutes les informations vous pourriez probablement avoir besoin sur OpenComputers. Et plus. Au prix incroyablement bas de... §oveuillez appuyer sur R pour continuer§7. -oc:tooltip.materialcosts=Prend [§f%s§7] pour les coûts des matériaux. -oc:tooltip.materials=Materiaux : -oc:tooltip.memory=Requis pour faire fonctionner les ordinateurs. Plus vous en avez, plus complexes les programmes seront utilisables. -oc:tooltip.microchip=La puce anciennement circuit intégré. J'ai aucune idée du comment ça marche avec la redstone, mais ça marche. -oc:tooltip.microcontroller=Les microcontrôleurs sont des ordinateurs résumés à l'essentiel. Ils sont destinés pour s'occuper de tâches très spécifiques, dirigant seulement l'unique programme que lui donne l'EEPROM qu'il intègre. -oc:tooltip.microcontrollercase=Composant de base pour construire des microcontrôleurs. Placez-le dans un assembleur pour ajouter de nouveaux composants et assemblez un microcontrôleur. -oc:tooltip.motionsensor=Peut détecter le mouvement d'êtres vivants à proximité. Demande une ligne de vue dégagée. -oc:tooltip.networkcard=Permet à des ordinateurs distants de communiquer en s'envoyant des messages. -oc:tooltip.poweracceptor=Vitesse de convertion d'énergie: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft en MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fCharges de Factorization§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² en EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism en Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion en RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine en Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Convertis l'énergie d'autres mods en l'énergie requise pour faire fonctionner les ordinateurs. -oc:tooltip.powerdistributor=Distribue l'énergie dans plusieurs réseaux. Utile pour partager cette énergie depuis un convertisseur vers différents sous-réseaux qui doivent rester séparés. -oc:tooltip.present=... pour vos ennuis. Ouvrez ce cadeau pour une chance de recevoir quelques éléments d'OpenCompucters §kpsorti du chapeaut§7![nl]§8 quand c'est le bon momment de recevoir un cadeau.§7 -oc:tooltip.print.BeaconBase=§8Fonctionne comme une base de balise. -oc:tooltip.print.LightValue=§8Lumière émise : %s. -oc:tooltip.print.RedstoneLevel=§8Sortie Redstone : %s. -oc:tooltip.printedcircuitboard=Le composant basique pour les cartes, la mémoire, etc. -oc:tooltip.raid=Permet de combiner trois disques durs dans un plus grand système de fichiers qui pourra être utilisé par tous les ordinateurs connectés. -oc:tooltip.rawcircuitboard=Peut être durci dans n'importe quel four. -oc:tooltip.redstone=Permet de recevoir et d'émettre des signaux de redstone autour du bloc. Contrôlable par n'importe quel ordinateur connecté à ce bloc. En gros, une carte redstone externe. -oc:tooltip.redstonecard.ProjectRed=§fProjectRed§7 est §asupporté7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 est §asupporté§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 est §asupporté§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 est §asupporté§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 est §asupporté§7. -oc:tooltip.redstonecard=Permet la réception et l'émission de signaux de redstone autour de l'ordinateur ou du robot. -oc:tooltip.robot=Contrairement aux ordinateurs, les robots peuvent se déplacer et intéragir avec le monde comme un joueur le fait. Ils ne peuvent §opas§r§7 intéragir avec des composants d'ordinateur extérieurs, ceci dit ! -# the underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fNiveau§7: §a%s§7. -oc:tooltip.robot_storedenergy=§fEnergie stockée§7: §a%s§7. -oc:tooltip.screen=Affiche du texte, contrôlé par une Carte graphique dans un Boitier.[nl] Résolution maximum: §f%sx%s§7[nl] Couleurs maximales: §f%s§7 -oc:tooltip.server=Ceci est un serveur, il y en a beaucoup comme ca, mais celui-ci peut être amélioré avec des composants bien plus qu'un ordinateur peut l'être. Il peut être placé dans un support de serveur. -oc:tooltip.server.Components=Composants installés : -oc:tooltip.rack=Permet l'installation jusqu'à quatre serveurs. -oc:tooltip.switch=Permet de connecter différents réseaux entre eux. Seulement des messages réseau seront transmis, les composants ne seront pas visibles via celui ci. A utiliser pour séparer des réseaux tout en leur permettant de communiquer entre eux, grâce aux Cartes réseau, par exemple. -oc:tooltip.tablet=Une tablette, pour une Lua fraiche en route. Peut être forcé à l'arrêt par un accroupi-clique droit. -oc:tooltip.tabletcase=Boitier de tablettes de base. Placez-le dans l'assembleur pour ajouter des composants et créez une tablette. -oc:tooltip.terminal=Permet de contrôler un serveur à distance, tant que vous êtes à portée de lui. Fonctionne comme un écran-clavier portable. Maj-clique droit un serveur du support pour le lier au terminal. -oc:tooltip.tier=§8Niv. %s -oc:tooltip.toolong=Maintenez la touche [§f%s§7] pour plus d'informations. -oc:tooltip.transistor=Un élément basique constituant la plupart des pièces d'un ordinateur. Il est un peu tordu, mais il fait son boulot. -oc:tooltip.upgradeangel=Permet aux robots pour placer des blocs d'air mince, même s'il n'y a aucun point de référence. -oc:tooltip.upgradebattery=Augmentez la quantité d'énergie qu'un robot peut stocker, le permettant de marcher plus longtemps sans devoir être rechargé. [nl] Capacité : §f%s§7 -oc:tooltip.upgradechunkloader=Si un robot se déplace dans une forêt sans personne à proximité, se déplace-il vraiment ? Cette amélioration s'assure qu'il le fait. Elle garde le chunk du robot chargé, mais consomme continuellement de l'énergie lors qu'il est actif. -oc:tooltip.upgradecontainercard=Cette amélioration de conteneur permet l'installation ou le retrait dynamique d'une carte du robot assemblé. [nl] Niv. Maximum : §f%s§7 -oc:tooltip.upgradecontainerupgrade=Cette amélioration de conteneur permet l'installation ou le retrait dynamique d'une amélioration du robot assemblé. [nl] Niv. Maximum : §f%s§7 -oc:tooltip.upgradecrafting=Permet aux robots d'utiliser le coin en haut à gauche de leur inventaire comme table d'artisanat. Vous devez respecter la position des objets comme pour un artisanat normal. -oc:tooltip.upgradedatabase=Cette amélioration permet de stoquer les information de pile d'éléments pour une utilisation future d'autres composants. Notez que ceci doit être configuré à la main.[nl] Entrées supportées : §f%s§7 -oc:tooltip.upgradeexperience=Cette amélioration permet aux robots d'accumuler de l'experience en exécutant diverses opérations. Plus ils gagnent d'expérience, plus ils peuvent stocker de l'énergie, miner plus rapidement et utiliser les outils plus efficacement. -oc:tooltip.upgradegenerator=Utilisé pour générer de l'énergie directement sur un robot, indépendament d'un chargeur. Consume du comburant (ex: charbon) pour une génération sur la durée, en fonction de l'efficacité du combustible. [nl] §fEfficacité§7: §a%s%%§7 -oc:tooltip.upgradeinventory=Cette amélioration donne un espace d'inventaire au robot. Sans l'une d'elles, les robots ne seraient pas capable de stocker des éléments en interne. -oc:tooltip.upgradeinventorycontroller=Cette amélioration permet au robot plus de contrôle sur ses interactions avec les inventaires externes, et leur permet d'échanger leurs outils avec un élément de son inventaire. -oc:tooltip.upgradenavigation=Utilisé pour déterminer la position et l'orientation d'un robot. Cette position est relative au centre de la zone (affichée sur une carte) ou l'amélioration a été créée. -oc:tooltip.upgradepiston=Cette amélioration est très rentre-dedans. Elle permet de déplacer des blocs comme lors de l'utilisation d'un piston. malgré tout, cela §lne déplace pas§7 les entités. -oc:tooltip.upgradesign=Permets de lire et d'écrire sur des panneaux. -oc:tooltip.upgradesolargenerator=Utilisé pour générer de l'énergie directement sur un robot, indépendament d'un chargeur, via le soleil. Aucun bloc ne doit se trouver au-dessus du robot pour que cette génération fonctionne. Génère de l'énergie à %s%% de la vitesse d'un moteur stirling (BuildCraft). -oc:tooltip.upgradetank=Cette amélioration donne un réservoire de fluides au robot. Sans l'une d'elles, les robots ne seraient pas capable de stocker des fluides en interne. -oc:tooltip.upgradetankcontroller=Cette amélioration permet au robot plus de contrôle sur ses interactions avec les réservoirs externes, et leur permet de transférer les fluides avec un élément réservoir de fluide de son inventaire. -oc:tooltip.upgradetractorbeam=Equipe le robot avec une technologie extrêmement avancée, surnommé "l'aimant d'éléments". Cela lui permet de prendre des éléments n'importe où 3 blocs autour de son emplacement. -oc:tooltip.wirelessnetworkcard=Permet l'envoi de messages réseaux sans fil. Pensez à régler la §fforce du signal§7, sinon aucun paquet ne sera envoyé! - -# NEI Integration -nei.options.inventory.oredict=Montrez les noms OreDictionary -nei.options.inventory.oredict.true=Vrai -nei.options.inventory.oredict.false=Faux - -# Waila Integration -option.oc.address=Adresse -option.oc.componentName=Nom du composant -option.oc.energy=Énergie diff --git a/src/main/resources/assets/opencomputers/lang/fr_fr.json b/src/main/resources/assets/opencomputers/lang/fr_fr.json new file mode 100644 index 0000000000..ac98b76c96 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/fr_fr.json @@ -0,0 +1,339 @@ +{ + "tile.oc.accesspoint": "Point d'accès", + "tile.oc.adapter": "Adaptateur", + "tile.oc.assembler": "Assembleur électronique", + "tile.oc.cable": "Câble", + "tile.oc.capacitor": "Capaciteur", + "tile.oc.case1": "Boitier (Niveau 1)", + "tile.oc.case2": "Boitier (Niveau 2)", + "tile.oc.case3": "Boitier (Niveau 3)", + "tile.oc.casecreative": "Boitier (Créatif)", + "tile.oc.chameliumblock": "Bloc de Chamélium", + "tile.oc.charger": "Chargeur", + "tile.oc.disassembler": "Désassembleur", + "tile.oc.diskdrive": "Lecteur de disquettes", + "tile.oc.endstone": "Pierre du néant", + "tile.oc.geolyzer": "Géoliseur", + "tile.oc.hologram1": "Projecteur d'hologramme (Niveau 1)", + "tile.oc.hologram2": "Projecteur d'hologramme (Niveau 2)", + "tile.oc.keyboard": "Clavier", + "tile.oc.microcontroller": "Micro-contrôleur", + "tile.oc.motionsensor": "Détecteur de mouvement", + "tile.oc.netsplitter": "Répartiteur réseau", + "tile.oc.powerconverter": "Convertisseur énergétique", + "tile.oc.powerdistributor": "Distributeur énergétique", + "tile.oc.print": "Impression 3D", + "tile.oc.printer": "Imprimante 3D", + "tile.oc.raid": "Raid", + "tile.oc.redstone": "Redstone E/S", + "tile.oc.relay": "Relai", + "tile.oc.robot": "Robot", + "tile.oc.robotafterimage": "Robot", + "tile.oc.screen1": "Ecran (Niveau 1)", + "tile.oc.screen2": "Ecran (Niveau 2)", + "tile.oc.screen3": "Ecran (Niveau 3)", + "tile.oc.rack": "Support de serveur", + "tile.oc.switch": "Routeur", + "tile.oc.transposer": "Transposeur", + "tile.oc.waypoint": "Point de passage", + "item.oc.abstractbuscard": "Carte de bus abstraite", + "item.oc.acid": "Grog", + "item.oc.alu": "Unité de Logique Arithmétique (ULA)", + "item.oc.analyzer": "Analyseur", + "item.oc.apu0": "Unité de Traitement Accélérée (UTA) (Niveau 1)", + "item.oc.apu1": "Unité de Traitement Accélérée (UTA) (Niveau 2)", + "item.oc.apu2": "Unité de Traitement Accélérée (UTA) (Créatif)", + "item.oc.arrowkeys": "Touches directionnelles", + "item.oc.buttongroup": "Groupe de boutons", + "item.oc.cardbase": "Base de carte", + "item.oc.chamelium": "Chamélium", + "item.oc.circuitboard": "Plaque de circuit imprimé", + "item.oc.componentbus0": "Bus des composants (Niveau 1)", + "item.oc.componentbus1": "Bus des composants (Niveau 2)", + "item.oc.componentbus2": "Bus des composants (Niveau 3)", + "item.oc.controlunit": "Unité de contrôle (UC)", + "item.oc.cpu0": "Processeur (Niveau 1)", + "item.oc.cpu1": "Processeur (Niveau 2)", + "item.oc.cpu2": "Processeur (Niveau 3)", + "item.oc.cuttingwire": "Fil de coupe", + "item.oc.debugcard": "Carte de débogueur", + "item.oc.debugger": "Débogueur réseau", + "item.oc.disk": "Disque", + "item.oc.drone": "Drone", + "item.oc.dronecase0": "Boitier de drone (Niveau 1)", + "item.oc.dronecase1": "Boitier de drone (Niveau 2)", + "item.oc.dronecase3": "Boitier de drone (Créatif)", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "Disquette", + "item.oc.graphicscard0": "Carte graphique (Niveau 1)", + "item.oc.graphicscard1": "Carte graphique (Niveau 2)", + "item.oc.graphicscard2": "Carte graphique (Niveau 3)", + "item.oc.harddiskdrive0": "Disque dur (Niveau 1)", + "item.oc.harddiskdrive1": "Disque dur (Niveau 2)", + "item.oc.harddiskdrive2": "Disque dur (Niveau 3)", + "item.oc.hoverboots": "Bottes de vol plané", + "item.oc.inkcartridge": "Cartouche d'encre", + "item.oc.inkcartridgeempty": "Cartouche d'encre (Vide)", + "item.oc.internetcard": "Carte internet", + "item.oc.interweb": "Interweb", + "item.oc.ironnugget": "Pépite de fer", + "item.oc.linkedcard": "Carte liée", + "item.oc.manual": "Manuel d'OpenComputers", + "item.oc.memory0": "Mémoire (Niveau 1)", + "item.oc.memory1": "Mémoire (Niveau 1.5)", + "item.oc.memory2": "Mémoire (Niveau 2)", + "item.oc.memory3": "Mémoire (Niveau 2.5)", + "item.oc.memory4": "Mémoire (Niveau 3)", + "item.oc.memory5": "Mémoire (Niveau 3.5)", + "item.oc.microchip0": "Puce électronique (Niveau 1)", + "item.oc.microchip1": "Puce électronique (Niveau 2)", + "item.oc.microchip2": "Puce électronique (Niveau 3)", + "item.oc.microcontrollercase0": "Boitier de microcontrôleur (Niveau 1)", + "item.oc.microcontrollercase1": "Boitier de microcontrôleur (Niveau 2)", + "item.oc.microcontrollercase3": "Boitier de microcontrôleur (Créatif)", + "item.oc.networkcard": "Carte réseau", + "item.oc.numpad": "Pavé numérique", + "item.oc.present": "Un petit quelque chose...", + "item.oc.printedcircuitboard": "Circuit imprimé", + "item.oc.rawcircuitboard": "Plaque de circuit imprimé brute", + "item.oc.redstonecard0": "Carte de Redstone (Niveau 1)", + "item.oc.redstonecard1": "Carte de Redstone (Niveau 2)", + "item.oc.server0": "Serveur (Niveau 1)", + "item.oc.server1": "Serveur (Niveau 2)", + "item.oc.server2": "Serveur (Niveau 3)", + "item.oc.server3": "Serveur (Créatif)", + "item.oc.tablet": "Tablette", + "item.oc.tabletcase": "Boitier de tablette (Niveau 1)", + "item.oc.tabletcase": "Boitier de tablette (Niveau 2)", + "item.oc.tabletcase": "Boitier de tablette (Créatif)", + "item.oc.terminal": "Terminal à distance", + "item.oc.texturepicker": "Ramasseur de texture", + "item.oc.transistor": "Transistor", + "item.oc.upgradeangel": "Amélioration ange", + "item.oc.upgradebattery0": "Amélioration batterie (Niveau 1)", + "item.oc.upgradebattery1": "Amélioration batterie (Niveau 2)", + "item.oc.upgradebattery2": "Amélioration batterie (Niveau 3)", + "item.oc.upgradechunkloader": "Amélioration chargement de chunk", + "item.oc.upgradecontainercard0": "Conteneur de carte (Niveau 1)", + "item.oc.upgradecontainercard1": "Conteneur de carte (Niveau 2)", + "item.oc.upgradecontainercard2": "Conteneur de carte (Niveau 3)", + "item.oc.upgradecontainerupgrade0": "Amélioration de conteneur (Niveau 1)", + "item.oc.upgradecontainerupgrade1": "Amélioration de conteneur (Niveau 2)", + "item.oc.upgradecontainerupgrade2": "Amélioration de conteneur (Niveau 3)", + "item.oc.upgradecrafting": "Amélioration d'artisanat", + "item.oc.upgradedatabase0": "Amélioration de base de données (Niveau 1)", + "item.oc.upgradedatabase1": "Amélioration de base de données (Niveau 2)", + "item.oc.upgradedatabase2": "Amélioration de base de données (Niveau 3)", + "item.oc.upgradeexperience": "Amélioration d'expérience", + "item.oc.upgradegenerator": "Amélioration de générateur", + "item.oc.upgradehover0": "Amélioration de vol plané (Niveau 1)", + "item.oc.upgradehover1": "Amélioration de vol plané (Niveau 2)", + "item.oc.upgradeinventory": "Amélioration d'inventaire", + "item.oc.upgradeinventorycontroller": "Amélioration du contrôleur d'inventaire", + "item.oc.upgradeleash": "Amélioration de laisse", + "item.oc.upgradenavigation": "Amélioration de navigation", + "item.oc.upgradepiston": "Amélioration de piston", + "item.oc.upgradesign": "Amélioration de panneau d'E/S", + "item.oc.upgradesolargenerator": "Amélioration du générateur solaire", + "item.oc.upgradetank": "Amélioration de réservoir", + "item.oc.upgradetankcontroller": "Amélioration du contrôleur de réservoir", + "item.oc.upgradetractorbeam": "Amélioration du rayon tracteur", + "item.oc.wirelessnetworkcard0": "Carte de réseau sans-fils (Niveau 1)", + "item.oc.wirelessnetworkcard1": "Carte de réseau sans-fils (Niveau 2)", + "item.oc.worldsensorcard": "Carte de capteur du monde", + "item.oc.wrench": "Crisseur", + "entity.oc.Drone.name": "Drone", + "oc:gui.Analyzer.Address": "§6Addresse§f: %s", + "oc:gui.Analyzer.AddressCopied": "Adresse copiée dans le presse-papier.", + "oc:gui.Analyzer.ChargerSpeed": "§6Vitesse de charge§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Nom du composant§f: %s", + "oc:gui.Analyzer.Components": "§6Nombre de composants connectés§f: %s", + "oc:gui.Analyzer.LastError": "§6Dernière erreur§f: %s", + "oc:gui.Analyzer.RobotName": "§6Nom§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Propriétaire§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Expérience§f: %s", + "oc:gui.Analyzer.StoredEnergy": "§6Energie stockée§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Energie totale§f: %s", + "oc:gui.Analyzer.Users": "§6Utilisateurs§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Force du signal§f: %s", + "oc:gui.Assembler.Collect": "Collectez la sortie", + "oc:gui.Assembler.Complexity": "Complexité: %s/%s", + "oc:gui.Assembler.InsertCase": "Insérez une partie de base", + "oc:gui.Assembler.InsertCPU": "Insérez une UCP", + "oc:gui.Assembler.InsertRAM": "Insérez un peu de mémoire", + "oc:gui.Assembler.Progress": "Progression: %s%% (%s)", + "oc:gui.Assembler.Run": "Assemble", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Carte graphique", + "oc:gui.Assembler.Warning.Inventory": "Amélioration d'inventaire", + "oc:gui.Assembler.Warning.Keyboard": "Clavier", + "oc:gui.Assembler.Warning.OS": "Moyen de démarrage", + "oc:gui.Assembler.Warning.Screen": "Ecran", + "oc:gui.Assembler.Warnings": "§eALERTE§7: Les composants recommandés sont manquant.", + "oc:gui.Chat.NewVersion": "une nouvelle version est disponible: %s", + "oc:gui.Chat.TextureName": "§7Le nom de la texture est §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "Il y a §cerrors§f lancement dans le transformeur de classe. Veuillez vérifier ceci, en même temps que votre fichier log (plein) FML §alatest.log§f/§afml-server-latest.log§f, Merci !", + "oc:gui.Chat.WarningFingerprint": "§cALERTE§f - Disparité d'empreinte digitale! Attendu '§a%s§f' mais eu '§e%s§f'. À moins que vous ne soyez un modder et que vous exécutiez la version deobfuscated du mod, Il est §lfortement§f recommendé de re-télécharger OpenComputers, parce que le JAR que vous utilisez peut avoir été falsifié.", + "oc:gui.Chat.WarningLink": "Impossible d'ouvrir le lien : %s", + "oc:gui.Chat.WarningLuaFallback": "Les bibliothèques natives Lua ne sont pas disponibles, les ordinateurs ne pourront pas persister en leur état. Ils réamorceront sur la recharge des chunks.", + "oc:gui.Chat.WarningProjectRed": "Vous utilisez une version de Project: Red qui est incompatible avec OpenComputers. Essayez de mettre à jour votre version de Project: Red.", + "oc:gui.Chat.WarningRecipes": "il y a des erreurs au lancement de recettes. Certains éléments doivent être infabricables. Veuillez vérifier votre fichier log pour plus d'information.", + "oc:gui.Chat.WarningSimpleComponent": "Un ajout (le votre ?) utilisant l'interface §aComposant Simple§f produit §equelquechose de mauvais§f. Le composant logique ne peut être inséré. Veuillez vérifier votre fichier log pour plus d'information.", + "oc:gui.Error.ComponentOverflow": "Beaucoup trop de composants connectés à l'ordinateur.", + "oc:gui.Error.InternalError": "Erreur interne, Veuillez lire le fichier log. Ceci est probablement une erreur.", + "oc:gui.Error.NoCPU": "Pas d'UCP installé dans cet ordinateur.", + "oc:gui.Error.NoEnergy": "Pas assez d'énergie.", + "oc:gui.Error.NoRAM": "pas de mémoire installée dans cet ordinateur.", + "oc:gui.Error.OutOfMemory": "Mémoire saturée.", + "oc:gui.Manual.Blocks": "Blocs d'OpenComputers", + "oc:gui.Manual.Home": "Accueil", + "oc:gui.Manual.Items": "Éléments d'OpenComputers", + "oc:gui.Manual.Warning.BlockMissing": "Bloc manquant.", + "oc:gui.Manual.Warning.ImageMissing": "Image manquante.", + "oc:gui.Manual.Warning.ItemMissing": "Élément manquant.", + "oc:gui.Manual.Warning.OreDictMissing": "Entrée au dictionnaire de minerai manquante.", + "oc:gui.Raid.Warning": "§4L'ajout d'un disque le nettoie.\nL'enlèvement d'un disque nettoie le raid.", + "oc:gui.Robot.Power": "Énergie", + "oc:gui.Robot.TurnOff": "Éteindre", + "oc:gui.Robot.TurnOn": "Allumer", + "oc:gui.Rack.None": "Aucun", + "oc:gui.Rack.Back": "Arrière", + "oc:gui.Rack.Bottom": "Bas", + "oc:gui.Rack.Left": "Gauche", + "oc:gui.Rack.Right": "Droite", + "oc:gui.Rack.Top": "Haut", + "oc:gui.Switch.TransferRate": "Vitesse du cycle", + "oc:gui.Switch.PacketsPerCycle": "paquets / cycle", + "oc:gui.Switch.QueueSize": "Taille de la file", + "oc:gui.Terminal.InvalidKey": "Clef invalide, très probablement qu'un autre terminal a dû nécessité le serveur.", + "oc:gui.Terminal.OutOfRange": "Pas de signal.", + "oc:container.accesspoint": "Point d'accès", + "oc:container.adapter": "Adapteur", + "oc:container.case": "Ordinateur", + "oc:container.charger": "Chargeur", + "oc:container.disassembler": "Désassembleur", + "oc:container.diskdrive": "Lecteur disque", + "oc:container.printer": "Imprimante", + "oc:container.raid": "Raid", + "oc:container.server": "Serveur", + "oc:container.rack": "Support de serveur", + "oc:container.switch": "Routeur", + "oc:container.tabletwrapper": "Tablette", + "key.opencomputers.materialCosts": "Montrer les coût en matériaux", + "key.opencomputers.clipboardPaste": "Coller le presse-papier", + "oc:tooltip.accesspoint": "Fonctionne comme un routeur, de plus il recois les paquets sans-fil et il transmet les paquets filaires au réseau sans-fil.", + "oc:tooltip.abstractbuscard": "Il permet d'interagir avec le bus abstait de §fStargateTech 2§7 lors de l'envoi et de la reception de paquets LIP.", + "oc:tooltip.acid": "Un produit semi-liquide très toxique, uniquement bu par certains pirates. Grâce à ses propriétés corrosives, il est très utile à la gravure de circuits imprimés.", + "oc:tooltip.adapter": "Utilisé pour contrôler des blocs n'étant pas des composants d'ordinateurs, comme des blocs Vanilla ou d'autres mods.", + "oc:tooltip.alu": "Ajoute des nombres pour que vous n'ayez pas à le faire. C'est peut-être mieux comme ça.", + "oc:tooltip.analyzer": "Utilisé pour afficher des informations sur des blocs, comme leur §faddresse§7 et leur §fnom de composant§7.\nAffiche aussi les erreurs en cas de crash d'un ordinateur.", + "oc:tooltip.apu": "Ceci est une UTC avec une UTG intégrée (ou UTI), lorsque vous avez juste besoin d'un emplacement de carte supplémentaire.\nComposants supportés : §f%s§7\nRésolution maximum: §f%sx%s§7\nprofondeur de couleur maxi: §f%s§7\nOpérations/tic: §f%s§7", + "oc:tooltip.assembler": "Il permet de construire des robots et d'autres dispositifs d'un certain nombre de pièces informatiques différentes.", + "oc:tooltip.cable": "Un moyen peu coûteux pour relier les ordinateurs entre eux.", + "oc:tooltip.capacitor": "Stocke l'énergie pour une utilisation ultérieure. Peut être chargé et déchargé très rapidement.", + "oc:tooltip.cardbase": "Comme son nom l'indique, c'est le bloc de construction basique pour toutes les cartes.", + "oc:tooltip.case": "La tour est le bloc principal de l'ordinateur, et héberge ses §fcartes§7, sa §fMémoire RAM§7 et ses §fdisques durs§7.\nEmplacements: §f%s§7", + "oc:tooltip.chamelium": "Matière première pour impression 3D. Ne pas avaler : Peut mener à la cécité et au manque de présence provisoire.", + "oc:tooltip.chameliumblock": "Joli et propre. Pratique pour formes teintées dans les impressions 3D, ou juste pour avoir un bloc propre, coloré, pour décorer votre base fantaisiste avec.", + "oc:tooltip.charger": "Transfère l'énergie depuis des capaciteurs pour les robots adjacents. La puissance du transfert dépends du §fsignal de redstone§7, ou une absence de signal ne charge pas les robots, et un signal maximum charge les robots le plus vite possible.", + "oc:tooltip.circuitboard": "Hé ben, on y arrive. Peut être gravé pour obtenir un circuit imprimé.", + "oc:tooltip.controlunit": "C'est une unité qui... controle... des trucs. On en a besoin pour faire un processeur. Donc, ouais, super important.", + "oc:tooltip.componentbus": "Cette extention autorise aux serveurs de communiquer avec plus composants en même temps, Semblable à ce que les UCP font.\nComposants supportés : §f%s§7", + "oc:tooltip.cpu": "Un composant essentiel pour tout ordinateur. La fréquence d'horloge est un peu douteuse, mais, vous vous attendiez à quoi avec un cadran solaire de poche ?", + "oc:tooltip.cpu.Architecture": "Architecture: §f%s§7", + "oc:tooltip.cuttingwire": "Utilisé pour couper des blocs d'argile en plaque de circuit imprimé. Se casse après utilisation, ce qui rends cet outil le moins efficace jamais utilisé.", + "oc:tooltip.debugcard": "Element en mode créatif, il autorise la manipulation du monde pour faciliter les tests. Utilisation à votre propre péril.", + "oc:tooltip.debugger": "Peut être utilisé pour sortir les information de débogue sur la grille réseau interne de OC. A utiliser seulement si instruit par un dev.", + "oc:tooltip.disassembler": "Sépare les éléments dans leur composants d'origine. §lAttention§7: les éléments ainsi récupérés ont %s%% de chance de ce briser dans le processus !", + "oc:tooltip.disk": "Moyen primitif qui peut être utilisé pour faire des appareils de stockage persistants.", + "oc:tooltip.diskdrive.CC": "Les disquettes ComputerCraft sont §asupportées§7.", + "oc:tooltip.diskdrive": "Permet de lire et d'écrire des disquettes. Peut être installé dans les robots pour permettre d'insérer des disquettes plus tard.", + "oc:tooltip.drone": "Les drones sont de légère unité de reconnaissance rapide avec un espace de chargement limité.", + "oc:tooltip.dronecase": "Ce boitier est utilisé pour construire des Drones dans l'assembleur. Il a des emplacement pour une petite quantité de composant et fournit la lévitation en pierre de néant alimentée.", + "oc:tooltip.eeprom": "Petit, stockage programmable qui contient le BIOS que les ordinateurs utilisent pour démarrer.", + "oc:tooltip.fakeendstone": "Presque aussi bon que la chose réelle, imite même sa flottabilité !", + "oc:tooltip.geolyzer": "Permet de scanner la dureté des blocs des environs. Ces informations peuvent être utiles pour produire un hologramme de la zone ou pour détecter les minerais.", + "oc:tooltip.graphicscard": "Utilisé pour changer ce qui est affiché sur écran.\nRésolution maximum: §f%sx%s§7\nCouleurs maximales: §f%s§7\nOperations/tick: §f%s§7", + "oc:tooltip.hoverboots": "Sautez plus haut, tombez plus bas, marchez mieux. Ceci et plus, avec les nouvelles Bottes de vol plané (TM).", + "oc:tooltip.inkcartridge": "Utilisé pour remplir d'encre les imprimantes 3D. Pour des raisons mystérieuses, elle ne doit pas rester dans l'imprimante.", + "oc:tooltip.inkcartridgeempty": "Cette cartouche d'encre a été vidé totalement. Remplissez la avec des couleurs. Ou jetez la. Voyez comme je m'en soucie.", + "oc:tooltip.internetcard": "Cette carte permet de faire des requêtes HTTP et utiliser de véritable TCP sockets.", + "oc:tooltip.interweb": "Félicitation, vous avez gagné un (1) interweb. Vous pouvez y connecter en utilisant une carte internet. Prenez garde : n'alimentez pas les trolls.", + "oc:tooltip.ironnugget": "Une pépite de fer. D'où son nom.", + "oc:tooltip.keyboard": "Peut être attaché à un écran pour permettre la saisie.", + "oc:tooltip.hologram0": "Un affichage volumétrique qui peut être contrôlé par des ordinateurs pour montrer des structures de voxel arbitraires.\nRésolution : §f48x32x48§7\nEchelle maxi : §f3x§7\nColorimétrie : §fMonochrome§7", + "oc:tooltip.hologram1": "Un affichage volumétrique qui peut être contrôlé par des ordinateurs pour montrer des structures de voxel arbitraires.\nRésolution : §f48x32x48§7\nEchelle maxi : §f4x§7\nColorimétrie : §fTricolore§7", + "oc:tooltip.linkedcard": "Elles sont fabriqué par paire et ne peuvent communiquer qu'avec leur Carte partenaire. Cependant, elles peuvent communiquer à travers n'importe quelle distance et même à travers les dimensions. L'énergie exigée pour envoyer un message est assez haute malgès tout.", + "oc:tooltip.linkedcard_channel": "§8Canal : %s§7", + "oc:tooltip.manual": "Toutes les informations vous pourriez probablement avoir besoin sur OpenComputers. Et plus. Au prix incroyablement bas de... §oveuillez appuyer sur R pour continuer§7.", + "oc:tooltip.materialcosts": "Prend [§f%s§7] pour les coûts des matériaux.", + "oc:tooltip.materials": "Materiaux :", + "oc:tooltip.memory": "Requis pour faire fonctionner les ordinateurs. Plus vous en avez, plus complexes les programmes seront utilisables.", + "oc:tooltip.microchip": "La puce anciennement circuit intégré. J'ai aucune idée du comment ça marche avec la redstone, mais ça marche.", + "oc:tooltip.microcontroller": "Les microcontrôleurs sont des ordinateurs résumés à l'essentiel. Ils sont destinés pour s'occuper de tâches très spécifiques, dirigant seulement l'unique programme que lui donne l'EEPROM qu'il intègre.", + "oc:tooltip.microcontrollercase": "Composant de base pour construire des microcontrôleurs. Placez-le dans un assembleur pour ajouter de nouveaux composants et assemblez un microcontrôleur.", + "oc:tooltip.motionsensor": "Peut détecter le mouvement d'êtres vivants à proximité. Demande une ligne de vue dégagée.", + "oc:tooltip.networkcard": "Permet à des ordinateurs distants de communiquer en s'envoyant des messages.", + "oc:tooltip.poweracceptor": "Vitesse de convertion d'énergie: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft en MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fCharges de Factorization§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² en EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism en Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion en RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine en Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Convertis l'énergie d'autres mods en l'énergie requise pour faire fonctionner les ordinateurs.", + "oc:tooltip.powerdistributor": "Distribue l'énergie dans plusieurs réseaux. Utile pour partager cette énergie depuis un convertisseur vers différents sous-réseaux qui doivent rester séparés.", + "oc:tooltip.present": "... pour vos ennuis. Ouvrez ce cadeau pour une chance de recevoir quelques éléments d'OpenCompucters §kpsorti du chapeaut§7!\n§8 quand c'est le bon momment de recevoir un cadeau.§7", + "oc:tooltip.print.BeaconBase": "§8Fonctionne comme une base de balise.", + "oc:tooltip.print.LightValue": "§8Lumière émise : %s.", + "oc:tooltip.print.RedstoneLevel": "§8Sortie Redstone : %s.", + "oc:tooltip.printedcircuitboard": "Le composant basique pour les cartes, la mémoire, etc.", + "oc:tooltip.raid": "Permet de combiner trois disques durs dans un plus grand système de fichiers qui pourra être utilisé par tous les ordinateurs connectés.", + "oc:tooltip.rawcircuitboard": "Peut être durci dans n'importe quel four.", + "oc:tooltip.redstone": "Permet de recevoir et d'émettre des signaux de redstone autour du bloc. Contrôlable par n'importe quel ordinateur connecté à ce bloc. En gros, une carte redstone externe.", + "oc:tooltip.redstonecard.ProjectRed": "§fProjectRed§7 est §asupporté7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 est §asupporté§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 est §asupporté§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 est §asupporté§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 est §asupporté§7.", + "oc:tooltip.redstonecard": "Permet la réception et l'émission de signaux de redstone autour de l'ordinateur ou du robot.", + "oc:tooltip.robot": "Contrairement aux ordinateurs, les robots peuvent se déplacer et intéragir avec le monde comme un joueur le fait. Ils ne peuvent §opas§r§7 intéragir avec des composants d'ordinateur extérieurs, ceci dit !", + "oc:tooltip.robot_level": "§fNiveau§7: §a%s§7.", + "oc:tooltip.robot_storedenergy": "§fEnergie stockée§7: §a%s§7.", + "oc:tooltip.screen": "Affiche du texte, contrôlé par une Carte graphique dans un Boitier.\nRésolution maximum: §f%sx%s§7\nCouleurs maximales: §f%s§7", + "oc:tooltip.server": "Ceci est un serveur, il y en a beaucoup comme ca, mais celui-ci peut être amélioré avec des composants bien plus qu'un ordinateur peut l'être. Il peut être placé dans un support de serveur.", + "oc:tooltip.server.Components": "Composants installés :", + "oc:tooltip.rack": "Permet l'installation jusqu'à quatre serveurs.", + "oc:tooltip.switch": "Permet de connecter différents réseaux entre eux. Seulement des messages réseau seront transmis, les composants ne seront pas visibles via celui ci. A utiliser pour séparer des réseaux tout en leur permettant de communiquer entre eux, grâce aux Cartes réseau, par exemple.", + "oc:tooltip.tablet": "Une tablette, pour une Lua fraiche en route. Peut être forcé à l'arrêt par un accroupi-clique droit.", + "oc:tooltip.tabletcase": "Boitier de tablettes de base. Placez-le dans l'assembleur pour ajouter des composants et créez une tablette.", + "oc:tooltip.terminal": "Permet de contrôler un serveur à distance, tant que vous êtes à portée de lui. Fonctionne comme un écran-clavier portable. Maj-clique droit un serveur du support pour le lier au terminal.", + "oc:tooltip.tier": "§8Niv. %s", + "oc:tooltip.toolong": "Maintenez la touche [§f%s§7] pour plus d'informations.", + "oc:tooltip.transistor": "Un élément basique constituant la plupart des pièces d'un ordinateur. Il est un peu tordu, mais il fait son boulot.", + "oc:tooltip.upgradeangel": "Permet aux robots pour placer des blocs d'air mince, même s'il n'y a aucun point de référence.", + "oc:tooltip.upgradebattery": "Augmentez la quantité d'énergie qu'un robot peut stocker, le permettant de marcher plus longtemps sans devoir être rechargé.\nCapacité : §f%s§7", + "oc:tooltip.upgradechunkloader": "Si un robot se déplace dans une forêt sans personne à proximité, se déplace-il vraiment ? Cette amélioration s'assure qu'il le fait. Elle garde le chunk du robot chargé, mais consomme continuellement de l'énergie lors qu'il est actif.", + "oc:tooltip.upgradecontainercard": "Cette amélioration de conteneur permet l'installation ou le retrait dynamique d'une carte du robot assemblé.\nNiv. Maximum : §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Cette amélioration de conteneur permet l'installation ou le retrait dynamique d'une amélioration du robot assemblé.\nNiv. Maximum : §f%s§7", + "oc:tooltip.upgradecrafting": "Permet aux robots d'utiliser le coin en haut à gauche de leur inventaire comme table d'artisanat. Vous devez respecter la position des objets comme pour un artisanat normal.", + "oc:tooltip.upgradedatabase": "Cette amélioration permet de stoquer les information de pile d'éléments pour une utilisation future d'autres composants. Notez que ceci doit être configuré à la main.\nEntrées supportées : §f%s§7", + "oc:tooltip.upgradeexperience": "Cette amélioration permet aux robots d'accumuler de l'experience en exécutant diverses opérations. Plus ils gagnent d'expérience, plus ils peuvent stocker de l'énergie, miner plus rapidement et utiliser les outils plus efficacement.", + "oc:tooltip.upgradegenerator": "Utilisé pour générer de l'énergie directement sur un robot, indépendament d'un chargeur. Consume du comburant (ex: charbon) pour une génération sur la durée, en fonction de l'efficacité du combustible.\n§fEfficacité§7: §a%s%%§7", + "oc:tooltip.upgradeinventory": "Cette amélioration donne un espace d'inventaire au robot. Sans l'une d'elles, les robots ne seraient pas capable de stocker des éléments en interne.", + "oc:tooltip.upgradeinventorycontroller": "Cette amélioration permet au robot plus de contrôle sur ses interactions avec les inventaires externes, et leur permet d'échanger leurs outils avec un élément de son inventaire.", + "oc:tooltip.upgradenavigation": "Utilisé pour déterminer la position et l'orientation d'un robot. Cette position est relative au centre de la zone (affichée sur une carte) ou l'amélioration a été créée.", + "oc:tooltip.upgradepiston": "Cette amélioration est très rentre-dedans. Elle permet de déplacer des blocs comme lors de l'utilisation d'un piston. malgré tout, cela §lne déplace pas§7 les entités.", + "oc:tooltip.upgradesign": "Permets de lire et d'écrire sur des panneaux.", + "oc:tooltip.upgradesolargenerator": "Utilisé pour générer de l'énergie directement sur un robot, indépendament d'un chargeur, via le soleil. Aucun bloc ne doit se trouver au-dessus du robot pour que cette génération fonctionne. Génère de l'énergie à %s%% de la vitesse d'un moteur stirling (BuildCraft).", + "oc:tooltip.upgradetank": "Cette amélioration donne un réservoire de fluides au robot. Sans l'une d'elles, les robots ne seraient pas capable de stocker des fluides en interne.", + "oc:tooltip.upgradetankcontroller": "Cette amélioration permet au robot plus de contrôle sur ses interactions avec les réservoirs externes, et leur permet de transférer les fluides avec un élément réservoir de fluide de son inventaire.", + "oc:tooltip.upgradetractorbeam": "Equipe le robot avec une technologie extrêmement avancée, surnommé \"l'aimant d'éléments\". Cela lui permet de prendre des éléments n'importe où 3 blocs autour de son emplacement.", + "oc:tooltip.wirelessnetworkcard": "Permet l'envoi de messages réseaux sans fil. Pensez à régler la §fforce du signal§7, sinon aucun paquet ne sera envoyé!", + "nei.options.inventory.oredict": "Montrez les noms OreDictionary", + "nei.options.inventory.oredict.true": "Vrai", + "nei.options.inventory.oredict.false": "Faux", + "option.oc.address": "Adresse", + "option.oc.componentName": "Nom du composant", + "option.oc.energy": "Énergie" +} diff --git a/src/main/resources/assets/opencomputers/lang/it_IT.lang b/src/main/resources/assets/opencomputers/lang/it_IT.lang deleted file mode 100644 index 11dfb3ec67..0000000000 --- a/src/main/resources/assets/opencomputers/lang/it_IT.lang +++ /dev/null @@ -1,271 +0,0 @@ -# Use [nl] to for a line break. - -# Blocks -tile.oc.accesspoint.name=Punto di Accesso -tile.oc.adapter.name=Adattatore -tile.oc.assembler.name=Assemblatore Elettronico -tile.oc.cable.name=Cavo -tile.oc.capacitor.name=Condendatore -tile.oc.case1.name=Computer Case (Livello 1) -tile.oc.case2.name=Computer Case (Livello 2) -tile.oc.case3.name=Computer Case (Livello 3) -tile.oc.casecreative.name=Computer Case (Creativo) -tile.oc.charger.name=Caricabatterie -tile.oc.disassembler.name=Disassemblatore -tile.oc.diskdrive.name=Unità Disco -tile.oc.geolyzer.name=Geolyzer -tile.oc.keyboard.name=Tastiera -tile.oc.hologram1.name=Proiettore Ologrammi (Livello 1) -tile.oc.hologram2.name=Proiettore Ologrammi (Livello 2) -tile.oc.motionsensor.name=Sensore di Movimento -tile.oc.powerconverter.name=Convertitore di Energia -tile.oc.powerdistributor.name=Distributore di Energia -tile.oc.redstone.name=Pietrarossa I/O -tile.oc.robot.name=Robot -tile.oc.robotafterimage.name=Robot -tile.oc.screen1.name=Schermo (Livello 1) -tile.oc.screen2.name=Schermo (Livello 2) -tile.oc.screen3.name=Schermo (Livello 3) -tile.oc.rack.name=Rack -tile.oc.switch.name=Switch - -# Items -item.oc.abstractbuscard.name=Scheda Bus Astratto -item.oc.acid.name=Grog -item.oc.alu.name=Unità Aritmetica Logica (ALU) -item.oc.analyzer.name=Analizzatore -item.oc.arrowkeys.name=Tasti Freccia -item.oc.buttongroup.name=Gruppo di Pulsanti -item.oc.cardbase.name=Scheda Base -item.oc.circuitboard.name=Circuito -item.oc.controlunit.name=Unità di Controllo (CU) -item.oc.componentbus0.name=Bus Periferiche (Livello 1) -item.oc.componentbus1.name=Bus Periferiche (Livello 2) -item.oc.componentbus2.name=Bus Periferiche (Livello 3) -item.oc.cpu0.name=Unità di Elaborazione Centrale (CPU) (Livello 1) -item.oc.cpu1.name=Unità di Elaborazione Centrale (CPU) (Livello 2) -item.oc.cpu2.name=Unità di Elaborazione Centrale (CPU) (Livello 3) -item.oc.cuttingwire.name=Taglierina -item.oc.debugcard.name=Scheda di Debug -item.oc.disk.name=Disco -item.oc.floppydisk.name=Disco Floppy -item.oc.graphicscard0.name=Scheda Grafica (Livello 1) -item.oc.graphicscard1.name=Scheda Grafica (Livello 2) -item.oc.graphicscard2.name=Scheda Grafica (Livello 3) -item.oc.harddiskdrive0.name=Disco Rigido (Livello 1) -item.oc.harddiskdrive1.name=Disco Rigido (Livello 2) -item.oc.harddiskdrive2.name=Disco Rigido (Livello 3) -item.oc.internetcard.name=Scheda Internet -item.oc.interweb.name=Interweb -item.oc.ironnugget.name=Pepita di Ferro -item.oc.linkedcard.name=Scheda Collegata -item.oc.memory0.name=Memoria (Livello 1) -item.oc.memory1.name=Memoria (Livello 1.5) -item.oc.memory2.name=Memoria (Livello 2) -item.oc.memory3.name=Memoria (Livello 2.5) -item.oc.memory4.name=Memoria (Livello 3) -item.oc.memory5.name=Memoria (Livello 3.5) -item.oc.microchip0.name=Microchip (Livello 1) -item.oc.microchip1.name=Microchip (Livello 2) -item.oc.microchip2.name=Microchip (Livello 3) -item.oc.networkcard.name=Scheda di Rete -item.oc.numpad.name=Tastierino Numerico -item.oc.printedcircuitboard.name=Circuito Stampato (PCB) -item.oc.rawcircuitboard.name=Circuito Grezzo -item.oc.redstonecard0.name=Scheda Pietrarossa (Livello 1) -item.oc.redstonecard1.name=Scheda Pietrarossa (Livello 2) -item.oc.server0.name=Server (Livello 1) -item.oc.server1.name=Server (Livello 2) -item.oc.server2.name=Server (Livello 3) -item.oc.server3.name=Server (Creativo) -item.oc.tablet.name=Tablet -item.oc.tabletcase0.name=Custodia Tablet (Livello 1) -item.oc.tabletcase1.name=Custodia Tablet (Livello 2) -item.oc.tabletcase3.name=Custodia Tablet (Creativo) -item.oc.terminal.name=Terminale Remoto -item.oc.transistor.name=Transistor -item.oc.upgradeangel.name=Upgrade Angel -item.oc.upgradebattery0.name=Upgrade Batteria (Livello 1) -item.oc.upgradebattery1.name=Upgrade Batteria (Livello 2) -item.oc.upgradebattery2.name=Upgrade Batteria (Livello 3) -item.oc.upgradechunkloader.name=Upgrade Chunkloader -item.oc.upgradecontainercard0.name=Contenitore Schede (Livello 1) -item.oc.upgradecontainercard1.name=Contenitore Schede (Livello 2) -item.oc.upgradecontainercard2.name=Contenitore Schede (Livello 3) -item.oc.upgradecontainerupgrade0.name=Upgrade Contenitore (Livello 1) -item.oc.upgradecontainerupgrade1.name=Upgrade Contenitore (Livello 2) -item.oc.upgradecontainerupgrade2.name=Upgrade Contenitore (Livello 3) -item.oc.upgradecrafting.name=Upgrade Crafting -item.oc.upgradeexperience.name=Upgrade Esperienza -item.oc.upgradegenerator.name=Upgrade Generatore -item.oc.upgradeinventory.name=Upgrade Inventario -item.oc.upgradeinventorycontroller.name=Upgrade Controllore Inventario -item.oc.upgradenavigation.name=Upgrade Navigazione -item.oc.upgradepiston.name=Upgrade Pistoni -item.oc.upgradesign.name=Upgrade I/O Cartelli -item.oc.upgradesolargenerator.name=Upgrade Panneli Solari -item.oc.upgradetank.name=Upgrade Taniche -item.oc.upgradetankcontroller.name=Upgrade Controllore Taniche -item.oc.upgradetractorbeam.name=Upgrade Raggio Traente -item.oc.wirelessnetworkcard0.name=Scheda di Rete Wireless (Livello 1) -item.oc.wirelessnetworkcard1.name=Scheda di Rete Wireless (Livello 2) - -# GUI -oc:gui.Analyzer.Address=§6Indirizzo§f: %s -oc:gui.Analyzer.AddressCopied=Indirizzo copiato negli appunti. -oc:gui.Analyzer.ChargerSpeed=§6Velocità ricarica§f: %s -oc:gui.Analyzer.ComponentName=§6Nome componente§f: %s -oc:gui.Analyzer.Components=§6Numero componenti connessi§f: %s -oc:gui.Analyzer.LastError=§6Ultimo errore§f: %s -oc:gui.Analyzer.RobotName=§6Nome§f: %s -oc:gui.Analyzer.RobotOwner=§6Proprietario§f: %s -oc:gui.Analyzer.RobotXp=§6Esperienza§f: %s (Livelli %s) -oc:gui.Analyzer.StoredEnergy=§6Energia immagazzinata§f: %s -oc:gui.Analyzer.TotalEnergy=§6Totale energia immagazzinata§f: %s -oc:gui.Analyzer.Users=§6Utenti§f: %s -oc:gui.Analyzer.WirelessStrength=§6Potenza segnale§f: %s -oc:gui.Assembler.Collect=Collect output -oc:gui.Assembler.Complexity=Complessità: %s/%s -oc:gui.Assembler.InsertCase=Inserisci un case -oc:gui.Assembler.InsertCPU=Inserisci una CPU -oc:gui.Assembler.InsertRAM=Inserisci della RAM -oc:gui.Assembler.Progress=Progresso: %s%% (%s) -oc:gui.Assembler.Run=Assembla -oc:gui.Assembler.Warnings=§eAttenzione§7: Mancano i componenti raccomandati. -oc:gui.Assembler.Warning.GraphicsCard=Scheda Grafica -oc:gui.Assembler.Warning.Inventory=Upgrade Inventario -oc:gui.Assembler.Warning.Keyboard=Tastiera -oc:gui.Assembler.Warning.OS=Disco di Boot -oc:gui.Assembler.Warning.Screen=Schermo -oc:gui.Chat.NewVersion=Una nuova versione è disponibile: %s -oc:gui.Chat.WarningFingerprint=§cATTENZIONE§f - mancata corrispondenza dei fingerprint! Atteso '§a%s§f' ma ottenuto '§e%s§f'. Tranne nel caso tu sia un mod che stai utilizzando una versione decomppilata, è §lfortemente§f raccomantado riscaricare OpenComputers, perché il file JAR che stai utilizzando potrebbe essere stato corrotto. -oc:gui.Chat.WarningLuaFallback=Le librerie Lua Native non sono disponibili, i computers non saranno in gredo di mantenere il loro stato di persostenza. Essi verranno riavviati quando il chunk viene ricaricato. -oc:gui.Chat.WarningProjectRed=Stai utilizzando una versione di Project: Red che è incompatibile con OpenComputers. Prova ad aggriornare la tua versione di Project: Red. -oc:gui.Error.ComponentOverflow=Troppi componenti connessi al computer. -oc:gui.Error.InternalError=Errore interno, per favore vedi il file di log. Questo è probabilmente un bug. -oc:gui.Error.NoCPU=Nessuna CPU instllata nel computer. -oc:gui.Error.NoEnergy=Energia insufficiente. -oc:gui.Error.NoRAM=Nessuna RAM installata nel computer. -oc:gui.Error.OutOfMemory=Memoria esaurita. -oc:gui.Robot.Power=Energia -oc:gui.Robot.TurnOff=Spegni -oc:gui.Robot.TurnOn=Accendi -oc:gui.Rack.None=Nessuno -oc:gui.Rack.Back=Indietro -oc:gui.Rack.Bottom=Basso -oc:gui.Rack.Left=Sinistra -oc:gui.Rack.Right=Destra -oc:gui.Rack.Top=Alto -oc:gui.Switch.TransferRate=Tasso di ciclo -oc:gui.Switch.PacketsPerCycle=Pacchetti / ciclo -oc:gui.Switch.QueueSize=Dimensione coda -oc:gui.Terminal.InvalidKey=Tasto invalido, molto probabilmente un altro terminale è stato collegato al server. -oc:gui.Terminal.OutOfRange=Nessun segnale - -# Containers -oc:container.accesspoint=Punto di accesso -oc:container.adapter=Adattatore -oc:container.charger=Caricabatterie -oc:container.case=Computer -oc:container.disassembler=Disassemblatore -oc:container.diskdrive=Unità Disco -oc:container.server=Server -oc:container.rack=Rack -oc:container.switch=Switch -oc:container.tabletwrapper=Tablet - -# Keybinds -key.materialCosts=Mostra Costi Materiali -key.clipboardPaste=Copia negli Appunti - -# Item / Block Tooltips -oc:tooltip.accesspoint=Funziona come uno Switch, ma può anche ricevere pacchetti wireless ed inoltrare pacchetti ricevuti via cavo in modo wireless. -oc:tooltip.abstractbuscard=Consente di interagire con il bus astratto di §fStargateTech 2§7 inviando e ricevendo pacchetti LIP. -oc:tooltip.acid=Un liquido altamente tossico, normalmente consumato solo da alcuni pirati. Grazie alla sua natura corrosiva è particolarmente adatto per l'incisione dei circuiti stampati. -oc:tooltip.adapter=Utilizzato per controllare blocchi che non sono componenti, come blocchi vanilla di minecraft e blocchi di altri mod. -oc:tooltip.alu=Calcola le somme così non devi farlo tu. Potrebbe essere meglgio così. -oc:tooltip.analyzer=Utilizzato per mostrare informazioni riguardo i blocchi, come il loro §findirizzo§7 e il loro §fnome del componente§7.[nl] Inoltre mostra gli errori che hanno causato un crash del computer, se non è stato spento normalmente. -oc:tooltip.assembler=Permette di costruire robot e altri dispositivi da un certo numero di parti di computer. -oc:tooltip.cable=Un modo economico per connettere i blocchi. -oc:tooltip.capacitor=Immagazzina energia per un uso successivo. Può essere riempito e svuotato molto rapidamente. -oc:tooltip.cardbase=Come indica il nome, questo è l'elemento di base per tutte le schede di espansione. -oc:tooltip.case=Il case del computer è l'elemento di base per i computer e ospita le §fschede di espansione§7, la §fRAM§7 e il §fdisco rigido§7.[nl] Slots: §f%s§7 -oc:tooltip.charger=Trasferisce energia nei robot adiacenti. La velocità di trasferimento dipende dal §fsegnale di pietrarossa§7 applicato, dove nessun segnale significa di non ricaricare i robot e segnale massimo significa ricarica alla massima velocità. -oc:tooltip.circuitboard=Ora stiamo ottenendo qualcosa. Può essere inciso per ottenere un circuito stampato. -oc:tooltip.controlunit=Questa è l'unità che ... controlla ... cose. È necessario per costruire una CPU. Quindi sì, è assolutamente importante. -oc:tooltip.componentbus=Questa espansione consente ai server di comunicare con più componenti, allo stesso tempo, in modo simile a come fanno le CPU.[nl] Componenti supportati: §f%s§7 -oc:tooltip.cpu=Una componente essenziale di tutti i computer. La frequenza di clock è un po' inaffidabile, ma cosa vi aspettate quando viene eseguito da una meridiana tascabile?[nl] Componenti supportati: §f%s§7 -oc:tooltip.cpu.Architecture=Architettura: §f%s§7 -oc:tooltip.cuttingwire=Usato per tagliare i blocchi di argilla in circuiti. Si rompe dopo l'uso, che rende probabilmente lo rende strumento più inefficiente mai visto. -oc:tooltip.debugcard=Oggetto per la modalità creativa, permette manipolare il mondo per rendere il testing più facile. Utilizzare a proprio rischio e pericolo. -oc:tooltip.disassembler=Separa gli oggetti nelle loro componenti originali. §lAttenzione§7: gli oggetti restituiti hanno il %s%% di probabilità di rompersi nel processo! -oc:tooltip.disk=Mezzo primitivo che può essere utilizzato per costruire dispositivi di memorizzazione persistente. -oc:tooltip.diskdrive.CC=Floppy di ComputerCraft sono §asupportati§7. -oc:tooltip.diskdrive=Permette di leggere e scrivere floppy. Può essere installato in robot per consentire in seguito l'inserimento di floppy. -oc:tooltip.geolyzer=Permette la scansione di durezza dei blocchi della zona circostante. Questa informazione può essere utile per generare ologrammi della zona o per rilevare minerali. -oc:tooltip.graphicscard=Usato per cambiare ciò che è visualizzato sullo schermo.[nl] Risoluzione massima: §f%sx%s§7[nl] Massima profondità di colore: §f%s§7[nl] Operazioni/ciclo: §f%s§7 -oc:tooltip.internetcard=Questa scheda consente di effettuare richieste HTTP e l'utilizzo di socket TCP reali. -oc:tooltip.interweb=Congratulazioni, hai vinto un (1) interweb. È possibile connettersi ad esso utilizzando una scheda di Internet. Attenzione: non alimentare i troll. -oc:tooltip.ironnugget=Una pepita fatta di ferro, ecco perché si chiama Pepita di Ferro, duh ... -oc:tooltip.keyboard=Può essere attaccato agli schermi per consentire la digitazione su di essi. -oc:tooltip.hologram0=Un display volumetrico che può essere controllato da computer per visualizzare le strutture voxel arbitrarie.[nl] Risoluzione: §f48x32x48§7 [nl] Scala massima: §f3x§7 [nl] Profondità di colore: §fMonocromatico§7 -oc:tooltip.hologram1=Un display volumetrico che può essere controllato da computer per visualizzare le strutture voxel arbitrarie.[nl] Risoluzione: §f48x32x48§7 [nl] Scala massima: §f4x§7 [nl] Profondità di colore: §fTricolore§7 -oc:tooltip.linkedcard=Questi sono realizzati a coppie, e possono comunicare solo con la loro scheda associata. Tuttavia, essi possono comunicare attraverso qualsiasi distanza, e anche attraverso le dimensioni. L'energia richiesta per inviare un messaggio però è piuttosto alta. -oc:tooltip.linkedcard_channel=§8Canale: %s§7 -oc:tooltip.materialcosts=Tieni premuto [§f%s§7] per il costo in materiali. -oc:tooltip.materials=Materiali: -oc:tooltip.memory=Necessaria per far funzionare i computer. Più ne hai, più complessi i programmi che possono essere eseguiti. -oc:tooltip.microchip=Il chip precedentemente noto come circuito integrato. Non ho idea del perché questo funziona con la pietrarossa, ma lo fa. -oc:tooltip.motionsensor=In grado di rilevare il movimento di esseri viventi nelle vicinanze. Richiede linea di vista ininterrotta. -oc:tooltip.networkcard=Consente ai computer remoti connessi da altri blocchi (come i cavi) di comunicare con l'invio di messaggi. -oc:tooltip.poweracceptor=Velocità di conversione di energia: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fFactorization Charge§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Converte l'energia da altri mod nel tipo di energia interna. Tassi di conversione: -oc:tooltip.powerdistributor=Distribuisce l'eenergia tra reti diverse. Questo è utile per la condivisione di energia immessa nel sistema da un convertitore tra le diverse sotto-reti che dovrebbero rimanere separati. -oc:tooltip.printedcircuitboard=L'elemento di base per schede di espansione, per la memoria e così via. -oc:tooltip.rawcircuitboard=Può essere indurito in qualsiasi forno o fornace compatibile. -oc:tooltip.redstone=Permette la lettura e l'emissione di segnali di pietrarossa attorno tutto il blocco. Può essere controllato da qualsiasi computer che è collegato. Questo è fondamentalmente come una scheda di pietrarossa esterna. -oc:tooltip.redstonecard.ProjectRed=§fProjectRed§7 è §asupportato§7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 è §asupportato§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 è §asupportato§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 è §asupportato§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 è §asupportato§7. -oc:tooltip.redstonecard=Permette la lettura e l'emissione di segnali di pietrarossa attorno tutto il computer o robot. -oc:tooltip.robot=A differenza dei computer, i robot possono muoversi e interagire con il mondo, molto simile a come può fare un giocatore. Tuttavia §onon§7 possono interagire con componenti esterni!U -# the underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fLivelli§7: §a%s§7 -oc:tooltip.robot_storedenergy=§fEnergia immagazzinata§7: §a%s§7 -oc:tooltip.screen=Mostra del testo, contollato da una scheda grafica contenuta nel case del computer.[nl] Risoluzione massima: §f%sx%s§7[nl] Profondità di colore massima: §f%s§7 -oc:tooltip.server=Questo è un server, ci sono tanti come lui, ma questo può essere aggiornato con i componenti in modo simile a un case del computer. Può essere avviato inserendolo in un rack di server. -oc:tooltip.server.Components=Componenti installati: -oc:tooltip.rack=Permette l'installazione di un massimo di quattro server. Utilizzare un terminale remoto per accedere ai server contenuti in questo server rack. -oc:tooltip.switch=Consente il collegamento di reti diverse tra loro. Solo i messaggi di rete saranno inoltrati attraverso il blocco, i componenti non saranno invece visibili Utilizzarlo per separare le reti pur consentendo la comunicazione utilizzando schede di rete, per esempio. -oc:tooltip.tablet=Un computer tablet, per del Lua fresco in movimento. Può essere forzato lo spegnimento usandolo mentre si preme maiusc. -oc:tooltip.tabletcase=Case di base per i tablet. Posizionarlo in assembler per aggiungere componenti e creare un tablet PC. -oc:tooltip.terminal=Permette il controllo di un server in remoto, fino a quando si è nella portata di esso. Agisce come uno schermo portatile e una tastiera. Utilizzare i tasti maiusc-destro del mouse su un server in un rack di server per associare il terminale ad esso. -oc:tooltip.livello=§8Livello %s -oc:tooltip.toolong=Tenere premuto [§f%s§7] per un tooltip dettagliato. -oc:tooltip.transistor=Un elemento fondamentale in molte altre parti del computer. È un po' contorto, ma fa il suo lavoro. -oc:tooltip.upgradeangel=Consente ai robot di posizionare i blocchi nel nulla, anche se non vi è alcun blocco di riferimento. -oc:tooltip.upgradebattery=Aumenta la quantità di energia che un robot è in grado di immagazzinare, permettendogli di lavorare più a lungo senza dover essere ricaricato. [nl] capacità: §f%s§7 -oc:tooltip.upgradechunkloader=Se un robot si muove in una foresta e non c'è nessuno intorno per vederlo, si muove veramente? Questo aggiornamento consente di essere certi che lo faccia. Mantiene il chunk dove si trova il robot caricato, ma consuma continuamente energia, mentre è attivo. -oc:tooltip.upgradecontainercard=Questo aggiornamento consente l'installazione e la rimozione di schede da un robot assemblato dinamicamente. [nl] Livello massimo: §f%s§7 -oc:tooltip.upgradecontainerupgrade=Questo aggiornamento consente l'installazione e la rimozione di upgrade da un robot assemblato dinamicamente. [nl] Livello massimo: §f%s§7 -oc:tooltip.upgradecrafting=Consente robot per utilizzare l'area in alto a sinistra del loro inventario per il crafting di oggetti. Gli oggetti devono essere allineati come sarebbero in un banco da lavoro. -oc:tooltip.upgradeexperience=Questo aggiornamento consente di robot di accumulare esperienza, quando effettuano diverse operazioni. Maggiore è la loro esperienza, più energia sono in grado di memorizzare, più velocemente possono raccogliere blocchi e più efficientemente utilizzano gli strumenti. -oc:tooltip.upgradegenerator=Può essere usato per generare energia dal combustibile mentre si è in movimento. Brucia oggetti per produrre energia nel corso del tempo, in base al loro valore di carburante.[nl] §fEfficienza§7: §a%s%%§7 -oc:tooltip.upgradeinventory=Questo aggiornamento fornisce spazio nell'inventario al robot. Senza uno di questi, i robot non saranno in grado di immagazzinare gli oggetti internamente. -oc:tooltip.upgradeinventorycontroller=Questo aggiornamento permette al robot più controllo nel modo in cui interagisce con gli inventari esterni, e gli permette di scambiare il suo strumento equipaggiato con uno nel suo inventario. -oc:tooltip.upgradenavigation=Può essere usato per determinare la posizione e l'orientamento del robot. La posizione è relativa al centro della mappa che è stata utilizzata per il crafting tale aggiornamento. -oc:tooltip.upgradepiston=Questo aggiornamentp è molto spinto. Permette di spingere i blocchi, in modo simile ad un pistone. Tuttavia §lnon§7 muove le entità. -oc:tooltip.upgradesign=Consente la lettura del testo e sulla scrittura del testo sui cartelli. -oc:tooltip.upgradesolargenerator=Può essere usato per generare energia dalla luce del sole. Richiede una chiara linea di vista verso il cielo sopra il robot. Genera energia al %s%% della velocità di un motore Stirling. -oc:tooltip.upgradetank=Questo aggiornamento fornisce un serbatoio per lo stoccaggio di liquidi al robot. Senza uno di questi, i robot non saranno in grado di immagazzinare i fluidi internamente. -oc:tooltip.upgradetankcontroller=Questo aggiornamento permette al robot più controllo nel modo in cui interagisce con serbatoi esterni, e permette di trasferire i fluidi dentro e fuori dal serbatoio nel suo inventario. -oc:tooltip.upgradetractorbeam=Dota il robot con tecnologia estremamente avanzata, soprannominato il "Magnet Item". Consente a raccogliere oggetti ovunque entro 3 blocchi dalla sua posizione. -oc:tooltip.wirelessnetworkcard=Consente invio di messaggi wireless in aggiunta a quelli normali. È possibile regolare la §fpotenza del segnale§7 per controllare quanto lontano i messaggi vengono inviati. Una più elevata potenza del segnale richiede un maggior consumo energetico. diff --git a/src/main/resources/assets/opencomputers/lang/it_it.json b/src/main/resources/assets/opencomputers/lang/it_it.json new file mode 100644 index 0000000000..fabc78d1d1 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/it_it.json @@ -0,0 +1,259 @@ +{ + "tile.oc.accesspoint": "Punto di Accesso", + "tile.oc.adapter": "Adattatore", + "tile.oc.assembler": "Assemblatore Elettronico", + "tile.oc.cable": "Cavo", + "tile.oc.capacitor": "Condendatore", + "tile.oc.case1": "Computer Case (Livello 1)", + "tile.oc.case2": "Computer Case (Livello 2)", + "tile.oc.case3": "Computer Case (Livello 3)", + "tile.oc.casecreative": "Computer Case (Creativo)", + "tile.oc.charger": "Caricabatterie", + "tile.oc.disassembler": "Disassemblatore", + "tile.oc.diskdrive": "Unità Disco", + "tile.oc.geolyzer": "Geolyzer", + "tile.oc.keyboard": "Tastiera", + "tile.oc.hologram1": "Proiettore Ologrammi (Livello 1)", + "tile.oc.hologram2": "Proiettore Ologrammi (Livello 2)", + "tile.oc.motionsensor": "Sensore di Movimento", + "tile.oc.powerconverter": "Convertitore di Energia", + "tile.oc.powerdistributor": "Distributore di Energia", + "tile.oc.redstone": "Pietrarossa I/O", + "tile.oc.robot": "Robot", + "tile.oc.robotafterimage": "Robot", + "tile.oc.screen1": "Schermo (Livello 1)", + "tile.oc.screen2": "Schermo (Livello 2)", + "tile.oc.screen3": "Schermo (Livello 3)", + "tile.oc.rack": "Rack", + "tile.oc.switch": "Switch", + "item.oc.abstractbuscard": "Scheda Bus Astratto", + "item.oc.acid": "Grog", + "item.oc.alu": "Unità Aritmetica Logica (ALU)", + "item.oc.analyzer": "Analizzatore", + "item.oc.arrowkeys": "Tasti Freccia", + "item.oc.buttongroup": "Gruppo di Pulsanti", + "item.oc.cardbase": "Scheda Base", + "item.oc.circuitboard": "Circuito", + "item.oc.controlunit": "Unità di Controllo (CU)", + "item.oc.componentbus0": "Bus Periferiche (Livello 1)", + "item.oc.componentbus1": "Bus Periferiche (Livello 2)", + "item.oc.componentbus2": "Bus Periferiche (Livello 3)", + "item.oc.cpu0": "Unità di Elaborazione Centrale (CPU) (Livello 1)", + "item.oc.cpu1": "Unità di Elaborazione Centrale (CPU) (Livello 2)", + "item.oc.cpu2": "Unità di Elaborazione Centrale (CPU) (Livello 3)", + "item.oc.cuttingwire": "Taglierina", + "item.oc.debugcard": "Scheda di Debug", + "item.oc.disk": "Disco", + "item.oc.floppydisk": "Disco Floppy", + "item.oc.graphicscard0": "Scheda Grafica (Livello 1)", + "item.oc.graphicscard1": "Scheda Grafica (Livello 2)", + "item.oc.graphicscard2": "Scheda Grafica (Livello 3)", + "item.oc.harddiskdrive0": "Disco Rigido (Livello 1)", + "item.oc.harddiskdrive1": "Disco Rigido (Livello 2)", + "item.oc.harddiskdrive2": "Disco Rigido (Livello 3)", + "item.oc.internetcard": "Scheda Internet", + "item.oc.interweb": "Interweb", + "item.oc.ironnugget": "Pepita di Ferro", + "item.oc.linkedcard": "Scheda Collegata", + "item.oc.memory0": "Memoria (Livello 1)", + "item.oc.memory1": "Memoria (Livello 1.5)", + "item.oc.memory2": "Memoria (Livello 2)", + "item.oc.memory3": "Memoria (Livello 2.5)", + "item.oc.memory4": "Memoria (Livello 3)", + "item.oc.memory5": "Memoria (Livello 3.5)", + "item.oc.microchip0": "Microchip (Livello 1)", + "item.oc.microchip1": "Microchip (Livello 2)", + "item.oc.microchip2": "Microchip (Livello 3)", + "item.oc.networkcard": "Scheda di Rete", + "item.oc.numpad": "Tastierino Numerico", + "item.oc.printedcircuitboard": "Circuito Stampato (PCB)", + "item.oc.rawcircuitboard": "Circuito Grezzo", + "item.oc.redstonecard0": "Scheda Pietrarossa (Livello 1)", + "item.oc.redstonecard1": "Scheda Pietrarossa (Livello 2)", + "item.oc.server0": "Server (Livello 1)", + "item.oc.server1": "Server (Livello 2)", + "item.oc.server2": "Server (Livello 3)", + "item.oc.server3": "Server (Creativo)", + "item.oc.tablet": "Tablet", + "item.oc.tabletcase0": "Custodia Tablet (Livello 1)", + "item.oc.tabletcase1": "Custodia Tablet (Livello 2)", + "item.oc.tabletcase3": "Custodia Tablet (Creativo)", + "item.oc.terminal": "Terminale Remoto", + "item.oc.transistor": "Transistor", + "item.oc.upgradeangel": "Upgrade Angel", + "item.oc.upgradebattery0": "Upgrade Batteria (Livello 1)", + "item.oc.upgradebattery1": "Upgrade Batteria (Livello 2)", + "item.oc.upgradebattery2": "Upgrade Batteria (Livello 3)", + "item.oc.upgradechunkloader": "Upgrade Chunkloader", + "item.oc.upgradecontainercard0": "Contenitore Schede (Livello 1)", + "item.oc.upgradecontainercard1": "Contenitore Schede (Livello 2)", + "item.oc.upgradecontainercard2": "Contenitore Schede (Livello 3)", + "item.oc.upgradecontainerupgrade0": "Upgrade Contenitore (Livello 1)", + "item.oc.upgradecontainerupgrade1": "Upgrade Contenitore (Livello 2)", + "item.oc.upgradecontainerupgrade2": "Upgrade Contenitore (Livello 3)", + "item.oc.upgradecrafting": "Upgrade Crafting", + "item.oc.upgradeexperience": "Upgrade Esperienza", + "item.oc.upgradegenerator": "Upgrade Generatore", + "item.oc.upgradeinventory": "Upgrade Inventario", + "item.oc.upgradeinventorycontroller": "Upgrade Controllore Inventario", + "item.oc.upgradenavigation": "Upgrade Navigazione", + "item.oc.upgradepiston": "Upgrade Pistoni", + "item.oc.upgradesign": "Upgrade I/O Cartelli", + "item.oc.upgradesolargenerator": "Upgrade Panneli Solari", + "item.oc.upgradetank": "Upgrade Taniche", + "item.oc.upgradetankcontroller": "Upgrade Controllore Taniche", + "item.oc.upgradetractorbeam": "Upgrade Raggio Traente", + "item.oc.wirelessnetworkcard0": "Scheda di Rete Wireless (Livello 1)", + "item.oc.wirelessnetworkcard1": "Scheda di Rete Wireless (Livello 2)", + "oc:gui.Analyzer.Address": "§6Indirizzo§f: %s", + "oc:gui.Analyzer.AddressCopied": "Indirizzo copiato negli appunti.", + "oc:gui.Analyzer.ChargerSpeed": "§6Velocità ricarica§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Nome componente§f: %s", + "oc:gui.Analyzer.Components": "§6Numero componenti connessi§f: %s", + "oc:gui.Analyzer.LastError": "§6Ultimo errore§f: %s", + "oc:gui.Analyzer.RobotName": "§6Nome§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Proprietario§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Esperienza§f: %s (Livelli %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Energia immagazzinata§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Totale energia immagazzinata§f: %s", + "oc:gui.Analyzer.Users": "§6Utenti§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Potenza segnale§f: %s", + "oc:gui.Assembler.Collect": "Collect output", + "oc:gui.Assembler.Complexity": "Complessità: %s/%s", + "oc:gui.Assembler.InsertCase": "Inserisci un case", + "oc:gui.Assembler.InsertCPU": "Inserisci una CPU", + "oc:gui.Assembler.InsertRAM": "Inserisci della RAM", + "oc:gui.Assembler.Progress": "Progresso: %s%% (%s)", + "oc:gui.Assembler.Run": "Assembla", + "oc:gui.Assembler.Warnings": "§eAttenzione§7: Mancano i componenti raccomandati.", + "oc:gui.Assembler.Warning.GraphicsCard": "Scheda Grafica", + "oc:gui.Assembler.Warning.Inventory": "Upgrade Inventario", + "oc:gui.Assembler.Warning.Keyboard": "Tastiera", + "oc:gui.Assembler.Warning.OS": "Disco di Boot", + "oc:gui.Assembler.Warning.Screen": "Schermo", + "oc:gui.Chat.NewVersion": "Una nuova versione è disponibile: %s", + "oc:gui.Chat.WarningFingerprint": "§cATTENZIONE§f - mancata corrispondenza dei fingerprint! Atteso '§a%s§f' ma ottenuto '§e%s§f'. Tranne nel caso tu sia un mod che stai utilizzando una versione decomppilata, è §lfortemente§f raccomantado riscaricare OpenComputers, perché il file JAR che stai utilizzando potrebbe essere stato corrotto.", + "oc:gui.Chat.WarningLuaFallback": "Le librerie Lua Native non sono disponibili, i computers non saranno in gredo di mantenere il loro stato di persostenza. Essi verranno riavviati quando il chunk viene ricaricato.", + "oc:gui.Chat.WarningProjectRed": "Stai utilizzando una versione di Project: Red che è incompatibile con OpenComputers. Prova ad aggriornare la tua versione di Project: Red.", + "oc:gui.Error.ComponentOverflow": "Troppi componenti connessi al computer.", + "oc:gui.Error.InternalError": "Errore interno, per favore vedi il file di log. Questo è probabilmente un bug.", + "oc:gui.Error.NoCPU": "Nessuna CPU instllata nel computer.", + "oc:gui.Error.NoEnergy": "Energia insufficiente.", + "oc:gui.Error.NoRAM": "Nessuna RAM installata nel computer.", + "oc:gui.Error.OutOfMemory": "Memoria esaurita.", + "oc:gui.Robot.Power": "Energia", + "oc:gui.Robot.TurnOff": "Spegni", + "oc:gui.Robot.TurnOn": "Accendi", + "oc:gui.Rack.None": "Nessuno", + "oc:gui.Rack.Back": "Indietro", + "oc:gui.Rack.Bottom": "Basso", + "oc:gui.Rack.Left": "Sinistra", + "oc:gui.Rack.Right": "Destra", + "oc:gui.Rack.Top": "Alto", + "oc:gui.Switch.TransferRate": "Tasso di ciclo", + "oc:gui.Switch.PacketsPerCycle": "Pacchetti / ciclo", + "oc:gui.Switch.QueueSize": "Dimensione coda", + "oc:gui.Terminal.InvalidKey": "Tasto invalido, molto probabilmente un altro terminale è stato collegato al server.", + "oc:gui.Terminal.OutOfRange": "Nessun segnale", + "oc:container.accesspoint": "Punto di accesso", + "oc:container.adapter": "Adattatore", + "oc:container.charger": "Caricabatterie", + "oc:container.case": "Computer", + "oc:container.disassembler": "Disassemblatore", + "oc:container.diskdrive": "Unità Disco", + "oc:container.server": "Server", + "oc:container.rack": "Rack", + "oc:container.switch": "Switch", + "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.materialCosts": "Mostra Costi Materiali", + "key.opencomputers.clipboardPaste": "Copia negli Appunti", + "oc:tooltip.accesspoint": "Funziona come uno Switch, ma può anche ricevere pacchetti wireless ed inoltrare pacchetti ricevuti via cavo in modo wireless.", + "oc:tooltip.abstractbuscard": "Consente di interagire con il bus astratto di §fStargateTech 2§7 inviando e ricevendo pacchetti LIP.", + "oc:tooltip.acid": "Un liquido altamente tossico, normalmente consumato solo da alcuni pirati. Grazie alla sua natura corrosiva è particolarmente adatto per l'incisione dei circuiti stampati.", + "oc:tooltip.adapter": "Utilizzato per controllare blocchi che non sono componenti, come blocchi vanilla di minecraft e blocchi di altri mod.", + "oc:tooltip.alu": "Calcola le somme così non devi farlo tu. Potrebbe essere meglgio così.", + "oc:tooltip.analyzer": "Utilizzato per mostrare informazioni riguardo i blocchi, come il loro §findirizzo§7 e il loro §fnome del componente§7.\nInoltre mostra gli errori che hanno causato un crash del computer, se non è stato spento normalmente.", + "oc:tooltip.assembler": "Permette di costruire robot e altri dispositivi da un certo numero di parti di computer.", + "oc:tooltip.cable": "Un modo economico per connettere i blocchi.", + "oc:tooltip.capacitor": "Immagazzina energia per un uso successivo. Può essere riempito e svuotato molto rapidamente.", + "oc:tooltip.cardbase": "Come indica il nome, questo è l'elemento di base per tutte le schede di espansione.", + "oc:tooltip.case": "Il case del computer è l'elemento di base per i computer e ospita le §fschede di espansione§7, la §fRAM§7 e il §fdisco rigido§7.\nSlots: §f%s§7", + "oc:tooltip.charger": "Trasferisce energia nei robot adiacenti. La velocità di trasferimento dipende dal §fsegnale di pietrarossa§7 applicato, dove nessun segnale significa di non ricaricare i robot e segnale massimo significa ricarica alla massima velocità.", + "oc:tooltip.circuitboard": "Ora stiamo ottenendo qualcosa. Può essere inciso per ottenere un circuito stampato.", + "oc:tooltip.controlunit": "Questa è l'unità che ... controlla ... cose. È necessario per costruire una CPU. Quindi sì, è assolutamente importante.", + "oc:tooltip.componentbus": "Questa espansione consente ai server di comunicare con più componenti, allo stesso tempo, in modo simile a come fanno le CPU.\nComponenti supportati: §f%s§7", + "oc:tooltip.cpu": "Una componente essenziale di tutti i computer. La frequenza di clock è un po' inaffidabile, ma cosa vi aspettate quando viene eseguito da una meridiana tascabile?\nComponenti supportati: §f%s§7", + "oc:tooltip.cpu.Architecture": "Architettura: §f%s§7", + "oc:tooltip.cuttingwire": "Usato per tagliare i blocchi di argilla in circuiti. Si rompe dopo l'uso, che rende probabilmente lo rende strumento più inefficiente mai visto.", + "oc:tooltip.debugcard": "Oggetto per la modalità creativa, permette manipolare il mondo per rendere il testing più facile. Utilizzare a proprio rischio e pericolo.", + "oc:tooltip.disassembler": "Separa gli oggetti nelle loro componenti originali. §lAttenzione§7: gli oggetti restituiti hanno il %s%% di probabilità di rompersi nel processo!", + "oc:tooltip.disk": "Mezzo primitivo che può essere utilizzato per costruire dispositivi di memorizzazione persistente.", + "oc:tooltip.diskdrive.CC": "Floppy di ComputerCraft sono §asupportati§7.", + "oc:tooltip.diskdrive": "Permette di leggere e scrivere floppy. Può essere installato in robot per consentire in seguito l'inserimento di floppy.", + "oc:tooltip.geolyzer": "Permette la scansione di durezza dei blocchi della zona circostante. Questa informazione può essere utile per generare ologrammi della zona o per rilevare minerali.", + "oc:tooltip.graphicscard": "Usato per cambiare ciò che è visualizzato sullo schermo.\nRisoluzione massima: §f%sx%s§7\nMassima profondità di colore: §f%s§7\nOperazioni/ciclo: §f%s§7", + "oc:tooltip.internetcard": "Questa scheda consente di effettuare richieste HTTP e l'utilizzo di socket TCP reali.", + "oc:tooltip.interweb": "Congratulazioni, hai vinto un (1) interweb. È possibile connettersi ad esso utilizzando una scheda di Internet. Attenzione: non alimentare i troll.", + "oc:tooltip.ironnugget": "Una pepita fatta di ferro, ecco perché si chiama Pepita di Ferro, duh ...", + "oc:tooltip.keyboard": "Può essere attaccato agli schermi per consentire la digitazione su di essi.", + "oc:tooltip.hologram0": "Un display volumetrico che può essere controllato da computer per visualizzare le strutture voxel arbitrarie.\nRisoluzione: §f48x32x48§7\nScala massima: §f3x§7\nProfondità di colore: §fMonocromatico§7", + "oc:tooltip.hologram1": "Un display volumetrico che può essere controllato da computer per visualizzare le strutture voxel arbitrarie.\nRisoluzione: §f48x32x48§7\nScala massima: §f4x§7\nProfondità di colore: §fTricolore§7", + "oc:tooltip.linkedcard": "Questi sono realizzati a coppie, e possono comunicare solo con la loro scheda associata. Tuttavia, essi possono comunicare attraverso qualsiasi distanza, e anche attraverso le dimensioni. L'energia richiesta per inviare un messaggio però è piuttosto alta.", + "oc:tooltip.linkedcard_channel": "§8Canale: %s§7", + "oc:tooltip.materialcosts": "Tieni premuto [§f%s§7] per il costo in materiali.", + "oc:tooltip.materials": "Materiali:", + "oc:tooltip.memory": "Necessaria per far funzionare i computer. Più ne hai, più complessi i programmi che possono essere eseguiti.", + "oc:tooltip.microchip": "Il chip precedentemente noto come circuito integrato. Non ho idea del perché questo funziona con la pietrarossa, ma lo fa.", + "oc:tooltip.motionsensor": "In grado di rilevare il movimento di esseri viventi nelle vicinanze. Richiede linea di vista ininterrotta.", + "oc:tooltip.networkcard": "Consente ai computer remoti connessi da altri blocchi (come i cavi) di comunicare con l'invio di messaggi.", + "oc:tooltip.poweracceptor": "Velocità di conversione di energia: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fFactorization Charge§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Converte l'energia da altri mod nel tipo di energia interna. Tassi di conversione:", + "oc:tooltip.powerdistributor": "Distribuisce l'eenergia tra reti diverse. Questo è utile per la condivisione di energia immessa nel sistema da un convertitore tra le diverse sotto-reti che dovrebbero rimanere separati.", + "oc:tooltip.printedcircuitboard": "L'elemento di base per schede di espansione, per la memoria e così via.", + "oc:tooltip.rawcircuitboard": "Può essere indurito in qualsiasi forno o fornace compatibile.", + "oc:tooltip.redstone": "Permette la lettura e l'emissione di segnali di pietrarossa attorno tutto il blocco. Può essere controllato da qualsiasi computer che è collegato. Questo è fondamentalmente come una scheda di pietrarossa esterna.", + "oc:tooltip.redstonecard.ProjectRed": "§fProjectRed§7 è §asupportato§7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 è §asupportato§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 è §asupportato§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 è §asupportato§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 è §asupportato§7.", + "oc:tooltip.redstonecard": "Permette la lettura e l'emissione di segnali di pietrarossa attorno tutto il computer o robot.", + "oc:tooltip.robot": "A differenza dei computer, i robot possono muoversi e interagire con il mondo, molto simile a come può fare un giocatore. Tuttavia §onon§7 possono interagire con componenti esterni!U", + "oc:tooltip.robot_level": "§fLivelli§7: §a%s§7", + "oc:tooltip.robot_storedenergy": "§fEnergia immagazzinata§7: §a%s§7", + "oc:tooltip.screen": "Mostra del testo, contollato da una scheda grafica contenuta nel case del computer.\nRisoluzione massima: §f%sx%s§7\nProfondità di colore massima: §f%s§7", + "oc:tooltip.server": "Questo è un server, ci sono tanti come lui, ma questo può essere aggiornato con i componenti in modo simile a un case del computer. Può essere avviato inserendolo in un rack di server.", + "oc:tooltip.server.Components": "Componenti installati:", + "oc:tooltip.rack": "Permette l'installazione di un massimo di quattro server. Utilizzare un terminale remoto per accedere ai server contenuti in questo server rack.", + "oc:tooltip.switch": "Consente il collegamento di reti diverse tra loro. Solo i messaggi di rete saranno inoltrati attraverso il blocco, i componenti non saranno invece visibili Utilizzarlo per separare le reti pur consentendo la comunicazione utilizzando schede di rete, per esempio.", + "oc:tooltip.tablet": "Un computer tablet, per del Lua fresco in movimento. Può essere forzato lo spegnimento usandolo mentre si preme maiusc.", + "oc:tooltip.tabletcase": "Case di base per i tablet. Posizionarlo in assembler per aggiungere componenti e creare un tablet PC.", + "oc:tooltip.terminal": "Permette il controllo di un server in remoto, fino a quando si è nella portata di esso. Agisce come uno schermo portatile e una tastiera. Utilizzare i tasti maiusc-destro del mouse su un server in un rack di server per associare il terminale ad esso.", + "oc:tooltip.livello": "§8Livello %s", + "oc:tooltip.toolong": "Tenere premuto [§f%s§7] per un tooltip dettagliato.", + "oc:tooltip.transistor": "Un elemento fondamentale in molte altre parti del computer. È un po' contorto, ma fa il suo lavoro.", + "oc:tooltip.upgradeangel": "Consente ai robot di posizionare i blocchi nel nulla, anche se non vi è alcun blocco di riferimento.", + "oc:tooltip.upgradebattery": "Aumenta la quantità di energia che un robot è in grado di immagazzinare, permettendogli di lavorare più a lungo senza dover essere ricaricato.\ncapacità: §f%s§7", + "oc:tooltip.upgradechunkloader": "Se un robot si muove in una foresta e non c'è nessuno intorno per vederlo, si muove veramente? Questo aggiornamento consente di essere certi che lo faccia. Mantiene il chunk dove si trova il robot caricato, ma consuma continuamente energia, mentre è attivo.", + "oc:tooltip.upgradecontainercard": "Questo aggiornamento consente l'installazione e la rimozione di schede da un robot assemblato dinamicamente.\nLivello massimo: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Questo aggiornamento consente l'installazione e la rimozione di upgrade da un robot assemblato dinamicamente.\nLivello massimo: §f%s§7", + "oc:tooltip.upgradecrafting": "Consente robot per utilizzare l'area in alto a sinistra del loro inventario per il crafting di oggetti. Gli oggetti devono essere allineati come sarebbero in un banco da lavoro.", + "oc:tooltip.upgradeexperience": "Questo aggiornamento consente di robot di accumulare esperienza, quando effettuano diverse operazioni. Maggiore è la loro esperienza, più energia sono in grado di memorizzare, più velocemente possono raccogliere blocchi e più efficientemente utilizzano gli strumenti.", + "oc:tooltip.upgradegenerator": "Può essere usato per generare energia dal combustibile mentre si è in movimento. Brucia oggetti per produrre energia nel corso del tempo, in base al loro valore di carburante.\n§fEfficienza§7: §a%s%%§7", + "oc:tooltip.upgradeinventory": "Questo aggiornamento fornisce spazio nell'inventario al robot. Senza uno di questi, i robot non saranno in grado di immagazzinare gli oggetti internamente.", + "oc:tooltip.upgradeinventorycontroller": "Questo aggiornamento permette al robot più controllo nel modo in cui interagisce con gli inventari esterni, e gli permette di scambiare il suo strumento equipaggiato con uno nel suo inventario.", + "oc:tooltip.upgradenavigation": "Può essere usato per determinare la posizione e l'orientamento del robot. La posizione è relativa al centro della mappa che è stata utilizzata per il crafting tale aggiornamento.", + "oc:tooltip.upgradepiston": "Questo aggiornamentp è molto spinto. Permette di spingere i blocchi, in modo simile ad un pistone. Tuttavia §lnon§7 muove le entità.", + "oc:tooltip.upgradesign": "Consente la lettura del testo e sulla scrittura del testo sui cartelli.", + "oc:tooltip.upgradesolargenerator": "Può essere usato per generare energia dalla luce del sole. Richiede una chiara linea di vista verso il cielo sopra il robot. Genera energia al %s%% della velocità di un motore Stirling.", + "oc:tooltip.upgradetank": "Questo aggiornamento fornisce un serbatoio per lo stoccaggio di liquidi al robot. Senza uno di questi, i robot non saranno in grado di immagazzinare i fluidi internamente.", + "oc:tooltip.upgradetankcontroller": "Questo aggiornamento permette al robot più controllo nel modo in cui interagisce con serbatoi esterni, e permette di trasferire i fluidi dentro e fuori dal serbatoio nel suo inventario.", + "oc:tooltip.upgradetractorbeam": "Dota il robot con tecnologia estremamente avanzata, soprannominato il \"Magnet Item\". Consente a raccogliere oggetti ovunque entro 3 blocchi dalla sua posizione.", + "oc:tooltip.wirelessnetworkcard": "Consente invio di messaggi wireless in aggiunta a quelli normali. È possibile regolare la §fpotenza del segnale§7 per controllare quanto lontano i messaggi vengono inviati. Una più elevata potenza del segnale richiede un maggior consumo energetico." +} diff --git a/src/main/resources/assets/opencomputers/lang/pt_BR.lang b/src/main/resources/assets/opencomputers/lang/pt_BR.lang deleted file mode 100644 index 6ac0a419f5..0000000000 --- a/src/main/resources/assets/opencomputers/lang/pt_BR.lang +++ /dev/null @@ -1,490 +0,0 @@ -# This is the Brazilian portuguese translation based on master english file. -#Author: Hilton W. Silva (hws689) -# Updated by guilherme-puida in 03/01/2021 - -# Blocks -tile.oc.accesspoint.name=§cPonto de Acesso§7 -tile.oc.adapter.name=Adaptador -tile.oc.assembler.name=Montador Eletrônico -tile.oc.cable.name=Cabo -tile.oc.capacitor.name=Capacitador -tile.oc.carpetedCapacitor.name=Capacitador com Tapete -tile.oc.case1.name=Gabinete (Nível 1) -tile.oc.case2.name=Gabinete (Nível 2) -tile.oc.case3.name=Gabinete (Nível 3) -tile.oc.casecreative.name=Gabinete (Criativo) -tile.oc.chameliumblock.name=Bloco Camaleão -tile.oc.charger.name=Carregador -tile.oc.disassembler.name=Desmontador -tile.oc.diskdrive.name=Unidade de Disco -tile.oc.endstone.name=Pedra do Fim -tile.oc.geolyzer.name=Geonalizador -tile.oc.hologram1.name=Projetor Holográfico (Nível 1) -tile.oc.hologram2.name=Projetor Holográfico (Nível 2) -tile.oc.keyboard.name=Teclado -tile.oc.microcontroller.name=Microcontrolador -tile.oc.motionsensor.name=Sensor de Movimento -tile.oc.netsplitter.name=Divisor de Rede -tile.oc.powerconverter.name=Conversor de Energia -tile.oc.powerdistributor.name=Distribuidor de Energia -tile.oc.print.name=Impressão 3D -tile.oc.printer.name=Impressora 3D -tile.oc.raid.name=Raid -tile.oc.redstone.name= E/S de Redstone -tile.oc.relay.name=Relé -tile.oc.robot.name=Robô -tile.oc.robotafterimage.name=Robô -tile.oc.screen1.name=Monitor (Nível 1) -tile.oc.screen2.name=Monitor (Nível 2) -tile.oc.screen3.name=Monitor (Nível 3) -tile.oc.rack.name=Rack -tile.oc.switch.name=§cSwitch§7 -tile.oc.transposer.name=Transpositor -tile.oc.waypoint.name=Ponto de Passagem - -# Items -item.oc.abstractbuscard.name=Placa de Barramento Abstrata -item.oc.acid.name=Grogue -item.oc.alu.name=Unidade Lógica Aritmética (ULA) -item.oc.analyzer.name=Analizador -item.oc.apu0.name=Unidade de Processamento Acelerado (UPA) (Nível 1) -item.oc.apu1.name=Unidade de Processamento Acelerado (UPA) (Nível 2) -item.oc.apu2.name=Unidade de Processamento Acelerado (UPA) (Criativo) -item.oc.arrowkeys.name=Setas do Teclado -item.oc.buttongroup.name=Grupo de Botões -item.oc.cardbase.name=Base de Cartão -item.oc.chamelium.name=Camaleão -item.oc.circuitboard.name=Placa de Circuito -item.oc.componentbus0.name=Componente de Barramento (Nível 1) -item.oc.componentbus1.name=Componente de Barramento (Nível 2) -item.oc.componentbus2.name=Componente de Barramento (Nível 3) -item.oc.componentbus3.name=Componente de Barramento (Criativo) -item.oc.controlunit.name=Unidade de Controle (UC) -item.oc.cpu0.name=Unidade Central de Processamento (CPU) (Nível 1) -item.oc.cpu1.name=Unidade Central de Processamento (CPU) (Nível 2) -item.oc.cpu2.name=Unidade Central de Processamento (CPU) (Nível 3) -item.oc.cuttingwire.name=Fio de Corte -item.oc.datacard0.name=Cartão de Dados (Nível 1) -item.oc.datacard1.name=Cartão de Dados (Nível 2) -item.oc.datacard2.name=Cartão de Dados (Nível 3) -item.oc.debugcard.name=Cartão de Depuração -item.oc.debugger.name=Depurador de Rede -item.oc.diamondchip.name=Chip de Diamante -item.oc.disk.name=Prato do Disco -item.oc.diskdrivemountable.name=Unidade de Disco -item.oc.drone.name=Drone -item.oc.dronecase0.name=Carcaça de Drone (Nível 1) -item.oc.dronecase1.name=Carcaça de Drone (Nível 2) -item.oc.dronecase3.name=Carcaça de Drone (Criativo) -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=Disquete -item.oc.graphicscard0.name=Placa de Vídeo (Nível 1) -item.oc.graphicscard1.name=Placa de Vídeo (Nível 2) -item.oc.graphicscard2.name=Placa de Vídeo (Nível 3) -item.oc.harddiskdrive0.name=Disco Rígido (Nível 1) -item.oc.harddiskdrive1.name=Disco Rígido (Nível 2) -item.oc.harddiskdrive2.name=Disco Rígido (Nível 3) -item.oc.hoverboots.name=Botas Voadoras -item.oc.inkcartridge.name=Cartucho de Tinta -item.oc.inkcartridgeempty.name=Cartucho de Tinta (Vazio) -item.oc.internetcard.name=Cartão de Internet -item.oc.interweb.name=Interweb -item.oc.ironnugget.name=Pepita de Ferro -item.oc.linkedcard.name=Cartão Vinculado -item.oc.manual.name=Manual do OpenComputers -item.oc.memory0.name=Memória (Nível 1) -item.oc.memory1.name=Memória (Nível 1.5) -item.oc.memory2.name=Memória (Nível 2) -item.oc.memory3.name=Memória (Nível 2.5) -item.oc.memory4.name=Memória (Nível 3) -item.oc.memory5.name=Memória (Nível 3.5) -item.oc.microchip0.name=Circuito Integrado (Nível 1) -item.oc.microchip1.name=Circuito Integrado (Nível 2) -item.oc.microchip2.name=Circuito Integrado (Nível 3) -item.oc.microcontrollercase0.name=Carcaça do Microcontrolador (Nível 1) -item.oc.microcontrollercase1.name=Carcaça do Microcontrolador (Nível 2) -item.oc.microcontrollercase3.name=Carcaça do Microcontrolador (Criativo) -item.oc.nanomachines.name=Nanomáquinas -item.oc.networkcard.name=Cartão de Rede -item.oc.numpad.name=Teclado Numérico -item.oc.present.name=Uma coisinha... -item.oc.printedcircuitboard.name=Placa de Circuito Impresso (PCB) -item.oc.rawcircuitboard.name=Placa de Circuito Crua -item.oc.redstonecard0.name=Cartão de Redstone (Nível 1) -item.oc.redstonecard1.name=Cartão de Redstone (Nível 2) -item.oc.server0.name=Servidor (Nível 1) -item.oc.server1.name=Servidor (Nível 2) -item.oc.server2.name=Servidor (Nível 3) -item.oc.server3.name=Servidor (Criativo) -item.oc.tablet.name=Tablet -item.oc.tabletcase0.name=Carcaça de Tablet (Nível 1) -item.oc.tabletcase1.name=Carcaça de Tablet (Nível 2) -item.oc.tabletcase3.name=Carcaça de Tablet (Criativo) -item.oc.terminal.name=Terminal Remoto -item.oc.terminalserver.name=Servidor de Terminal -item.oc.texturepicker.name=Selecionador de Textura -item.oc.transistor.name=Transistor -item.oc.upgradeangel.name=Aprimoramento Angelical -item.oc.upgradebattery0.name=Aprimoramento de Bateria (Nível 1) -item.oc.upgradebattery1.name=Aprimoramento de Bateria (Nível 2) -item.oc.upgradebattery2.name=Aprimoramento de Bateria (Nível 3) -item.oc.upgradechunkloader.name=Aprimoramento de Chunkloader -item.oc.upgradecontainercard0.name=Contêiner de Cartão (Nível 1) -item.oc.upgradecontainercard1.name=Contêiner de Cartão (Nível 2) -item.oc.upgradecontainercard2.name=Contêiner de Cartão (Nível 3) -item.oc.upgradecontainerupgrade0.name=Aprimoramento de Contêiner (Nível 1) -item.oc.upgradecontainerupgrade1.name=Aprimoramento de Contêiner (Nível 2) -item.oc.upgradecontainerupgrade2.name=Aprimoramento de Contêiner (Nível 3) -item.oc.upgradecrafting.name=Aprimoramento de Construção -item.oc.upgradedatabase0.name=Aprimoramento de Banco de Dados (Nível 1) -item.oc.upgradedatabase1.name=Aprimoramento de Banco de Dados (Nível 2) -item.oc.upgradedatabase2.name=Aprimoramento de Banco de Dados (Nível 3) -item.oc.upgradeexperience.name=Aprimoramento de Experiência -item.oc.upgradegenerator.name=Aprimoramento de Gerador -item.oc.upgradehover0.name=Aprimoramento de Flutuação (Nível 1) -item.oc.upgradehover1.name=Aprimoramento de Flutuação (Nível 2) -item.oc.upgradeinventory.name=Aprimoramento de Inventário -item.oc.upgradeinventorycontroller.name=Aprimoramento de Controlador de Inventário -item.oc.upgradeleash.name=Aprimoramento de Correia -item.oc.upgradeMF.name=MFU -item.oc.upgradenavigation.name=Aprimoramento de Navegação -item.oc.upgradepiston.name=Aprimoramento de Pistão -item.oc.upgradesign.name=Aprimoramento de Sinal de E/S -item.oc.upgradesolargenerator.name=Aprimoramento de Gerador Solar -item.oc.upgradetank.name=Aprimoramento de Tanque -item.oc.upgradetankcontroller.name=Aprimoramento de Controlador de Tanque -item.oc.upgradetractorbeam.name=Aprimoramento de Raio Trator -item.oc.upgradetrading.name=Aprimoramento de Troca -item.oc.wirelessnetworkcard0.name=Cartão de Rede Sem Fio (Nível 1) -item.oc.wirelessnetworkcard1.name=Cartão de Rede Sem Fio (Nível 2) -item.oc.worldsensorcard.name=Cartão de Sensor de Mundo -item.oc.wrench.name=Chave de fenda-boca - -# Entities -entity.oc.Drone.name=Drone - -# GUI -oc:gui.Analyzer.Address=§6Endereço§f: %s -oc:gui.Analyzer.AddressCopied=Endereço copiado para a área de transferência. -oc:gui.Analyzer.ChargerSpeed=§6Velocidade da carga§f: %s -oc:gui.Analyzer.ComponentName=§6Nome do componente§f: %s -oc:gui.Analyzer.Components=§6Número de componentes conectados§f: %s -oc:gui.Analyzer.CopyToClipboard=Clique para copiar para a área de transferência. -oc:gui.Analyzer.LastError=§6Último erro§f: %s -oc:gui.Analyzer.RobotName=§6Nome§f: %s -oc:gui.Analyzer.RobotOwner=§6Dono§f: %s -oc:gui.Analyzer.RobotXp=§6Experiência§f: %s (Nível %s) -oc:gui.Analyzer.StoredEnergy=§6Energia armazenada§f: %s -oc:gui.Analyzer.TotalEnergy=§6Total de energia armazenada§f: %s -oc:gui.Analyzer.Users=§6Usuários§f: %s -oc:gui.Analyzer.WirelessStrength=§6Força do sinal§f: %s -oc:gui.Assembler.Collect=Saída do coletador -oc:gui.Assembler.Complexity=Complexidade: %s/%s -oc:gui.Assembler.InsertCase=Insira uma parte base -oc:gui.Assembler.InsertCPU=Insira uma CPU -oc:gui.Assembler.InsertRAM=Insira alguma RAM -oc:gui.Assembler.Progress=Progresso: %s%% (%s) -oc:gui.Assembler.Run=Montar -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Placa de Vídeo -oc:gui.Assembler.Warning.Inventory=Aprimoramento de Inventário -oc:gui.Assembler.Warning.Keyboard=Teclado -oc:gui.Assembler.Warning.OS=Mídia Inicializável -oc:gui.Assembler.Warning.Screen=Monitor -oc:gui.Assembler.Warnings=§eAtenção§7: Componentes recomendados ausentes. -oc:gui.Chat.NewVersion=Uma nova versão está disponível: %s -oc:gui.Chat.TextureName=§7Nome da textura é §a%s§f. -oc:gui.Chat.WarningClassTransformer=Houve um erro §cerros§f ao rodar o 'class transformer'. Por favor relate isso, junto com o seu arquivo de log (completo!) FML §alatest.log§f/§afml-server-latest.log§f , obrigado! -oc:gui.Chat.WarningFingerprint=§cATENÇÃO§f - impressão digital não reconhecida! Experado '§a%s§f' mas recebido '§e%s§f'. A não ser que você seja um desenvolvedor e esteja rodando uma versão desofuscada, é §lextremamente§f recomendável que baixe novamente o OpenComputers, por que o JAR que está usando talvez esteja adulterado. -oc:gui.Chat.WarningLink=Não foi possível abrir o atalho: %s -oc:gui.Chat.WarningLuaFallback=Bibliotecas nativas do Lua não disponíveis, computadores não serão capazes de persistir seus estados. Eles reinicializarão quando chunks descarregarem. -oc:gui.Chat.WarningProjectRed=Você está usando uma versão do Project: Red que é incompativel com OpenComputers. Tente atualizar sua versão do Project: Red. -oc:gui.Chat.WarningRecipes=Houve erros ao carregar uma ou mais receitas. Alguns itens não poderão ser construídos. Verifique seu arquivo de log para mais informações. -oc:gui.Chat.WarningSimpleComponent=Um addon (seu?) usa a interface §aSimpleComponent§f que faz §ealgo errado§f. A lógica de componente não pode ser injetada. Por favor verifique seu arquivo de log para mais informações. -oc:gui.Drive.Managed=Gerenciado -oc:gui.Drive.Unmanaged=Não Gerenciado -oc:gui.Drive.ReadOnlyLock=Bloqueado -oc:gui.Drive.ReadOnlyLockWarning=§Somente leitura§r. Isso não pode ser removido, exceto quando o disco é formatado. -oc:gui.Drive.Warning=§lAtençaõ§r: mudança de modos resultará em perca de todos os dados do disco! -oc:gui.Error.ComponentOverflow=Muitos componentes conectados ao computador. -oc:gui.Error.InternalError=Erro interno, por favor veja o arquivo de log. Isto é provavelmente um erro. -oc:gui.Error.NoCPU=Não há CPU instalada no computador. -oc:gui.Error.NoEnergy=Energia insuficiente. -oc:gui.Error.NoRAM=Não há RAM instalada no computador. -oc:gui.Error.OutOfMemory=Sem memória. -oc:gui.Manual.Blocks=Blocos do OpenComputers -oc:gui.Manual.Home=Início -oc:gui.Manual.Items=Itens do OpenComputers -oc:gui.Manual.Warning.BlockMissing=Bloco indisponível. -oc:gui.Manual.Warning.ImageMissing=Imagem não encontrada. -oc:gui.Manual.Warning.ItemMissing=Item indisponível. -oc:gui.Manual.Warning.OreDictMissing=Entrada no dicionário de minérios indisponível. -oc:gui.Raid.Warning=§4Adicionar um disco o formata.[nl] Remover um disco limpa a raid. -oc:gui.Robot.Power=Energia -oc:gui.Robot.TurnOff=Desligar -oc:gui.Robot.TurnOn=Ligar[nl]§7Use um Analizador para verficar problemas.§r -oc:gui.Rack.Back=Voltar -oc:gui.Rack.Bottom=Baixo -oc:gui.Rack.Left=Esquerda -oc:gui.Rack.None=Nenhum -oc:gui.Rack.Right=Direita -oc:gui.Rack.Enabled=Habilitado -oc:gui.Rack.Disabled=Disabilitado -oc:gui.Rack.RelayModeTooltip=Modo de relé -oc:gui.Rack.Top=Cima -oc:gui.Switch.PacketsPerCycle=Pacotes / ciclo -oc:gui.Switch.QueueSize=Tamanho da fila -oc:gui.Switch.TransferRate=Taxa do ciclo -oc:gui.Terminal.InvalidKey=Chave inválida, provavelmente outro terminal foi conectado ao servidor. -oc:gui.Terminal.OutOfRange=Sem sinal. - -# Containers -oc:container.accesspoint=Ponto de Acesso -oc:container.adapter=Adaptador -oc:container.case=Computador -oc:container.charger=Carregador -oc:container.disassembler=Desmontador -oc:container.diskdrive=Unidade de disco -oc:container.printer=Impressora -oc:container.raid=Raid -oc:container.relay=Relé -oc:container.server=Servidor -oc:container.rack=Rack -oc:container.switch=Switch -oc:container.tabletwrapper=Tablet - -# Keybinds -key.clipboardPaste=Colar da área de transferência -key.materialCosts=Mostrar custo dos materiais - -# Item / Block Tooltips -oc:tooltip.accesspoint=Atua como um Switch, mas adicionalmente recebe pacotes sem fio e retransmite pacotes com fio sem fio. -oc:tooltip.abstractbuscard=Permite interagir com o barramento abstrato de §fStargateTech 2§7 enviando e recebendo pacotes LIP. -oc:tooltip.acid=Um pseudo-liquido altamente tóxico, usualmente consumidos por alguns piratas (ihic!). Também, todavia, pode ser útil de outros modos. -oc:tooltip.adapter=Usado para controlar blocos que não são componentes, tal como blocos vanillas ou de outros mods. -oc:tooltip.alu=Adiciona números que você não têm. Bem melhor assim. -oc:tooltip.analyzer=Usado para mostrar informações sobre os blocos, tal como seu §fendereço§7 e §fnome do componente§7.[nl] Também mostra o erro que causou um computador travar se não foi desligado normalmente. -oc:tooltip.apu=Isso é um CPU com um GPU integrado (ou IGP), quando você apenas precisa deste slot de placa .[nl] Componentes Suportados: §f%s§7[nl] Resolução mãxima: §f%sx%s§7[nl] Densidade de cores máxima: §f%s§7[nl] Operações/tick: §f%s§7 -oc:tooltip.assembler=Permite construir robôs e outros dispositivos com várias partes de computador. -oc:tooltip.cable=Uma maneira barata de conectar os blocos. -oc:tooltip.capacitor=Armazena energia para usar mais tarde. Pode ser enchido e esvaziado rapidamente. -oc:tooltip.cardbase=Como o nome diz, este é o bloco de construção básico para os cartões de expansão. -oc:tooltip.carpetedcapacitor=Armazena energia para uso posterior. Pode ser carregado e descarregado rapidamente. Carrega quando uma ovelha ou jaguatirica anda sobre o mesmo. -oc:tooltip.case=O gabinete é o bloco básico para computadores e guarda os §fcartões de extensão§7, §fRAM§7 e §fdiscos rígidos§7.[nl] Slots: §f%s§7 -oc:tooltip.chamelium=Matéria prima para as imressões 3D. Não ingerir: talvez leve a cegueira e um lapso temporário de presença. -oc:tooltip.chameliumblock=Agradável e Limpo. Usado para formas coloridas nas impressões 3D, ou para simplesmente ter, um bloco colorido para decorar sua linda e chamativa base. -oc:tooltip.charger=Transfere energia dos capacitores para robôs e drones adjacentes. A taxa de energia depende do §fsinal de redstone§7 vindo, onde sem sinal não carrega os dispositivos, e quanto mais forte o sinal maior será a velocidade de carregamento. Pode ser usado também para carregar tablets e unidades de disco de acesso em tablets. -oc:tooltip.circuitboard=Agora estamos chegando em algum lugar. Pode ser cozido para obter uma placa de circuito impresso. -oc:tooltip.controlunit=Está é a unidade que... controla... coisas. Você precisa disso para construir a CPU. Então é tipo, totalmente importante. -oc:tooltip.componentbus=Está expansão permite aos servidores comunicarem com mais componentes ao mesmo tempo, similar como os CPUs fazem.[nl] Componentes Suportados: §f%s§7 -oc:tooltip.cpu=Um componente essencial para todos os computadores. O clock (relógio/taxa do processamento) é um tanto quanto que duvidoso, mas o que você espera se ele roda num relógio de sol de bolso?[nl] Componentes Suportados: §f%s§7 -oc:tooltip.cpu.Architecture=Arquitetura: §f%s§7 -oc:tooltip.cuttingwire=Usado para cortar blocos de argila em formato de placas de circuito. Quebra quando usa, o que provavelmente é a pior ferramenta de todas. -oc:tooltip.datacard0=Provê um conjuto de algoritmos tal como hash e desinflar/inflar. -oc:tooltip.datacard1=Provê um conjuto de algoritmos tal como hash, encriptação AES e desinflar/inflar. -oc:tooltip.datacard2=Provê um conjuto de algoritmos tal como hash, encriptação AES, criptografia de curva elíptica e desinflar/inflar. -oc:tooltip.debugcard=Item do modo criativo, permite manipular o mundo para fazer testes facieis. Use por sua conta e risco. -oc:tooltip.debugger=Pode ser usado para depurar informações na grade de rede interna do OC. Somente use se for instruído por um desenvolvedor. -oc:tooltip.diamondchip=Um pedacinho de um diamante uma vez impecável. Ele nunca será o mesmo de novo. -oc:tooltip.disassembler=Separa os itens em seus componentes originais. §lAtenção§7: o item retornado tem uma chance de %s%% de quebrar no processo! -oc:tooltip.disk=Uma mídia primitiva que pode ser usada para construir dispositvos de armazenamentos contínuos. -oc:tooltip.diskdrive.CC=Disquetes do ComputerCraft são §asuportados§7. -oc:tooltip.diskdrive=Permite escrever e ler disquetes. Pode ser instalado em robôs para permitir inserir disquetes mais tarde. -oc:tooltip.diskdrivemountable=Provê a mesma funcionalidade de uma unidade de disco normal, mas só pode ser instalado em um rack. -oc:tooltip.diskusage=Uso de disco: %s/%s Byte -oc:tooltip.disklocked=Bloqueado por: %s -oc:tooltip.diskmodemanaged=Modo: Gerenciado -oc:tooltip.diskmodeunmanaged=Modo: Não gerenciado -oc:tooltip.drone=Drones são leves, com reconhecimento rápido de unidades com espaço interno limitado. -oc:tooltip.dronecase=Está carcaça é usada para montar drones no montador. Ele tem um espaço para uma pequena quantidade de componentes e provê levitação com a pedra do fim. -oc:tooltip.eeprom=Pequeno armazenamento programável que contem a BIOS dos computadores usado para iniciá-los. -oc:tooltip.fakeendstone=Quase igual a coisa real, emula até mesmo sua levitação! -oc:tooltip.geolyzer=Permite escanear a dureza da área ao redor de blocos sólidos. Essa informação pode ser útil para geração de hologramas de área ou para detectar minérios. -oc:tooltip.graphicscard=Usado para mudar o que é mostrado nos monitores.[nl] Máxima resolução: §f%sx%s§7[nl] Máxima profundidade de cores: §f%s§7[nl] Operações/tick: §f%s§7 -oc:tooltip.hoverboots=Pule alto, caia devagar, caminhe melhor. Isso e mais, com as mais novas e patenteadas Botas Voadoras (TM). -oc:tooltip.inkcartridge=Usado para carregar tinta nas impressoras 3D. Por razões misteriosas ele não tem que permanecer na impressora. -oc:tooltip.inkcartridgeempty=Este cartucho de tinta foi usado até secar. Recarregue isso usando corantes. Ou jogue-o fora. Veja minha cara de preocupado -.-. -oc:tooltip.internetcard=Este cartão faz requisições HTTP usa protocolos TCP reais. -oc:tooltip.interweb=Parabéns, você ganhou um (1) interweb. Você pode conectar a isso usando um Cartão de Internet. Cuidado: não alimente os trolls. -oc:tooltip.ironnugget=Uma pepita feita de ferro, por isso é chamado de Pepita de Ferro, darrr... -oc:tooltip.keyboard=Pode ser anexados aos monitores para permitir digitar neles. -oc:tooltip.hologram0=Uma exibição volumétrica que pode ser controlado por computadores para exibir estruturas de voxel arbitrárias.[nl] Resolução: §f48x32x48§7 [nl] Escala máxima: §f3x§7 [nl] Profundidade de cores: §fMonocromatico§7 -oc:tooltip.hologram1=Uma exibição volumétrica que pode ser controlado por computadores para exibir estruturas de voxel arbitrárias.[nl] Resolução: §f48x32x48§7 [nl] Escala máxima: §f4x§7 [nl] Profundidade de cores: §fTricolor§7 -oc:tooltip.linkedcard=Eles são construidos em pares, e só pode comunicar-se com seu par. Entretanto, eles podem se comunicar em qualquer distância, e também cruzar dimensões. A energia requerida para enviar a mensagem é altíssima. -oc:tooltip.linkedcard_channel=§8Canal: %s§7 -oc:tooltip.manual=Toda informaçâo que você provalvemente precisará do OpenComputers. E mais. Por um preço inacreditável de... §opor favor pressiona R para continuar§7. -oc:tooltip.materialcosts=Segure [§f%s§7] para custos do material. -oc:tooltip.materials=Materiais: -oc:tooltip.memory=Requerida para fazer os computadores funcionarem. Quanto mais tiver, mais complexos podem ser os programas. -oc:tooltip.microchip=Um chip conhecido formalmente como Circuito Integrado. Eu não tenho ideia porque isso funciona com redstone, mas funciona. -oc:tooltip.microcontroller=Microcontroladores são mini computadores. Eles são usadas para cuidar de tarefas específicas, rodando um único programa que é provido pela EEPROM consruida dentro dele.[nl] §cNão pode conectar com componentes externos.§7 -oc:tooltip.microcontrollercase=Componente básico para construir microcontroladores. Coloque-o em um montador para adicionar novos componentes e montar um microcontrolador. -oc:tooltip.motionsensor=Pode detectar movimento de formas de vidas próximas. Requer visão direta. -oc:tooltip.nanomachines=Unidade de controle e um grupo de nanomáquinas pra ingerir, se você tiver coragem. -oc:tooltip.networkcard=Permite que computadores distantes conectem por outros blocos (como cabos) para comunicar-se enviando mensagens um para o outro. -oc:tooltip.poweracceptor=Velocidade da conversão de energia: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fCarregamento do Factorization§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Converte energia de outros mods para o tipo de energia interna. Taxa de conversão: -oc:tooltip.powerdistributor=Distribui energia por meio de diferentes redes. Isto é útil para compartilhar fluxo de energia de seu sistema para outro conversor em diferentes sub-redes que podem manter separados. -oc:tooltip.present=... para seus problemas. Abra este presente para ter uma chance de receber §kum grande tesouro§7![nl]§8Construa itens do OpenComputers para ter uma chance de receber presente.§7 -oc:tooltip.print.BeaconBase=§8Funciona como uma base do sinalizador. -oc:tooltip.print.LightValue=§8Luz emitida: %s. -oc:tooltip.print.RedstoneLevel=§8Saída da Redstone: %s. -oc:tooltip.printedcircuitboard=O bloco de construção básico para cartões de expansão, memória e outros. -oc:tooltip.printer=Permite imprimir blocos com forma definida pelo usuário usando o Camaleão (nome lindo?) e Cartucho de Tinta. Deve ser configurado usando um computador. Deixe longe do alcance das crianças. Porque sim. -oc:tooltip.raid=Permite cobinar três discos rigídos em um único grande arquivo de sistema que pode ser usado por todos os computadores conectados. -oc:tooltip.rawcircuitboard=Pode ser endurecido em qualquer fornalha compátivel. -oc:tooltip.redstone=Permite ler e emitir sinais de redstone ao redor do bloco. Pode ser controlado por qualquer computador que o bloco estiver conectado. Isto é básicamente um cartão de redstone externo. -oc:tooltip.redstonecard.ProjectRed=§fProjectRed§7 é §asuportado§7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 é §asuportado§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 é §asuportado§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 é §asuportado§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 é §asuportado§7. -oc:tooltip.redstonecard=Permite ler e emitir sinais de redstone ao redor do robô computadorizado. -oc:tooltip.relay=Permite conectar diversas redes uma com a outra. Somente mensagens de rede serão passadas, componentes não serão vísiveis através disto. Use para separar redes e continuar permitindo comunicação usando Cartões de Rede, por exemplo. -oc:tooltip.robot=Diferente de computadores, robôs podem mover-se por ai e interagir com o mundo parecido com o que um jogador faz.[nl] §cNão pode conectar-se com componentes externos.§7 -# The underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fNível§7: §a%s§7 -oc:tooltip.robot_storedenergy=§fEnergia armazenada§7: §a%s§7 -oc:tooltip.screen=Mostra texto, controlado por uma placa de vídeo em um Gabinete.[nl] Resolução máxima: §f%sx%s§7[nl] Densidade máxima: §f%s§7 -oc:tooltip.server=Isto é um servidor: existem muitos como este, mas este pode ser aprimorado com componentes como um gabinete pode ser. Ele pode ser ligado inserindo-o em um rack de servidor. -oc:tooltip.server.Components=Componentes instalados: -oc:tooltip.rack=Permite a instalação de até quatro servidores ou outros montáveis. -oc:tooltip.switch=Permite conectar várias redes uma com a outra. Somente mensagens de rede serão transmitidas, componentes não serão vísiveis. Use-o para separar redes permitindo comunicação usando Cartões de Rede, por exemplo. -oc:tooltip.tablet=Um tablet, para usar Lua de maneira portátil. Pode ser forçado a desligar se abaixando e ativando-o. -oc:tooltip.tabletcase=Carcaça básica para tablets. Coloque-o no montador para adicionar componentes e criar um tablet. -oc:tooltip.terminal=Permite controlar um servidor remotamente, enquanto estiver no seu alcance. Atua como monitor e teclados portateis. Shift + Botão Direito em um servidor em um rack para vincular um terminal. -oc:tooltip.terminalserver=Backend para que Terminais remotos possam ser conectados para prover controle remoto. Contém um monitor virtual e um teclado. -oc:tooltip.texturepicker=Esta ferramente mostra uma string descrevendo uma superfície de bloco, para usar na impressora 3D e definir formas. Não são nomes de texturas, nana nina não. -oc:tooltip.tier=§8Nível %s -oc:tooltip.netsplitter=Atua como um conector dinâmico. Conectividade de cada lado pode ser mudada batendo-a com uma chave. Conectividade de todos os lados podem ser invertidas aplicando um sinal de redstone. -oc:tooltip.toolong=Segure [§f%s§7] para uma dica detalhada. -oc:tooltip.transistor=Um elemento básico para outras partes de computador. É um pouco torto, mas faz seu trabalho. -oc:tooltip.transposer=Permite transferir automaticamente itens e fluidos entre inventários adjacentes e recipientes de fluidos. -oc:tooltip.upgradeangel=Permite que robôs coloquem blocos no ar, mesmo sem um ponto de apoio. -oc:tooltip.upgradebattery=Aumenta a quantidade de energia que o dispositivo pode armazenar, permitindo que trabalhe mais tempo sem precisa recarregar. [nl] Capacidade: §f%s§7 -oc:tooltip.upgradechunkloader=Se um robô move-se em uma floresta e ninguém é capaz de ver ele, ele está se mexendo ou não? Este aprimoramento faz ter certeza que sim. Isto mantêm um chunk em que um dispositvo está carregado, mas consome energia continuamente enquanto ativo. -oc:tooltip.upgradecontainercard=Este aprimoramento de contêiner permite dinamicamente instalar e remover um cartão de um dispositivo montado. [nl] Nível máximo: §f%s§7 -oc:tooltip.upgradecontainerupgrade=Este aprimoramento de contêiner permite dinamicamente instalar e remover outro aprimoramento de um dispositivo montado. [nl] Nível máximo: §f%s§7 -oc:tooltip.upgradecrafting=Permite que robôs usem o canto superior esquerdo de seu inventário para construir objetos. Os itens devem estar posicionados como se estivessem numa mesa de trabalho. -oc:tooltip.upgradedatabase=Este aprimoramento permite armazenar informação de pilha de itens para ser usado por outros componentes.[nl] Entradas suportadas: §f%s§7 -oc:tooltip.upgradeexperience=Este aprimoramento permite que robôs acumulem experiência efetuando diversas operações. Quanto mais experiência estiver, mais energia pode armazenar, mais rápido pode cortar blocos e mais efeciente ele usa as ferramentas. -oc:tooltip.upgradegenerator=Pode ser usado para gerar combustível em movimento. Queima itens para gerar energia o tempo todo, baseado em seus valores de combustível.[nl] §fEfeciencia§7: §a%s%%§7 -oc:tooltip.upgradehover=Este aprimoramento permite que robôs voem acima do solo sem precisar escalar paredes.[nl] Altura máxima: §f%s§7 -oc:tooltip.upgradeinventory=Este aprimoramento provê espaço no inventário para robôs ou drones. Sem um destes, eles não serão capazes de armazenar itens internamente. -oc:tooltip.upgradeinventorycontroller=Este aprimoramento permite que robôs e drones tenham mais controle de como eles interagem com o inventário externo, e permite que robôs troquem sua ferramenta equipada com algum item do seu inventário. -oc:tooltip.upgrademf=Permite que adaptadores acessem blocos não adjacentes. -oc:tooltip.upgrademf.Linked=§fConexão estabelecida§7 -oc:tooltip.upgrademf.Unlinked=§fSem conexão§7 -oc:tooltip.upgradeleash=Permite que alguns dispostivos, como drones, coloquem animais em coleiras. MUITOSSSS ANIMAIIIIIIss. -oc:tooltip.upgradenavigation=Pode ser usado para determinar a posição e orientação de um dispositivo. A posição é relativa ao centro do mapa que foi usado para construir este aprimoramento. -oc:tooltip.upgradepiston=Este aprimoramento tem um movimento agressivo. Permite mexer blocos, como se estivesse usando um pistão. Isto §lnão§7 move entidades. -oc:tooltip.upgradesign=Permite ler e escrever em placas de texto. -oc:tooltip.upgradesolargenerator=Pode ser usado para gerar energia solar em movimento. Requer uma linha de visão limpa do céu acima do dispositivo. Gera energia em %s%% da velocidade de um Motor Stirling. -oc:tooltip.upgradetank=Este aprimoramento provê um armazenamento de tanque ou fluidos para robôs e drones. Sem um destes, ele não será capaz de armazenar fluidos internamente. -oc:tooltip.upgradetankcontroller=Este aprimoramento permite que robôs e drones tenham mais controle de como eles interagem com tanques externos, e permite que ele transfira fluidos para os tanques e dos tanques. -oc:tooltip.upgradetractorbeam=Equipa um dispositivo com uma tecnologia extremamente avançada, codenome "Imã". Permite que dispositivos capturem blocos com o raio de 3 blocos de sua localização. -oc:tooltip.upgradetrading=Permite que robôs e drones façam trocas com Aldeões. -oc:tooltip.waypoint=Provê um ponto de referência para os dispositvos com um aprimoramento de navegação. -oc:tooltip.wirelessnetworkcard=Permite enviar mensagens de rede além do meio normal. Você pode ajustar a §fforça do sinal§7 para controlar o quão longe as mensagens vão. Altos níveis de força de sinal resulta em alto consumo de energia. -oc:tooltip.worldsensorcard=Permite ler informações sobre o mundo, como gravidade e/ou se tem uma atmosfera respirável. Use os resultados por sua conta e risco. O fabricando não se responsabiliza por danos corporais ou materias por decisões causadas sobre as saídas dos cartões. Nós temos advogados. E dinheiro. Então nem tente. -oc:tooltip.wrench=Um híbrido entre uma Chave de fenda e uma Chave de boca, esta ferramenta é fácil de usar, mas díficil de dominar. - -#Achievements -achievement.oc.adapter=Ai que delicia cara. -achievement.oc.adapter.desc=Interagiu com blocos de outros mods e também do Minecraft vanilla! -achievement.oc.assembler=Explêndido -achievement.oc.assembler.desc=Hora de dominar o mundo! -achievement.oc.cable=Não é um Fio Sujo -achievement.oc.cable.desc=Com a tecnologia patenteada de anti-espaguete. -achievement.oc.capacitor=Baterias inclusas -achievement.oc.capacitor.desc=Você não pode parar isso. -achievement.oc.card=Aceitamos Cartões -achievement.oc.card.desc=Para sua conveniência. Sem segundas intenções, prometo. -achievement.oc.case=Em Caso de Problemas -achievement.oc.case.desc=Porque torres quadradas são as melhores. -achievement.oc.charger=Tá bom, vamo lá -achievement.oc.charger.desc=Carrreeegaaaan- poxa, esqueci do sinal de redstone de novo. -achievement.oc.chip=Todas as Pequenas Coisas -achievement.oc.chip.desc=Porque tubos a vácuo são coisa do passado. -achievement.oc.cpu=Overclocked -achievement.oc.cpu.desc=Hora de fazer uso destes ciclos computacionais. -achievement.oc.disassembler=Vamo quebra tudo! -achievement.oc.disassembler.desc=Caso suas ideias brilhantes não sejam tão brilhantes assim. -achievement.oc.diskDrive=Roda a Roda. -achievement.oc.diskDrive.desc=Capacidade inferior mas um som lindíssimo. -achievement.oc.drone=Voando alto. -achievement.oc.drone.desc=Mantenha a calma e jogue-o para fora do planeta. -achievement.oc.eeprom=Só pode ter um -achievement.oc.eeprom.desc=Por computador, claro. Para manter a ordem da inicialização, entendeu? -achievement.oc.floppy=Um ri- disco -achievement.oc.floppy.desc=Velho, mas ainda funciona. -achievement.oc.geolyzer=Pra dentro da Terra -achievement.oc.geolyzer.desc=Têm qualidades extraordinárias. -achievement.oc.graphicsCard=Ultima Geração -achievement.oc.graphicsCard.desc=Assim pode ser ... ann... rendereziado. Ah sim sim. -achievement.oc.hdd=HDD Melhor que HD -achievement.oc.hdd.desc=Não espere, não é isso que significa. Bom, você entendeu... -achievement.oc.hologram=Próxima Dimensão -achievement.oc.hologram.desc=Porque 2D é chato. Ou não é? -achievement.oc.keyboard=PegadorDeSujeira3000 -achievement.oc.keyboard.desc=É altamente recomendável resistir o impulso de girar eles no ar. -achievement.oc.microcontroller=Irmanzinha -achievement.oc.microcontroller.desc=O irmãozinho do computador. -achievement.oc.motionSensor=Mexe ae que quero vê -achievement.oc.motionSensor.desc=Igual a Steve Swagger. -achievement.oc.networkCard=Agora Estamos Falando! -achievement.oc.networkCard.desc=Mantenha contato com esses parentes distantes. -achievement.oc.openOS=Buti -achievement.oc.openOS.desc=Um SO para - espere, já usei essa antes? Droga. -achievement.oc.powerDistributor=Compartilhar é Amar -achievement.oc.powerDistributor.desc=Quando você precisa de ajuda para equilibrar toda essa energia. -achievement.oc.rack=Mas que potência -achievement.oc.rack.desc=Sim, os racks de servidores são potentes. -achievement.oc.raid=LFG -achievement.oc.raid.desc=Heroíco valeuflws. -achievement.oc.ram=Random Access Memories (Memórias de Acesso Aleatório) -achievement.oc.ram.desc=Parabéns, Você está fazendo isso certo. -achievement.oc.redstoneCard=Con... tato -achievement.oc.redstoneCard.desc=Hora de ser análogo. -achievement.oc.redstoneIO=Hello from the outside... -achievement.oc.redstoneIO.desc=Levando os sinais de redstone para onde quiser. -achievement.oc.robot=Bip Bip -achievement.oc.robot.desc=EXTERMINAR! -achievement.oc.screen=Tentou desligar e ligar de novo? -achievement.oc.screen.desc=Não, de verdade. Um pulso de redstone pode alternar o monitor entre ligado e desligado. -achievement.oc.server=Dedicado -achievement.oc.server.desc=Servidores na nuvem, aqui vamos nós. -achievement.oc.switch=Topologia Complexa -achievement.oc.switch.desc=Evite embalagens baratas devido a possibilidade de pacotes perdidos. -achievement.oc.tablet=Não Engula -achievement.oc.tablet.desc=Mantenha longe do alcance das crianças para evitar cobranças exageradas no seu cartão de crédito. -achievement.oc.transistor=Onipresente -achievement.oc.transistor.desc=Crie um Transistor para começar. -achievement.oc.wirelessNetworkCard=Sinais -achievement.oc.wirelessNetworkCard.desc=Hora de ir onde nenhum pacote foi antes. - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s foi muito ganancioso. -death.attack.oc.nanomachinesOverload.2=%s teve um ataque de nervos. -death.attack.oc.nanomachinesOverload.3=As nanomáquinas de %s estão fora de controle. -death.attack.oc.nanomachinesHungry.1=%s foi comido por nanomáquinas. -death.attack.oc.nanomachinesHungry.2=%s não alimentou suas nanomáquinas. -death.attack.oc.nanomachinesHungry.3=%s foi digerido. - -# NEI Integration -nei.options.inventory.oredict=Mostre nomes do OreDictionary -nei.options.inventory.oredict.true=Sim -nei.options.inventory.oredict.false=Não -nei.usage.oc.Manual=Open Manual - -# Waila Integration -option.oc.address=Endereço -option.oc.componentName=Nome do Componente -option.oc.energy=Energia diff --git a/src/main/resources/assets/opencomputers/lang/pt_PT.lang b/src/main/resources/assets/opencomputers/lang/pt_PT.lang deleted file mode 100644 index 23e0772fe6..0000000000 --- a/src/main/resources/assets/opencomputers/lang/pt_PT.lang +++ /dev/null @@ -1,137 +0,0 @@ -# This is the portuguese lang file, adapted from the english master file. -# -# CHANGES: -# LordFokas (6-JAN-2014): first translation from en_US to pt_PT. -# It's not perfect, but it's hard to translate, especially because -# given I'm a Computer Science Engineering student, I tend to mix in -# technical english words. I suggest someone less involved with CSE to -# try and revise my translation. -# -######################################################################### - - -# Blocks -tile.oc.adapter.name=Adaptador -tile.oc.cable.name=Cabo -tile.oc.capacitor.name=Condensador -tile.oc.case1.name=Caixa Básica -tile.oc.case2.name=Caixa Avançada -tile.oc.case3.name=Caixa Superior -tile.oc.charger.name=Carregador -tile.oc.diskdrive.name=Drive de Disquetes -tile.oc.keyboard.name=Teclado -tile.oc.powerconverter.name=Conversor de Energia -tile.oc.powerdistributor.name=Distribuidor de Energia -tile.oc.redstone.name=E/S de Redstone -tile.oc.robot.name=Robô -tile.oc.robotafterimage.name=Robô -tile.oc.screen1.name=Ecrã Básico -tile.oc.screen2.name=Ecrã Avançado -tile.oc.screen3.name=Ecrã Superior -tile.oc.switch.name=Roteador - -# Items -item.oc.acid.name=Grogue -item.oc.alu.name=Unidade Aritmética e Lógica -item.oc.analyzer.name=Analizador -item.oc.arrowkeys.name=Setas -item.oc.buttongroup.name=Grupo de Botões -item.oc.cardbase.name=Placa Base -item.oc.circuitboard.name=Placa de Circuitos -item.oc.controlunit.name=Unidade de Controlo -item.oc.cpu.name=Processador -item.oc.cuttingwire.name=Arame de Corte -item.oc.disk.name=Disco -item.oc.floppydisk.name=Disquete -item.oc.graphicscard0.name=Placa Gráfica Básica -item.oc.graphicscard1.name=Placa Gráfica Avançada -item.oc.graphicscard2.name=Placa Gráfica Superior -item.oc.harddiskdrive.name=Disco Rígido -item.oc.ironnugget.name=Pepita de Ferro -item.oc.memory.name=Memória -item.oc.microchip0.name=Circuito Integrado Simples -item.oc.microchip1.name=Circuito Integrado Avançado -item.oc.microchip2.name=Circuito Integrado Superior -item.oc.networkcard.name=Placa de Rede -item.oc.numpad.name=Teclado Numérico -item.oc.printedcircuitboard.name=Placa de Circuitos Impressos -item.oc.rawcircuitboard.name=Placa de Circuitos Vazia -item.oc.redstonecard.name=Placa de Redstone -item.oc.transistor.name=Transístor -item.oc.upgradecrafting.name=Melhoramento: Fabrico -item.oc.upgradegenerator.name=Melhoramento: Gerador -item.oc.upgradenavigation.name=Melhoramento: Navegação -item.oc.upgradesign.name=Melhoramento: Escrita e Leitura de Placas -item.oc.upgradesolargenerator.name=Melhoramento: Gerador Solar -item.oc.wirelessnetworkcard0.name=Placa de Rede sem Fios -item.oc.WirelessNetworkCard1.name=Placa de Rede sem Fios - -# GUI -oc:gui.Analyzer.Address=§6Endereço§f: %s -oc:gui.Analyzer.ComponentName=§6Nome do Componente§f: %s -oc:gui.Analyzer.LastError=§6Último erro§f: %s -oc:gui.Analyzer.RobotName=§6Nome§f: %s -oc:gui.Analyzer.RobotOwner=§6Proprietário§f: %s -oc:gui.Analyzer.RobotXp=§6Experiência§f: %s -oc:gui.Analyzer.StoredEnergy=§6Energia Armazenada§f: %s -oc:gui.Analyzer.TotalEnergy=§6Total de Energia Armazenada§f: %s -oc:gui.Analyzer.Users=§6Utilizadores§f: %s -oc:gui.Robot.Power=Energia -oc:gui.Robot.TurnOff=Desligar -oc:gui.Robot.TurnOn=Ligar - -# Containers -oc:container.case=Computador -oc:container.charger=Carregador -oc:container.diskdrive=Drive de Disquetes -oc:container.switch=Roteador - -# Item / Block Tooltips -oc:tooltip.acid=Um líquido muito tóxico, geralmente apenas consumido por alguns piratas. Graças à sua natureza corrosiva é perfeito para gravar placas de circuitos. -oc:tooltip.adapter=Usado para controlar blocos que não sejam componentes, tais como blocos do Minecraft Vanilla ou de outros mods. -oc:tooltip.alu=Faz cálculos por ti. Talvez seja melhor assim. -oc:tooltip.analyzer=Usado para mostrar informação acerca de blocos, tais como o seu §fendereço§7 e §fnome de componente§7.[nl] também mostra o erro que fez o computador crashar caso este não se tenha desligado normalmente. -oc:tooltip.cable=Uma forma barata de conectar blocos. -oc:tooltip.capacitor=Armazena energia para ser usada mais tarde. Pode encher e esvaziar-se muito rapidamente. -oc:tooltip.cardbase=Como o nome indica, é o componente básico para construir placas de expansão. -oc:tooltip.case=A Caixa é o bloco básico para a construção de computadores e contém as §fplacas de expansão§7, §fRAM§7 e §fdiscos rígidos§7.[nl] Slots: §f%s§7 -oc:tooltip.charger=Transfere energia dos condensadores para robôs adjacentes. A taxa de transferência depende do §fsinal de redstone§7 aplicado, onde nenhum sinal significa não carregar, e o sinal máximo significa carregar à velocidade máxima. -oc:tooltip.circuitboard=Agora estamos a chegar a algum lado. Pode ser gravada para obter uma placa de circuitos impressos. -oc:tooltip.controlunit=Esta é a unidade que... controla... coisas. Precisas dela para montar um Processador. Portanto, sim, pode-se dizer que é importante. -oc:tooltip.cpu=Um componente essencial de qualquer computador. A frequencia é pouco fiável, mas o que é que esperas quando corre com um relógio de sol? -oc:tooltip.cuttingwire=Usado para cortar blocos de barro na forma de uma placa. Parte-se ao fim de um uso, o que faz dela a ferramenta menos eficiente de sempre. -oc:tooltip.disk=Um meio primitivo que pode ser usado para construir dispositivos de armazenamento persistente. -oc:tooltip.diskdrive.CC=Disquetes do ComputerCraft são §asuportadas§7. -oc:tooltip.diskdrive=Permite ler e escrever dados em Disquetes. -oc:tooltip.graphicscard=Usado para alterar o que é mostrado nos ecrãs.[nl] Resolução máxima: §f%sx%s§7[nl] Profundidade de cor máxima: §f%s§7[nl] Operações/ciclo: §f%s§7 -oc:tooltip.ironnugget=Uma pepita feita de ferro, por isso é que se chama Pepita de Ferro, duh... -oc:tooltip.keyboard=Pode ligar-se a um ecrã para permitir escrever através dele. -oc:tooltip.memory=Necessária para poder usar um computador. Quanta mais RAM tiveres, mais complexos são os programas que podes correr. -oc:tooltip.microchip=Um chip antes conhecido por Circuito Integrado. Não faço ideia como é que isto funciona com redstone, mas funciona. -oc:tooltip.networkcard=Permite a computadores distantes ligados por blocos (tais como Cabos) comunicar enviando mensagens entre eles. -oc:tooltip.powerconverter.BuildCraft=§fMJ de Buildcraft§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fEU de IndustrialCraft²§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fRF de Thermal Expansion§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fCoulombs de Resonant Engine§7: §a%s:%s§7 -oc:tooltip.powerconverter=Converte energia de outros mods para o tipo de energia usado internamente. Taxas de conversão: -oc:tooltip.powerdistributor=Distribui energia entre diferentes redes. É util para alimentar várias redes separadas através de uma fonte de energia partilhada. -oc:tooltip.printedcircuitboard=O componente básico de placas de expansão, memórias e afins. -oc:tooltip.rawcircuitboard=Pode ser endurecida em qualquer fornalha. -oc:tooltip.redstone=Permite receber e emitir sinais de redstone. Pode ser controlado por qualquer computador ao qual o bloco esteja conectado. Basicamente isto é uma placa de controlo de redstone. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 é §asuportado§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 é §asuportado§7. -oc:tooltip.redstonecard=Permite ao computador ou robô receber e emitir sinais de redstone. -oc:tooltip.robot=Ao contrário dos computadores, os robôs podem mover-se e interagir com o mundo como um jogado. No entanto, eles §onão§r§7 podem interagir com componentes externos! -# the underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fNível§7: §a%s§7. -oc:tooltip.robot_storedenergy=§fEnergia Armazenada§7: §a%s§7. -oc:tooltip.screen=Mostra texto, controlado por uma placa gráfica numa Caixa.[nl] Resolução máxima: §f%sx%s§7[nl] Profundidade de cor máxima: §f%s§7 -oc:tooltip.switch=Permite interligar redes diferentes. Apenas pacotes de rede serão passados, componentes não serão visiveis em redes vizinhas. Usa isto para separar várias redes e ao mesmo tempo permitir comunicação através de placas de rede, por exemplo. -oc:tooltip.toolong=Prime [§f%s§7] para uma descrição detalhada. -oc:tooltip.transistor=Um componente básico do hardware do computador. É um pouco retorcido, mas faz o seu trabalho. -oc:tooltip.upgradecrafting=Permite aos robôs usar a parte superior esquerda do seu inventório para fabricar objectos. Os itens têm de estar alinhados como numa mesa de fabrico. -oc:tooltip.upgradegenerator=Pode ser usado para gerar energia a partir de combustível. Queima itens para gerar energia ao longo do tempo, dependendo da energia que pode ser extraída de cada componente.[nl] §fEficiência§7: §a%s%%§7 -oc:tooltip.upgradenavigation=Pode ser usado para determinar a posição e orientação do robô. A posição é relativa ao centro do mapa usado para fabricar este melhoramento. -oc:tooltip.upgradesign=Permiter ler e escrever texto em Placas. -oc:tooltip.upgradesolargenerator=Pode ser usado para gerar energia a partir da luz solar. Requer exposição directa à luz solar. Gera energia a %s%% da velocidade de um motor a carvão. -oc:tooltip.wirelessnetworkcard=Permite trocar mensagens em redes com e sem fios. Certifica-te que defines a §fforça do sinal§7 ou nenhum pacote será enviado via redes sem fios! \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lang/pt_br.json b/src/main/resources/assets/opencomputers/lang/pt_br.json new file mode 100644 index 0000000000..e44e35451a --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/pt_br.json @@ -0,0 +1,465 @@ +{ + "tile.oc.accesspoint": "§cPonto de Acesso§7", + "tile.oc.adapter": "Adaptador", + "tile.oc.assembler": "Montador Eletrônico", + "tile.oc.cable": "Cabo", + "tile.oc.capacitor": "Capacitador", + "tile.oc.carpetedCapacitor": "Capacitador com Tapete", + "tile.oc.case1": "Gabinete (Nível 1)", + "tile.oc.case2": "Gabinete (Nível 2)", + "tile.oc.case3": "Gabinete (Nível 3)", + "tile.oc.casecreative": "Gabinete (Criativo)", + "tile.oc.chameliumblock": "Bloco Camaleão", + "tile.oc.charger": "Carregador", + "tile.oc.disassembler": "Desmontador", + "tile.oc.diskdrive": "Unidade de Disco", + "tile.oc.endstone": "Pedra do Fim", + "tile.oc.geolyzer": "Geonalizador", + "tile.oc.hologram1": "Projetor Holográfico (Nível 1)", + "tile.oc.hologram2": "Projetor Holográfico (Nível 2)", + "tile.oc.keyboard": "Teclado", + "tile.oc.microcontroller": "Microcontrolador", + "tile.oc.motionsensor": "Sensor de Movimento", + "tile.oc.netsplitter": "Divisor de Rede", + "tile.oc.powerconverter": "Conversor de Energia", + "tile.oc.powerdistributor": "Distribuidor de Energia", + "tile.oc.print": "Impressão 3D", + "tile.oc.printer": "Impressora 3D", + "tile.oc.raid": "Raid", + "tile.oc.redstone": " E/S de Redstone", + "tile.oc.relay": "Relé", + "tile.oc.robot": "Robô", + "tile.oc.robotafterimage": "Robô", + "tile.oc.screen1": "Monitor (Nível 1)", + "tile.oc.screen2": "Monitor (Nível 2)", + "tile.oc.screen3": "Monitor (Nível 3)", + "tile.oc.rack": "Rack", + "tile.oc.switch": "§cSwitch§7", + "tile.oc.transposer": "Transpositor", + "tile.oc.waypoint": "Ponto de Passagem", + "item.oc.abstractbuscard": "Placa de Barramento Abstrata", + "item.oc.acid": "Grogue", + "item.oc.alu": "Unidade Lógica Aritmética (ULA)", + "item.oc.analyzer": "Analizador", + "item.oc.apu0": "Unidade de Processamento Acelerado (UPA) (Nível 1)", + "item.oc.apu1": "Unidade de Processamento Acelerado (UPA) (Nível 2)", + "item.oc.apu2": "Unidade de Processamento Acelerado (UPA) (Criativo)", + "item.oc.arrowkeys": "Setas do Teclado", + "item.oc.buttongroup": "Grupo de Botões", + "item.oc.cardbase": "Base de Cartão", + "item.oc.chamelium": "Camaleão", + "item.oc.circuitboard": "Placa de Circuito", + "item.oc.componentbus0": "Componente de Barramento (Nível 1)", + "item.oc.componentbus1": "Componente de Barramento (Nível 2)", + "item.oc.componentbus2": "Componente de Barramento (Nível 3)", + "item.oc.componentbus3": "Componente de Barramento (Criativo)", + "item.oc.controlunit": "Unidade de Controle (UC)", + "item.oc.cpu0": "Unidade Central de Processamento (CPU) (Nível 1)", + "item.oc.cpu1": "Unidade Central de Processamento (CPU) (Nível 2)", + "item.oc.cpu2": "Unidade Central de Processamento (CPU) (Nível 3)", + "item.oc.cuttingwire": "Fio de Corte", + "item.oc.datacard0": "Cartão de Dados (Nível 1)", + "item.oc.datacard1": "Cartão de Dados (Nível 2)", + "item.oc.datacard2": "Cartão de Dados (Nível 3)", + "item.oc.debugcard": "Cartão de Depuração", + "item.oc.debugger": "Depurador de Rede", + "item.oc.diamondchip": "Chip de Diamante", + "item.oc.disk": "Prato do Disco", + "item.oc.diskdrivemountable": "Unidade de Disco", + "item.oc.drone": "Drone", + "item.oc.dronecase0": "Carcaça de Drone (Nível 1)", + "item.oc.dronecase1": "Carcaça de Drone (Nível 2)", + "item.oc.dronecase3": "Carcaça de Drone (Criativo)", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "Disquete", + "item.oc.graphicscard0": "Placa de Vídeo (Nível 1)", + "item.oc.graphicscard1": "Placa de Vídeo (Nível 2)", + "item.oc.graphicscard2": "Placa de Vídeo (Nível 3)", + "item.oc.harddiskdrive0": "Disco Rígido (Nível 1)", + "item.oc.harddiskdrive1": "Disco Rígido (Nível 2)", + "item.oc.harddiskdrive2": "Disco Rígido (Nível 3)", + "item.oc.hoverboots": "Botas Voadoras", + "item.oc.inkcartridge": "Cartucho de Tinta", + "item.oc.inkcartridgeempty": "Cartucho de Tinta (Vazio)", + "item.oc.internetcard": "Cartão de Internet", + "item.oc.interweb": "Interweb", + "item.oc.ironnugget": "Pepita de Ferro", + "item.oc.linkedcard": "Cartão Vinculado", + "item.oc.manual": "Manual do OpenComputers", + "item.oc.memory0": "Memória (Nível 1)", + "item.oc.memory1": "Memória (Nível 1.5)", + "item.oc.memory2": "Memória (Nível 2)", + "item.oc.memory3": "Memória (Nível 2.5)", + "item.oc.memory4": "Memória (Nível 3)", + "item.oc.memory5": "Memória (Nível 3.5)", + "item.oc.microchip0": "Circuito Integrado (Nível 1)", + "item.oc.microchip1": "Circuito Integrado (Nível 2)", + "item.oc.microchip2": "Circuito Integrado (Nível 3)", + "item.oc.microcontrollercase0": "Carcaça do Microcontrolador (Nível 1)", + "item.oc.microcontrollercase1": "Carcaça do Microcontrolador (Nível 2)", + "item.oc.microcontrollercase3": "Carcaça do Microcontrolador (Criativo)", + "item.oc.nanomachines": "Nanomáquinas", + "item.oc.networkcard": "Cartão de Rede", + "item.oc.numpad": "Teclado Numérico", + "item.oc.present": "Uma coisinha...", + "item.oc.printedcircuitboard": "Placa de Circuito Impresso (PCB)", + "item.oc.rawcircuitboard": "Placa de Circuito Crua", + "item.oc.redstonecard0": "Cartão de Redstone (Nível 1)", + "item.oc.redstonecard1": "Cartão de Redstone (Nível 2)", + "item.oc.server0": "Servidor (Nível 1)", + "item.oc.server1": "Servidor (Nível 2)", + "item.oc.server2": "Servidor (Nível 3)", + "item.oc.server3": "Servidor (Criativo)", + "item.oc.tablet": "Tablet", + "item.oc.tabletcase0": "Carcaça de Tablet (Nível 1)", + "item.oc.tabletcase1": "Carcaça de Tablet (Nível 2)", + "item.oc.tabletcase3": "Carcaça de Tablet (Criativo)", + "item.oc.terminal": "Terminal Remoto", + "item.oc.terminalserver": "Servidor de Terminal", + "item.oc.texturepicker": "Selecionador de Textura", + "item.oc.transistor": "Transistor", + "item.oc.upgradeangel": "Aprimoramento Angelical", + "item.oc.upgradebattery0": "Aprimoramento de Bateria (Nível 1)", + "item.oc.upgradebattery1": "Aprimoramento de Bateria (Nível 2)", + "item.oc.upgradebattery2": "Aprimoramento de Bateria (Nível 3)", + "item.oc.upgradechunkloader": "Aprimoramento de Chunkloader", + "item.oc.upgradecontainercard0": "Contêiner de Cartão (Nível 1)", + "item.oc.upgradecontainercard1": "Contêiner de Cartão (Nível 2)", + "item.oc.upgradecontainercard2": "Contêiner de Cartão (Nível 3)", + "item.oc.upgradecontainerupgrade0": "Aprimoramento de Contêiner (Nível 1)", + "item.oc.upgradecontainerupgrade1": "Aprimoramento de Contêiner (Nível 2)", + "item.oc.upgradecontainerupgrade2": "Aprimoramento de Contêiner (Nível 3)", + "item.oc.upgradecrafting": "Aprimoramento de Construção", + "item.oc.upgradedatabase0": "Aprimoramento de Banco de Dados (Nível 1)", + "item.oc.upgradedatabase1": "Aprimoramento de Banco de Dados (Nível 2)", + "item.oc.upgradedatabase2": "Aprimoramento de Banco de Dados (Nível 3)", + "item.oc.upgradeexperience": "Aprimoramento de Experiência", + "item.oc.upgradegenerator": "Aprimoramento de Gerador", + "item.oc.upgradehover0": "Aprimoramento de Flutuação (Nível 1)", + "item.oc.upgradehover1": "Aprimoramento de Flutuação (Nível 2)", + "item.oc.upgradeinventory": "Aprimoramento de Inventário", + "item.oc.upgradeinventorycontroller": "Aprimoramento de Controlador de Inventário", + "item.oc.upgradeleash": "Aprimoramento de Correia", + "item.oc.upgradeMF": "MFU", + "item.oc.upgradenavigation": "Aprimoramento de Navegação", + "item.oc.upgradepiston": "Aprimoramento de Pistão", + "item.oc.upgradesign": "Aprimoramento de Sinal de E/S", + "item.oc.upgradesolargenerator": "Aprimoramento de Gerador Solar", + "item.oc.upgradetank": "Aprimoramento de Tanque", + "item.oc.upgradetankcontroller": "Aprimoramento de Controlador de Tanque", + "item.oc.upgradetractorbeam": "Aprimoramento de Raio Trator", + "item.oc.upgradetrading": "Aprimoramento de Troca", + "item.oc.wirelessnetworkcard0": "Cartão de Rede Sem Fio (Nível 1)", + "item.oc.wirelessnetworkcard1": "Cartão de Rede Sem Fio (Nível 2)", + "item.oc.worldsensorcard": "Cartão de Sensor de Mundo", + "item.oc.wrench": "Chave de fenda-boca", + "entity.oc.Drone.name": "Drone", + "oc:gui.Analyzer.Address": "§6Endereço§f: %s", + "oc:gui.Analyzer.AddressCopied": "Endereço copiado para a área de transferência.", + "oc:gui.Analyzer.ChargerSpeed": "§6Velocidade da carga§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Nome do componente§f: %s", + "oc:gui.Analyzer.Components": "§6Número de componentes conectados§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "Clique para copiar para a área de transferência.", + "oc:gui.Analyzer.LastError": "§6Último erro§f: %s", + "oc:gui.Analyzer.RobotName": "§6Nome§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Dono§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Experiência§f: %s (Nível %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Energia armazenada§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Total de energia armazenada§f: %s", + "oc:gui.Analyzer.Users": "§6Usuários§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Força do sinal§f: %s", + "oc:gui.Assembler.Collect": "Saída do coletador", + "oc:gui.Assembler.Complexity": "Complexidade: %s/%s", + "oc:gui.Assembler.InsertCase": "Insira uma parte base", + "oc:gui.Assembler.InsertCPU": "Insira uma CPU", + "oc:gui.Assembler.InsertRAM": "Insira alguma RAM", + "oc:gui.Assembler.Progress": "Progresso: %s%% (%s)", + "oc:gui.Assembler.Run": "Montar", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Placa de Vídeo", + "oc:gui.Assembler.Warning.Inventory": "Aprimoramento de Inventário", + "oc:gui.Assembler.Warning.Keyboard": "Teclado", + "oc:gui.Assembler.Warning.OS": "Mídia Inicializável", + "oc:gui.Assembler.Warning.Screen": "Monitor", + "oc:gui.Assembler.Warnings": "§eAtenção§7: Componentes recomendados ausentes.", + "oc:gui.Chat.NewVersion": "Uma nova versão está disponível: %s", + "oc:gui.Chat.TextureName": "§7Nome da textura é §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "Houve um erro §cerros§f ao rodar o 'class transformer'. Por favor relate isso, junto com o seu arquivo de log (completo!) FML §alatest.log§f/§afml-server-latest.log§f , obrigado!", + "oc:gui.Chat.WarningFingerprint": "§cATENÇÃO§f - impressão digital não reconhecida! Experado '§a%s§f' mas recebido '§e%s§f'. A não ser que você seja um desenvolvedor e esteja rodando uma versão desofuscada, é §lextremamente§f recomendável que baixe novamente o OpenComputers, por que o JAR que está usando talvez esteja adulterado.", + "oc:gui.Chat.WarningLink": "Não foi possível abrir o atalho: %s", + "oc:gui.Chat.WarningLuaFallback": "Bibliotecas nativas do Lua não disponíveis, computadores não serão capazes de persistir seus estados. Eles reinicializarão quando chunks descarregarem.", + "oc:gui.Chat.WarningProjectRed": "Você está usando uma versão do Project: Red que é incompativel com OpenComputers. Tente atualizar sua versão do Project: Red.", + "oc:gui.Chat.WarningRecipes": "Houve erros ao carregar uma ou mais receitas. Alguns itens não poderão ser construídos. Verifique seu arquivo de log para mais informações.", + "oc:gui.Chat.WarningSimpleComponent": "Um addon (seu?) usa a interface §aSimpleComponent§f que faz §ealgo errado§f. A lógica de componente não pode ser injetada. Por favor verifique seu arquivo de log para mais informações.", + "oc:gui.Drive.Managed": "Gerenciado", + "oc:gui.Drive.Unmanaged": "Não Gerenciado", + "oc:gui.Drive.ReadOnlyLock": "Bloqueado", + "oc:gui.Drive.ReadOnlyLockWarning": "§Somente leitura§r. Isso não pode ser removido, exceto quando o disco é formatado.", + "oc:gui.Drive.Warning": "§lAtençaõ§r: mudança de modos resultará em perca de todos os dados do disco!", + "oc:gui.Error.ComponentOverflow": "Muitos componentes conectados ao computador.", + "oc:gui.Error.InternalError": "Erro interno, por favor veja o arquivo de log. Isto é provavelmente um erro.", + "oc:gui.Error.NoCPU": "Não há CPU instalada no computador.", + "oc:gui.Error.NoEnergy": "Energia insuficiente.", + "oc:gui.Error.NoRAM": "Não há RAM instalada no computador.", + "oc:gui.Error.OutOfMemory": "Sem memória.", + "oc:gui.Manual.Blocks": "Blocos do OpenComputers", + "oc:gui.Manual.Home": "Início", + "oc:gui.Manual.Items": "Itens do OpenComputers", + "oc:gui.Manual.Warning.BlockMissing": "Bloco indisponível.", + "oc:gui.Manual.Warning.ImageMissing": "Imagem não encontrada.", + "oc:gui.Manual.Warning.ItemMissing": "Item indisponível.", + "oc:gui.Manual.Warning.OreDictMissing": "Entrada no dicionário de minérios indisponível.", + "oc:gui.Raid.Warning": "§4Adicionar um disco o formata.\nRemover um disco limpa a raid.", + "oc:gui.Robot.Power": "Energia", + "oc:gui.Robot.TurnOff": "Desligar", + "oc:gui.Robot.TurnOn": "Ligar\n§7Use um Analizador para verficar problemas.§r", + "oc:gui.Rack.Back": "Voltar", + "oc:gui.Rack.Bottom": "Baixo", + "oc:gui.Rack.Left": "Esquerda", + "oc:gui.Rack.None": "Nenhum", + "oc:gui.Rack.Right": "Direita", + "oc:gui.Rack.Enabled": "Habilitado", + "oc:gui.Rack.Disabled": "Disabilitado", + "oc:gui.Rack.RelayModeTooltip": "Modo de relé", + "oc:gui.Rack.Top": "Cima", + "oc:gui.Switch.PacketsPerCycle": "Pacotes / ciclo", + "oc:gui.Switch.QueueSize": "Tamanho da fila", + "oc:gui.Switch.TransferRate": "Taxa do ciclo", + "oc:gui.Terminal.InvalidKey": "Chave inválida, provavelmente outro terminal foi conectado ao servidor.", + "oc:gui.Terminal.OutOfRange": "Sem sinal.", + "oc:container.accesspoint": "Ponto de Acesso", + "oc:container.adapter": "Adaptador", + "oc:container.case": "Computador", + "oc:container.charger": "Carregador", + "oc:container.disassembler": "Desmontador", + "oc:container.diskdrive": "Unidade de disco", + "oc:container.printer": "Impressora", + "oc:container.raid": "Raid", + "oc:container.relay": "Relé", + "oc:container.server": "Servidor", + "oc:container.rack": "Rack", + "oc:container.switch": "Switch", + "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.clipboardPaste": "Colar da área de transferência", + "key.opencomputers.materialCosts": "Mostrar custo dos materiais", + "oc:tooltip.accesspoint": "Atua como um Switch, mas adicionalmente recebe pacotes sem fio e retransmite pacotes com fio sem fio.", + "oc:tooltip.abstractbuscard": "Permite interagir com o barramento abstrato de §fStargateTech 2§7 enviando e recebendo pacotes LIP.", + "oc:tooltip.acid": "Um pseudo-liquido altamente tóxico, usualmente consumidos por alguns piratas (ihic!). Também, todavia, pode ser útil de outros modos.", + "oc:tooltip.adapter": "Usado para controlar blocos que não são componentes, tal como blocos vanillas ou de outros mods.", + "oc:tooltip.alu": "Adiciona números que você não têm. Bem melhor assim.", + "oc:tooltip.analyzer": "Usado para mostrar informações sobre os blocos, tal como seu §fendereço§7 e §fnome do componente§7.\nTambém mostra o erro que causou um computador travar se não foi desligado normalmente.", + "oc:tooltip.apu": "Isso é um CPU com um GPU integrado (ou IGP), quando você apenas precisa deste slot de placa .\nComponentes Suportados: §f%s§7\nResolução mãxima: §f%sx%s§7\nDensidade de cores máxima: §f%s§7\nOperações/tick: §f%s§7", + "oc:tooltip.assembler": "Permite construir robôs e outros dispositivos com várias partes de computador.", + "oc:tooltip.cable": "Uma maneira barata de conectar os blocos.", + "oc:tooltip.capacitor": "Armazena energia para usar mais tarde. Pode ser enchido e esvaziado rapidamente.", + "oc:tooltip.cardbase": "Como o nome diz, este é o bloco de construção básico para os cartões de expansão.", + "oc:tooltip.carpetedcapacitor": "Armazena energia para uso posterior. Pode ser carregado e descarregado rapidamente. Carrega quando uma ovelha ou jaguatirica anda sobre o mesmo.", + "oc:tooltip.case": "O gabinete é o bloco básico para computadores e guarda os §fcartões de extensão§7, §fRAM§7 e §fdiscos rígidos§7.\nSlots: §f%s§7", + "oc:tooltip.chamelium": "Matéria prima para as imressões 3D. Não ingerir: talvez leve a cegueira e um lapso temporário de presença.", + "oc:tooltip.chameliumblock": "Agradável e Limpo. Usado para formas coloridas nas impressões 3D, ou para simplesmente ter, um bloco colorido para decorar sua linda e chamativa base.", + "oc:tooltip.charger": "Transfere energia dos capacitores para robôs e drones adjacentes. A taxa de energia depende do §fsinal de redstone§7 vindo, onde sem sinal não carrega os dispositivos, e quanto mais forte o sinal maior será a velocidade de carregamento. Pode ser usado também para carregar tablets e unidades de disco de acesso em tablets.", + "oc:tooltip.circuitboard": "Agora estamos chegando em algum lugar. Pode ser cozido para obter uma placa de circuito impresso.", + "oc:tooltip.controlunit": "Está é a unidade que... controla... coisas. Você precisa disso para construir a CPU. Então é tipo, totalmente importante.", + "oc:tooltip.componentbus": "Está expansão permite aos servidores comunicarem com mais componentes ao mesmo tempo, similar como os CPUs fazem.\nComponentes Suportados: §f%s§7", + "oc:tooltip.cpu": "Um componente essencial para todos os computadores. O clock (relógio/taxa do processamento) é um tanto quanto que duvidoso, mas o que você espera se ele roda num relógio de sol de bolso?\nComponentes Suportados: §f%s§7", + "oc:tooltip.cpu.Architecture": "Arquitetura: §f%s§7", + "oc:tooltip.cuttingwire": "Usado para cortar blocos de argila em formato de placas de circuito. Quebra quando usa, o que provavelmente é a pior ferramenta de todas.", + "oc:tooltip.datacard0": "Provê um conjuto de algoritmos tal como hash e desinflar/inflar.", + "oc:tooltip.datacard1": "Provê um conjuto de algoritmos tal como hash, encriptação AES e desinflar/inflar.", + "oc:tooltip.datacard2": "Provê um conjuto de algoritmos tal como hash, encriptação AES, criptografia de curva elíptica e desinflar/inflar.", + "oc:tooltip.debugcard": "Item do modo criativo, permite manipular o mundo para fazer testes facieis. Use por sua conta e risco.", + "oc:tooltip.debugger": "Pode ser usado para depurar informações na grade de rede interna do OC. Somente use se for instruído por um desenvolvedor.", + "oc:tooltip.diamondchip": "Um pedacinho de um diamante uma vez impecável. Ele nunca será o mesmo de novo.", + "oc:tooltip.disassembler": "Separa os itens em seus componentes originais. §lAtenção§7: o item retornado tem uma chance de %s%% de quebrar no processo!", + "oc:tooltip.disk": "Uma mídia primitiva que pode ser usada para construir dispositvos de armazenamentos contínuos.", + "oc:tooltip.diskdrive.CC": "Disquetes do ComputerCraft são §asuportados§7.", + "oc:tooltip.diskdrive": "Permite escrever e ler disquetes. Pode ser instalado em robôs para permitir inserir disquetes mais tarde.", + "oc:tooltip.diskdrivemountable": "Provê a mesma funcionalidade de uma unidade de disco normal, mas só pode ser instalado em um rack.", + "oc:tooltip.diskusage": "Uso de disco: %s/%s Byte", + "oc:tooltip.disklocked": "Bloqueado por: %s", + "oc:tooltip.diskmodemanaged": "Modo: Gerenciado", + "oc:tooltip.diskmodeunmanaged": "Modo: Não gerenciado", + "oc:tooltip.drone": "Drones são leves, com reconhecimento rápido de unidades com espaço interno limitado.", + "oc:tooltip.dronecase": "Está carcaça é usada para montar drones no montador. Ele tem um espaço para uma pequena quantidade de componentes e provê levitação com a pedra do fim.", + "oc:tooltip.eeprom": "Pequeno armazenamento programável que contem a BIOS dos computadores usado para iniciá-los.", + "oc:tooltip.fakeendstone": "Quase igual a coisa real, emula até mesmo sua levitação!", + "oc:tooltip.geolyzer": "Permite escanear a dureza da área ao redor de blocos sólidos. Essa informação pode ser útil para geração de hologramas de área ou para detectar minérios.", + "oc:tooltip.graphicscard": "Usado para mudar o que é mostrado nos monitores.\nMáxima resolução: §f%sx%s§7\nMáxima profundidade de cores: §f%s§7\nOperações/tick: §f%s§7", + "oc:tooltip.hoverboots": "Pule alto, caia devagar, caminhe melhor. Isso e mais, com as mais novas e patenteadas Botas Voadoras (TM).", + "oc:tooltip.inkcartridge": "Usado para carregar tinta nas impressoras 3D. Por razões misteriosas ele não tem que permanecer na impressora.", + "oc:tooltip.inkcartridgeempty": "Este cartucho de tinta foi usado até secar. Recarregue isso usando corantes. Ou jogue-o fora. Veja minha cara de preocupado -.-.", + "oc:tooltip.internetcard": "Este cartão faz requisições HTTP usa protocolos TCP reais.", + "oc:tooltip.interweb": "Parabéns, você ganhou um (1) interweb. Você pode conectar a isso usando um Cartão de Internet. Cuidado: não alimente os trolls.", + "oc:tooltip.ironnugget": "Uma pepita feita de ferro, por isso é chamado de Pepita de Ferro, darrr...", + "oc:tooltip.keyboard": "Pode ser anexados aos monitores para permitir digitar neles.", + "oc:tooltip.hologram0": "Uma exibição volumétrica que pode ser controlado por computadores para exibir estruturas de voxel arbitrárias.\nResolução: §f48x32x48§7\nEscala máxima: §f3x§7\nProfundidade de cores: §fMonocromatico§7", + "oc:tooltip.hologram1": "Uma exibição volumétrica que pode ser controlado por computadores para exibir estruturas de voxel arbitrárias.\nResolução: §f48x32x48§7\nEscala máxima: §f4x§7\nProfundidade de cores: §fTricolor§7", + "oc:tooltip.linkedcard": "Eles são construidos em pares, e só pode comunicar-se com seu par. Entretanto, eles podem se comunicar em qualquer distância, e também cruzar dimensões. A energia requerida para enviar a mensagem é altíssima.", + "oc:tooltip.linkedcard_channel": "§8Canal: %s§7", + "oc:tooltip.manual": "Toda informaçâo que você provalvemente precisará do OpenComputers. E mais. Por um preço inacreditável de... §opor favor pressiona R para continuar§7.", + "oc:tooltip.materialcosts": "Segure [§f%s§7] para custos do material.", + "oc:tooltip.materials": "Materiais:", + "oc:tooltip.memory": "Requerida para fazer os computadores funcionarem. Quanto mais tiver, mais complexos podem ser os programas.", + "oc:tooltip.microchip": "Um chip conhecido formalmente como Circuito Integrado. Eu não tenho ideia porque isso funciona com redstone, mas funciona.", + "oc:tooltip.microcontroller": "Microcontroladores são mini computadores. Eles são usadas para cuidar de tarefas específicas, rodando um único programa que é provido pela EEPROM consruida dentro dele.\n§cNão pode conectar com componentes externos.§7", + "oc:tooltip.microcontrollercase": "Componente básico para construir microcontroladores. Coloque-o em um montador para adicionar novos componentes e montar um microcontrolador.", + "oc:tooltip.motionsensor": "Pode detectar movimento de formas de vidas próximas. Requer visão direta.", + "oc:tooltip.nanomachines": "Unidade de controle e um grupo de nanomáquinas pra ingerir, se você tiver coragem.", + "oc:tooltip.networkcard": "Permite que computadores distantes conectem por outros blocos (como cabos) para comunicar-se enviando mensagens um para o outro.", + "oc:tooltip.poweracceptor": "Velocidade da conversão de energia: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fCarregamento do Factorization§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Converte energia de outros mods para o tipo de energia interna. Taxa de conversão:", + "oc:tooltip.powerdistributor": "Distribui energia por meio de diferentes redes. Isto é útil para compartilhar fluxo de energia de seu sistema para outro conversor em diferentes sub-redes que podem manter separados.", + "oc:tooltip.present": "... para seus problemas. Abra este presente para ter uma chance de receber §kum grande tesouro§7!\n§8Construa itens do OpenComputers para ter uma chance de receber presente.§7", + "oc:tooltip.print.BeaconBase": "§8Funciona como uma base do sinalizador.", + "oc:tooltip.print.LightValue": "§8Luz emitida: %s.", + "oc:tooltip.print.RedstoneLevel": "§8Saída da Redstone: %s.", + "oc:tooltip.printedcircuitboard": "O bloco de construção básico para cartões de expansão, memória e outros.", + "oc:tooltip.printer": "Permite imprimir blocos com forma definida pelo usuário usando o Camaleão (nome lindo?) e Cartucho de Tinta. Deve ser configurado usando um computador. Deixe longe do alcance das crianças. Porque sim.", + "oc:tooltip.raid": "Permite cobinar três discos rigídos em um único grande arquivo de sistema que pode ser usado por todos os computadores conectados.", + "oc:tooltip.rawcircuitboard": "Pode ser endurecido em qualquer fornalha compátivel.", + "oc:tooltip.redstone": "Permite ler e emitir sinais de redstone ao redor do bloco. Pode ser controlado por qualquer computador que o bloco estiver conectado. Isto é básicamente um cartão de redstone externo.", + "oc:tooltip.redstonecard.ProjectRed": "§fProjectRed§7 é §asuportado§7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 é §asuportado§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 é §asuportado§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 é §asuportado§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 é §asuportado§7.", + "oc:tooltip.redstonecard": "Permite ler e emitir sinais de redstone ao redor do robô computadorizado.", + "oc:tooltip.relay": "Permite conectar diversas redes uma com a outra. Somente mensagens de rede serão passadas, componentes não serão vísiveis através disto. Use para separar redes e continuar permitindo comunicação usando Cartões de Rede, por exemplo.", + "oc:tooltip.robot": "Diferente de computadores, robôs podem mover-se por ai e interagir com o mundo parecido com o que um jogador faz.\n§cNão pode conectar-se com componentes externos.§7", + "oc:tooltip.robot_level": "§fNível§7: §a%s§7", + "oc:tooltip.robot_storedenergy": "§fEnergia armazenada§7: §a%s§7", + "oc:tooltip.screen": "Mostra texto, controlado por uma placa de vídeo em um Gabinete.\nResolução máxima: §f%sx%s§7\nDensidade máxima: §f%s§7", + "oc:tooltip.server": "Isto é um servidor: existem muitos como este, mas este pode ser aprimorado com componentes como um gabinete pode ser. Ele pode ser ligado inserindo-o em um rack de servidor.", + "oc:tooltip.server.Components": "Componentes instalados:", + "oc:tooltip.rack": "Permite a instalação de até quatro servidores ou outros montáveis.", + "oc:tooltip.switch": "Permite conectar várias redes uma com a outra. Somente mensagens de rede serão transmitidas, componentes não serão vísiveis. Use-o para separar redes permitindo comunicação usando Cartões de Rede, por exemplo.", + "oc:tooltip.tablet": "Um tablet, para usar Lua de maneira portátil. Pode ser forçado a desligar se abaixando e ativando-o.", + "oc:tooltip.tabletcase": "Carcaça básica para tablets. Coloque-o no montador para adicionar componentes e criar um tablet.", + "oc:tooltip.terminal": "Permite controlar um servidor remotamente, enquanto estiver no seu alcance. Atua como monitor e teclados portateis. Shift + Botão Direito em um servidor em um rack para vincular um terminal.", + "oc:tooltip.terminalserver": "Backend para que Terminais remotos possam ser conectados para prover controle remoto. Contém um monitor virtual e um teclado.", + "oc:tooltip.texturepicker": "Esta ferramente mostra uma string descrevendo uma superfície de bloco, para usar na impressora 3D e definir formas. Não são nomes de texturas, nana nina não.", + "oc:tooltip.tier": "§8Nível %s", + "oc:tooltip.netsplitter": "Atua como um conector dinâmico. Conectividade de cada lado pode ser mudada batendo-a com uma chave. Conectividade de todos os lados podem ser invertidas aplicando um sinal de redstone.", + "oc:tooltip.toolong": "Segure [§f%s§7] para uma dica detalhada.", + "oc:tooltip.transistor": "Um elemento básico para outras partes de computador. É um pouco torto, mas faz seu trabalho.", + "oc:tooltip.transposer": "Permite transferir automaticamente itens e fluidos entre inventários adjacentes e recipientes de fluidos.", + "oc:tooltip.upgradeangel": "Permite que robôs coloquem blocos no ar, mesmo sem um ponto de apoio.", + "oc:tooltip.upgradebattery": "Aumenta a quantidade de energia que o dispositivo pode armazenar, permitindo que trabalhe mais tempo sem precisa recarregar.\nCapacidade: §f%s§7", + "oc:tooltip.upgradechunkloader": "Se um robô move-se em uma floresta e ninguém é capaz de ver ele, ele está se mexendo ou não? Este aprimoramento faz ter certeza que sim. Isto mantêm um chunk em que um dispositvo está carregado, mas consome energia continuamente enquanto ativo.", + "oc:tooltip.upgradecontainercard": "Este aprimoramento de contêiner permite dinamicamente instalar e remover um cartão de um dispositivo montado.\nNível máximo: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Este aprimoramento de contêiner permite dinamicamente instalar e remover outro aprimoramento de um dispositivo montado.\nNível máximo: §f%s§7", + "oc:tooltip.upgradecrafting": "Permite que robôs usem o canto superior esquerdo de seu inventário para construir objetos. Os itens devem estar posicionados como se estivessem numa mesa de trabalho.", + "oc:tooltip.upgradedatabase": "Este aprimoramento permite armazenar informação de pilha de itens para ser usado por outros componentes.\nEntradas suportadas: §f%s§7", + "oc:tooltip.upgradeexperience": "Este aprimoramento permite que robôs acumulem experiência efetuando diversas operações. Quanto mais experiência estiver, mais energia pode armazenar, mais rápido pode cortar blocos e mais efeciente ele usa as ferramentas.", + "oc:tooltip.upgradegenerator": "Pode ser usado para gerar combustível em movimento. Queima itens para gerar energia o tempo todo, baseado em seus valores de combustível.\n§fEfeciencia§7: §a%s%%§7", + "oc:tooltip.upgradehover": "Este aprimoramento permite que robôs voem acima do solo sem precisar escalar paredes.\nAltura máxima: §f%s§7", + "oc:tooltip.upgradeinventory": "Este aprimoramento provê espaço no inventário para robôs ou drones. Sem um destes, eles não serão capazes de armazenar itens internamente.", + "oc:tooltip.upgradeinventorycontroller": "Este aprimoramento permite que robôs e drones tenham mais controle de como eles interagem com o inventário externo, e permite que robôs troquem sua ferramenta equipada com algum item do seu inventário.", + "oc:tooltip.upgrademf": "Permite que adaptadores acessem blocos não adjacentes.", + "oc:tooltip.upgrademf.Linked": "§fConexão estabelecida§7", + "oc:tooltip.upgrademf.Unlinked": "§fSem conexão§7", + "oc:tooltip.upgradeleash": "Permite que alguns dispostivos, como drones, coloquem animais em coleiras. MUITOSSSS ANIMAIIIIIIss.", + "oc:tooltip.upgradenavigation": "Pode ser usado para determinar a posição e orientação de um dispositivo. A posição é relativa ao centro do mapa que foi usado para construir este aprimoramento.", + "oc:tooltip.upgradepiston": "Este aprimoramento tem um movimento agressivo. Permite mexer blocos, como se estivesse usando um pistão. Isto §lnão§7 move entidades.", + "oc:tooltip.upgradesign": "Permite ler e escrever em placas de texto.", + "oc:tooltip.upgradesolargenerator": "Pode ser usado para gerar energia solar em movimento. Requer uma linha de visão limpa do céu acima do dispositivo. Gera energia em %s%% da velocidade de um Motor Stirling.", + "oc:tooltip.upgradetank": "Este aprimoramento provê um armazenamento de tanque ou fluidos para robôs e drones. Sem um destes, ele não será capaz de armazenar fluidos internamente.", + "oc:tooltip.upgradetankcontroller": "Este aprimoramento permite que robôs e drones tenham mais controle de como eles interagem com tanques externos, e permite que ele transfira fluidos para os tanques e dos tanques.", + "oc:tooltip.upgradetractorbeam": "Equipa um dispositivo com uma tecnologia extremamente avançada, codenome \"Imã\". Permite que dispositivos capturem blocos com o raio de 3 blocos de sua localização.", + "oc:tooltip.upgradetrading": "Permite que robôs e drones façam trocas com Aldeões.", + "oc:tooltip.waypoint": "Provê um ponto de referência para os dispositvos com um aprimoramento de navegação.", + "oc:tooltip.wirelessnetworkcard": "Permite enviar mensagens de rede além do meio normal. Você pode ajustar a §fforça do sinal§7 para controlar o quão longe as mensagens vão. Altos níveis de força de sinal resulta em alto consumo de energia.", + "oc:tooltip.worldsensorcard": "Permite ler informações sobre o mundo, como gravidade e/ou se tem uma atmosfera respirável. Use os resultados por sua conta e risco. O fabricando não se responsabiliza por danos corporais ou materias por decisões causadas sobre as saídas dos cartões. Nós temos advogados. E dinheiro. Então nem tente.", + "oc:tooltip.wrench": "Um híbrido entre uma Chave de fenda e uma Chave de boca, esta ferramenta é fácil de usar, mas díficil de dominar.", + "achievement.oc.adapter": "Ai que delicia cara.", + "achievement.oc.adapter.desc": "Interagiu com blocos de outros mods e também do Minecraft vanilla!", + "achievement.oc.assembler": "Explêndido", + "achievement.oc.assembler.desc": "Hora de dominar o mundo!", + "achievement.oc.cable": "Não é um Fio Sujo", + "achievement.oc.cable.desc": "Com a tecnologia patenteada de anti-espaguete.", + "achievement.oc.capacitor": "Baterias inclusas", + "achievement.oc.capacitor.desc": "Você não pode parar isso.", + "achievement.oc.card": "Aceitamos Cartões", + "achievement.oc.card.desc": "Para sua conveniência. Sem segundas intenções, prometo.", + "achievement.oc.case": "Em Caso de Problemas", + "achievement.oc.case.desc": "Porque torres quadradas são as melhores.", + "achievement.oc.charger": "Tá bom, vamo lá", + "achievement.oc.charger.desc": "Carrreeegaaaan- poxa, esqueci do sinal de redstone de novo.", + "achievement.oc.chip": "Todas as Pequenas Coisas", + "achievement.oc.chip.desc": "Porque tubos a vácuo são coisa do passado.", + "achievement.oc.cpu": "Overclocked", + "achievement.oc.cpu.desc": "Hora de fazer uso destes ciclos computacionais.", + "achievement.oc.disassembler": "Vamo quebra tudo!", + "achievement.oc.disassembler.desc": "Caso suas ideias brilhantes não sejam tão brilhantes assim.", + "achievement.oc.diskDrive": "Roda a Roda.", + "achievement.oc.diskDrive.desc": "Capacidade inferior mas um som lindíssimo.", + "achievement.oc.drone": "Voando alto.", + "achievement.oc.drone.desc": "Mantenha a calma e jogue-o para fora do planeta.", + "achievement.oc.eeprom": "Só pode ter um", + "achievement.oc.eeprom.desc": "Por computador, claro. Para manter a ordem da inicialização, entendeu?", + "achievement.oc.floppy": "Um ri- disco", + "achievement.oc.floppy.desc": "Velho, mas ainda funciona.", + "achievement.oc.geolyzer": "Pra dentro da Terra", + "achievement.oc.geolyzer.desc": "Têm qualidades extraordinárias.", + "achievement.oc.graphicsCard": "Ultima Geração", + "achievement.oc.graphicsCard.desc": "Assim pode ser ... ann... rendereziado. Ah sim sim.", + "achievement.oc.hdd": "HDD Melhor que HD", + "achievement.oc.hdd.desc": "Não espere, não é isso que significa. Bom, você entendeu...", + "achievement.oc.hologram": "Próxima Dimensão", + "achievement.oc.hologram.desc": "Porque 2D é chato. Ou não é?", + "achievement.oc.keyboard": "PegadorDeSujeira3000", + "achievement.oc.keyboard.desc": "É altamente recomendável resistir o impulso de girar eles no ar.", + "achievement.oc.microcontroller": "Irmanzinha", + "achievement.oc.microcontroller.desc": "O irmãozinho do computador.", + "achievement.oc.motionSensor": "Mexe ae que quero vê", + "achievement.oc.motionSensor.desc": "Igual a Steve Swagger.", + "achievement.oc.networkCard": "Agora Estamos Falando!", + "achievement.oc.networkCard.desc": "Mantenha contato com esses parentes distantes.", + "achievement.oc.openOS": "Buti", + "achievement.oc.openOS.desc": "Um SO para - espere, já usei essa antes? Droga.", + "achievement.oc.powerDistributor": "Compartilhar é Amar", + "achievement.oc.powerDistributor.desc": "Quando você precisa de ajuda para equilibrar toda essa energia.", + "achievement.oc.rack": "Mas que potência", + "achievement.oc.rack.desc": "Sim, os racks de servidores são potentes.", + "achievement.oc.raid": "LFG", + "achievement.oc.raid.desc": "Heroíco valeuflws.", + "achievement.oc.ram": "Random Access Memories (Memórias de Acesso Aleatório)", + "achievement.oc.ram.desc": "Parabéns, Você está fazendo isso certo.", + "achievement.oc.redstoneCard": "Con... tato", + "achievement.oc.redstoneCard.desc": "Hora de ser análogo.", + "achievement.oc.redstoneIO": "Hello from the outside...", + "achievement.oc.redstoneIO.desc": "Levando os sinais de redstone para onde quiser.", + "achievement.oc.robot": "Bip Bip", + "achievement.oc.robot.desc": "EXTERMINAR!", + "achievement.oc.screen": "Tentou desligar e ligar de novo?", + "achievement.oc.screen.desc": "Não, de verdade. Um pulso de redstone pode alternar o monitor entre ligado e desligado.", + "achievement.oc.server": "Dedicado", + "achievement.oc.server.desc": "Servidores na nuvem, aqui vamos nós.", + "achievement.oc.switch": "Topologia Complexa", + "achievement.oc.switch.desc": "Evite embalagens baratas devido a possibilidade de pacotes perdidos.", + "achievement.oc.tablet": "Não Engula", + "achievement.oc.tablet.desc": "Mantenha longe do alcance das crianças para evitar cobranças exageradas no seu cartão de crédito.", + "achievement.oc.transistor": "Onipresente", + "achievement.oc.transistor.desc": "Crie um Transistor para começar.", + "achievement.oc.wirelessNetworkCard": "Sinais", + "achievement.oc.wirelessNetworkCard.desc": "Hora de ir onde nenhum pacote foi antes.", + "death.attack.oc.nanomachinesOverload.1": "%s foi muito ganancioso.", + "death.attack.oc.nanomachinesOverload.2": "%s teve um ataque de nervos.", + "death.attack.oc.nanomachinesOverload.3": "As nanomáquinas de %s estão fora de controle.", + "death.attack.oc.nanomachinesHungry.1": "%s foi comido por nanomáquinas.", + "death.attack.oc.nanomachinesHungry.2": "%s não alimentou suas nanomáquinas.", + "death.attack.oc.nanomachinesHungry.3": "%s foi digerido.", + "nei.options.inventory.oredict": "Mostre nomes do OreDictionary", + "nei.options.inventory.oredict.true": "Sim", + "nei.options.inventory.oredict.false": "Não", + "nei.usage.oc.Manual": "Open Manual", + "option.oc.address": "Endereço", + "option.oc.componentName": "Nome do Componente", + "option.oc.energy": "Energia" +} diff --git a/src/main/resources/assets/opencomputers/lang/pt_pt.json b/src/main/resources/assets/opencomputers/lang/pt_pt.json new file mode 100644 index 0000000000..15ad352395 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/pt_pt.json @@ -0,0 +1,117 @@ +{ + "tile.oc.adapter": "Adaptador", + "tile.oc.cable": "Cabo", + "tile.oc.capacitor": "Condensador", + "tile.oc.case1": "Caixa Básica", + "tile.oc.case2": "Caixa Avançada", + "tile.oc.case3": "Caixa Superior", + "tile.oc.charger": "Carregador", + "tile.oc.diskdrive": "Drive de Disquetes", + "tile.oc.keyboard": "Teclado", + "tile.oc.powerconverter": "Conversor de Energia", + "tile.oc.powerdistributor": "Distribuidor de Energia", + "tile.oc.redstone": "E/S de Redstone", + "tile.oc.robot": "Robô", + "tile.oc.robotafterimage": "Robô", + "tile.oc.screen1": "Ecrã Básico", + "tile.oc.screen2": "Ecrã Avançado", + "tile.oc.screen3": "Ecrã Superior", + "tile.oc.switch": "Roteador", + "item.oc.acid": "Grogue", + "item.oc.alu": "Unidade Aritmética e Lógica", + "item.oc.analyzer": "Analizador", + "item.oc.arrowkeys": "Setas", + "item.oc.buttongroup": "Grupo de Botões", + "item.oc.cardbase": "Placa Base", + "item.oc.circuitboard": "Placa de Circuitos", + "item.oc.controlunit": "Unidade de Controlo", + "item.oc.cpu": "Processador", + "item.oc.cuttingwire": "Arame de Corte", + "item.oc.disk": "Disco", + "item.oc.floppydisk": "Disquete", + "item.oc.graphicscard0": "Placa Gráfica Básica", + "item.oc.graphicscard1": "Placa Gráfica Avançada", + "item.oc.graphicscard2": "Placa Gráfica Superior", + "item.oc.harddiskdrive": "Disco Rígido", + "item.oc.ironnugget": "Pepita de Ferro", + "item.oc.memory": "Memória", + "item.oc.microchip0": "Circuito Integrado Simples", + "item.oc.microchip1": "Circuito Integrado Avançado", + "item.oc.microchip2": "Circuito Integrado Superior", + "item.oc.networkcard": "Placa de Rede", + "item.oc.numpad": "Teclado Numérico", + "item.oc.printedcircuitboard": "Placa de Circuitos Impressos", + "item.oc.rawcircuitboard": "Placa de Circuitos Vazia", + "item.oc.redstonecard": "Placa de Redstone", + "item.oc.transistor": "Transístor", + "item.oc.upgradecrafting": "Melhoramento: Fabrico", + "item.oc.upgradegenerator": "Melhoramento: Gerador", + "item.oc.upgradenavigation": "Melhoramento: Navegação", + "item.oc.upgradesign": "Melhoramento: Escrita e Leitura de Placas", + "item.oc.upgradesolargenerator": "Melhoramento: Gerador Solar", + "item.oc.wirelessnetworkcard0": "Placa de Rede sem Fios", + "item.oc.WirelessNetworkCard1": "Placa de Rede sem Fios", + "oc:gui.Analyzer.Address": "§6Endereço§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Nome do Componente§f: %s", + "oc:gui.Analyzer.LastError": "§6Último erro§f: %s", + "oc:gui.Analyzer.RobotName": "§6Nome§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Proprietário§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Experiência§f: %s", + "oc:gui.Analyzer.StoredEnergy": "§6Energia Armazenada§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Total de Energia Armazenada§f: %s", + "oc:gui.Analyzer.Users": "§6Utilizadores§f: %s", + "oc:gui.Robot.Power": "Energia", + "oc:gui.Robot.TurnOff": "Desligar", + "oc:gui.Robot.TurnOn": "Ligar", + "oc:container.case": "Computador", + "oc:container.charger": "Carregador", + "oc:container.diskdrive": "Drive de Disquetes", + "oc:container.switch": "Roteador", + "oc:tooltip.acid": "Um líquido muito tóxico, geralmente apenas consumido por alguns piratas. Graças à sua natureza corrosiva é perfeito para gravar placas de circuitos.", + "oc:tooltip.adapter": "Usado para controlar blocos que não sejam componentes, tais como blocos do Minecraft Vanilla ou de outros mods.", + "oc:tooltip.alu": "Faz cálculos por ti. Talvez seja melhor assim.", + "oc:tooltip.analyzer": "Usado para mostrar informação acerca de blocos, tais como o seu §fendereço§7 e §fnome de componente§7.\ntambém mostra o erro que fez o computador crashar caso este não se tenha desligado normalmente.", + "oc:tooltip.cable": "Uma forma barata de conectar blocos.", + "oc:tooltip.capacitor": "Armazena energia para ser usada mais tarde. Pode encher e esvaziar-se muito rapidamente.", + "oc:tooltip.cardbase": "Como o nome indica, é o componente básico para construir placas de expansão.", + "oc:tooltip.case": "A Caixa é o bloco básico para a construção de computadores e contém as §fplacas de expansão§7, §fRAM§7 e §fdiscos rígidos§7.\nSlots: §f%s§7", + "oc:tooltip.charger": "Transfere energia dos condensadores para robôs adjacentes. A taxa de transferência depende do §fsinal de redstone§7 aplicado, onde nenhum sinal significa não carregar, e o sinal máximo significa carregar à velocidade máxima.", + "oc:tooltip.circuitboard": "Agora estamos a chegar a algum lado. Pode ser gravada para obter uma placa de circuitos impressos.", + "oc:tooltip.controlunit": "Esta é a unidade que... controla... coisas. Precisas dela para montar um Processador. Portanto, sim, pode-se dizer que é importante.", + "oc:tooltip.cpu": "Um componente essencial de qualquer computador. A frequencia é pouco fiável, mas o que é que esperas quando corre com um relógio de sol?", + "oc:tooltip.cuttingwire": "Usado para cortar blocos de barro na forma de uma placa. Parte-se ao fim de um uso, o que faz dela a ferramenta menos eficiente de sempre.", + "oc:tooltip.disk": "Um meio primitivo que pode ser usado para construir dispositivos de armazenamento persistente.", + "oc:tooltip.diskdrive.CC": "Disquetes do ComputerCraft são §asuportadas§7.", + "oc:tooltip.diskdrive": "Permite ler e escrever dados em Disquetes.", + "oc:tooltip.graphicscard": "Usado para alterar o que é mostrado nos ecrãs.\nResolução máxima: §f%sx%s§7\nProfundidade de cor máxima: §f%s§7\nOperações/ciclo: §f%s§7", + "oc:tooltip.ironnugget": "Uma pepita feita de ferro, por isso é que se chama Pepita de Ferro, duh...", + "oc:tooltip.keyboard": "Pode ligar-se a um ecrã para permitir escrever através dele.", + "oc:tooltip.memory": "Necessária para poder usar um computador. Quanta mais RAM tiveres, mais complexos são os programas que podes correr.", + "oc:tooltip.microchip": "Um chip antes conhecido por Circuito Integrado. Não faço ideia como é que isto funciona com redstone, mas funciona.", + "oc:tooltip.networkcard": "Permite a computadores distantes ligados por blocos (tais como Cabos) comunicar enviando mensagens entre eles.", + "oc:tooltip.powerconverter.BuildCraft": "§fMJ de Buildcraft§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fEU de IndustrialCraft²§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fRF de Thermal Expansion§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fCoulombs de Resonant Engine§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Converte energia de outros mods para o tipo de energia usado internamente. Taxas de conversão:", + "oc:tooltip.powerdistributor": "Distribui energia entre diferentes redes. É util para alimentar várias redes separadas através de uma fonte de energia partilhada.", + "oc:tooltip.printedcircuitboard": "O componente básico de placas de expansão, memórias e afins.", + "oc:tooltip.rawcircuitboard": "Pode ser endurecida em qualquer fornalha.", + "oc:tooltip.redstone": "Permite receber e emitir sinais de redstone. Pode ser controlado por qualquer computador ao qual o bloco esteja conectado. Basicamente isto é uma placa de controlo de redstone.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 é §asuportado§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 é §asuportado§7.", + "oc:tooltip.redstonecard": "Permite ao computador ou robô receber e emitir sinais de redstone.", + "oc:tooltip.robot": "Ao contrário dos computadores, os robôs podem mover-se e interagir com o mundo como um jogado. No entanto, eles §onão§r§7 podem interagir com componentes externos!", + "oc:tooltip.robot_level": "§fNível§7: §a%s§7.", + "oc:tooltip.robot_storedenergy": "§fEnergia Armazenada§7: §a%s§7.", + "oc:tooltip.screen": "Mostra texto, controlado por uma placa gráfica numa Caixa.\nResolução máxima: §f%sx%s§7\nProfundidade de cor máxima: §f%s§7", + "oc:tooltip.switch": "Permite interligar redes diferentes. Apenas pacotes de rede serão passados, componentes não serão visiveis em redes vizinhas. Usa isto para separar várias redes e ao mesmo tempo permitir comunicação através de placas de rede, por exemplo.", + "oc:tooltip.toolong": "Prime [§f%s§7] para uma descrição detalhada.", + "oc:tooltip.transistor": "Um componente básico do hardware do computador. É um pouco retorcido, mas faz o seu trabalho.", + "oc:tooltip.upgradecrafting": "Permite aos robôs usar a parte superior esquerda do seu inventório para fabricar objectos. Os itens têm de estar alinhados como numa mesa de fabrico.", + "oc:tooltip.upgradegenerator": "Pode ser usado para gerar energia a partir de combustível. Queima itens para gerar energia ao longo do tempo, dependendo da energia que pode ser extraída de cada componente.\n§fEficiência§7: §a%s%%§7", + "oc:tooltip.upgradenavigation": "Pode ser usado para determinar a posição e orientação do robô. A posição é relativa ao centro do mapa usado para fabricar este melhoramento.", + "oc:tooltip.upgradesign": "Permiter ler e escrever texto em Placas.", + "oc:tooltip.upgradesolargenerator": "Pode ser usado para gerar energia a partir da luz solar. Requer exposição directa à luz solar. Gera energia a %s%% da velocidade de um motor a carvão.", + "oc:tooltip.wirelessnetworkcard": "Permite trocar mensagens em redes com e sem fios. Certifica-te que defines a §fforça do sinal§7 ou nenhum pacote será enviado via redes sem fios!" +} diff --git a/src/main/resources/assets/opencomputers/lang/ru_RU.lang b/src/main/resources/assets/opencomputers/lang/ru_RU.lang deleted file mode 100644 index d85e8a8782..0000000000 --- a/src/main/resources/assets/opencomputers/lang/ru_RU.lang +++ /dev/null @@ -1,482 +0,0 @@ -# This is the Russian localization file. -# Authors: YuRaNnNzZZ, Sodiet, Adaptivity, cyber01, makkarpov, dangranos -# Use [nl] to for a line break. - -# Blocks -tile.oc.accesspoint.name=§cТочка доступа§7 -tile.oc.adapter.name=Адаптер -tile.oc.assembler.name=Сборщик -tile.oc.cable.name=Кабель -tile.oc.capacitor.name=Конденсатор энергии -tile.oc.case1.name=Системный блок (1-ый уровень) -tile.oc.case2.name=Системный блок (2-ой уровень) -tile.oc.case3.name=Системный блок (3-ий уровень) -tile.oc.casecreative.name=Системный блок (Творческий) -tile.oc.chameliumblock.name=Блок хамелиума -tile.oc.charger.name=Зарядное устройство -tile.oc.disassembler.name=Разборщик -tile.oc.diskdrive.name=Дисковод -tile.oc.endstone.name=Камень края -tile.oc.geolyzer.name=Геоанализатор -tile.oc.hologram1.name=Голографический проектор (1-ый уровень) -tile.oc.hologram2.name=Голографический проектор (2-ой уровень) -tile.oc.keyboard.name=Клавиатура -tile.oc.microcontroller.name=Микроконтроллер -tile.oc.motionsensor.name=Датчик движения -tile.oc.netsplitter.name=Сетевой переключатель -tile.oc.powerconverter.name=Преобразователь энергии -tile.oc.powerdistributor.name=Распределитель энергии -tile.oc.print.name=3D модель -tile.oc.printer.name=3D принтер -tile.oc.raid.name=RAID -tile.oc.redstone.name=Красный контроллер -tile.oc.relay.name=Ретранслятор -tile.oc.robot.name=Робот -tile.oc.robotafterimage.name=Робот -tile.oc.screen1.name=Монитор (1-ый уровень) -tile.oc.screen2.name=Монитор (2-ой уровень) -tile.oc.screen3.name=Монитор (3-ий уровень) -tile.oc.rack.name=Серверная стойка -tile.oc.switch.name=§cКоммутатор§7 -tile.oc.transposer.name=Транспозер -tile.oc.waypoint.name=Путевая точка - -# Items -item.oc.abstractbuscard.name=Карта абстрактной шины -item.oc.acid.name=Кислота -item.oc.alu.name=Арифметико-логическое устройство (АЛУ) -item.oc.analyzer.name=Анализатор -item.oc.apu0.name=Процессор с видеокартой (APU) (1-ый уровень) -item.oc.apu1.name=Процессор с видеокартой (APU) (2-ой уровень) -item.oc.apu2.name=Процессор с видеокартой (APU) (Творческий) -item.oc.arrowkeys.name=Клавиши со стрелками -item.oc.buttongroup.name=Группа кнопок -item.oc.cardbase.name=Основа для карт -item.oc.chamelium.name=Хамелиум -item.oc.circuitboard.name=Нетравленая печатная плата -item.oc.componentbus0.name=Компонентная шина (1-ый уровень) -item.oc.componentbus1.name=Компонентная шина (2-ой уровень) -item.oc.componentbus2.name=Компонентная шина (3-ий уровень) -item.oc.controlunit.name=Устройство управления (УУ) -item.oc.cpu0.name=Центральный процессор (ЦП) (1-ый уровень) -item.oc.cpu1.name=Центральный процессор (ЦП) (2-ой уровень) -item.oc.cpu2.name=Центральный процессор (ЦП) (3-ий уровень) -item.oc.cuttingwire.name=Проволока -item.oc.datacard0.name=Карта данных (1-ый уровень) -item.oc.datacard1.name=Карта данных (2-ой уровень) -item.oc.datacard2.name=Карта данных (3-ий уровень) -item.oc.debugcard.name=Отладочная карта -item.oc.debugger.name=Сетевой отладчик -item.oc.diamondchip.name=Алмазный обломок -item.oc.disk.name=Металлический диск -item.oc.diskdrivemountable.name=Дисковод (для серверной стойки) -item.oc.drone.name=Дрон -item.oc.dronecase0.name=Корпус дрона (1-ый уровень) -item.oc.dronecase1.name=Корпус дрона (2-ой уровень) -item.oc.dronecase3.name=Корпус дрона (Креатив) -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=Дискета -item.oc.graphicscard0.name=Видеокарта (1-ый уровень) -item.oc.graphicscard1.name=Видеокарта (2-ой уровень) -item.oc.graphicscard2.name=Видеокарта (3-ий уровень) -item.oc.harddiskdrive0.name=Жёсткий диск (1-ый уровень) -item.oc.harddiskdrive1.name=Жёсткий диск (2-ой уровень) -item.oc.harddiskdrive2.name=Жёсткий диск (3-ий уровень) -item.oc.hoverboots.name=Парящие ботинки -item.oc.inkcartridge.name=Картридж с чернилами -item.oc.inkcartridgeempty.name=Картридж с чернилами (Пустой) -item.oc.internetcard.name=Интернет карта -item.oc.interweb.name=Интерпаутина -item.oc.ironnugget.name=Железный самородок -item.oc.linkedcard.name=Соединённая карта -item.oc.manual.name=Руководство OpenComputers -item.oc.memory0.name=Память (1-ый уровень) -item.oc.memory1.name=Память (Уровень 1.5) -item.oc.memory2.name=Память (2-ой уровень) -item.oc.memory3.name=Память (Уровень 2.5) -item.oc.memory4.name=Память (3-ий уровень) -item.oc.memory5.name=Память (Уровень 3.5) -item.oc.microchip0.name=Микрочип (1-ый уровень) -item.oc.microchip1.name=Микрочип (2-ой уровень) -item.oc.microchip2.name=Микрочип (3-ий уровень) -item.oc.microcontrollercase0.name=Корпус микроконтроллера (1-ый уровень) -item.oc.microcontrollercase1.name=Корпус микроконтроллера (2-ой уровень) -item.oc.microcontrollercase3.name=Корпус микроконтроллера (Креатив) -item.oc.nanomachines.name=Нанороботы -item.oc.networkcard.name=Сетевая карта -item.oc.numpad.name=Цифровой блок клавиш -item.oc.present.name=Маленькое что-то... -item.oc.printedcircuitboard.name=Печатная плата -item.oc.rawcircuitboard.name=Основа для печатной платы -item.oc.redstonecard0.name=Плата на красном камне (1-ый уровень) -item.oc.redstonecard1.name=Плата на красном камне (2-ой уровень) -item.oc.server0.name=Сервер (1-ый уровень) -item.oc.server1.name=Сервер (2-ой уровень) -item.oc.server2.name=Сервер (3-ий уровень) -item.oc.server3.name=Сервер (Творческий) -item.oc.tablet.name=Планшет -item.oc.tabletcase0.name=Корпус планшета (1-ый уровень) -item.oc.tabletcase1.name=Корпус планшета (2-ой уровень) -item.oc.tabletcase3.name=Корпус планшета (Креатив) -item.oc.terminal.name=Беспроводной терминал -item.oc.terminalserver.name=Терминальный сервер -item.oc.texturepicker.name=Определитель текстур -item.oc.transistor.name=Транзистор -item.oc.upgradeangel.name="Ангельское" улучшение -item.oc.upgradebattery0.name=Улучшение "Ёмкость" (1-ый уровень) -item.oc.upgradebattery1.name=Улучшение "Ёмкость" (2-ой уровень) -item.oc.upgradebattery2.name=Улучшение "Ёмкость" (3-ий уровень) -item.oc.upgradechunkloader.name=Улучшение "Загрузчик чанков" -item.oc.upgradecontainercard0.name=Контейнер для карты (1-ый уровень) -item.oc.upgradecontainercard1.name=Контейнер для карты (2-ой уровень) -item.oc.upgradecontainercard2.name=Контейнер для карты (3-ий уровень) -item.oc.upgradecontainerupgrade0.name=Контейнер для улучшения (1-ый уровень) -item.oc.upgradecontainerupgrade1.name=Контейнер для улучшения (2-ой уровень) -item.oc.upgradecontainerupgrade2.name=Контейнер для улучшения (3-ий уровень) -item.oc.upgradecrafting.name=Улучшение "Верстак" -item.oc.upgradedatabase0.name=Улучшение "База данных" (1-ый уровень) -item.oc.upgradedatabase1.name=Улучшение "База данных" (2-ой уровень) -item.oc.upgradedatabase2.name=Улучшение "База данных" (3-ий уровень) -item.oc.upgradeexperience.name=Улучшение "Опыт" -item.oc.upgradegenerator.name=Улучшение "Генератор" -item.oc.upgradehover0.name=Улучшение "Парение" (1-ый уровень) -item.oc.upgradehover1.name=Улучшение "Парение" (2-ый уровень) -item.oc.upgradeinventory.name=Улучшение "Инвентарь" -item.oc.upgradeinventorycontroller.name=Улучшение "Контроллер инвентаря" -item.oc.upgradeleash.name=Улучшение "Поводок" -item.oc.upgrademf.name=МФУ -item.oc.upgradenavigation.name=Улучшение "Навигация" -item.oc.upgradepiston.name=Улучшение "Поршень" -item.oc.upgradesign.name=Улучшение "Контроллер табличек" -item.oc.upgradesolargenerator.name=Улучшение "Солнечный генератор" -item.oc.upgradetank.name=Улучшение "Бак для жидкостей" -item.oc.upgradetankcontroller.name=Улучшение "Контроллер бака" -item.oc.upgradetractorbeam.name=Улучшение "Притягивающий луч" -item.oc.upgradetrading.name=Улучшение "Торговля" -item.oc.wirelessnetworkcard0.name=Плата беспроводной сети (1-ый уровень) -item.oc.wirelessnetworkcard1.name=Плата беспроводной сети (2-ой уровень) -item.oc.worldsensorcard.name=Карта-мировой сенсор -item.oc.wrench.name=Ключ - -# Entities -entity.oc.Drone.name=Дрон - -# GUI -oc:gui.Analyzer.Address=§6Адрес§f: %s -oc:gui.Analyzer.AddressCopied=Адрес скопирован в буфер обмена. -oc:gui.Analyzer.ChargerSpeed=§6Скорость зарядки§f: %s -oc:gui.Analyzer.ComponentName=§6Имя компонента§f: %s -oc:gui.Analyzer.Components=§6Количество подключенных компонентов§f: %s -oc:gui.Analyzer.CopyToClipboard=Кликните для копирования в буфер обмена. -oc:gui.Analyzer.LastError=§6Последняя ошибка§f: %s -oc:gui.Analyzer.RobotName=§6Имя§f: %s -oc:gui.Analyzer.RobotOwner=§6Владелец§f: %s -oc:gui.Analyzer.RobotXp=§6Опыт§f: %s (Уровень: %s) -oc:gui.Analyzer.StoredEnergy=§6Накоплено энергии§f: %s -oc:gui.Analyzer.TotalEnergy=§6Всего накоплено энергии§f: %s -oc:gui.Analyzer.Users=§6Пользователи§f: %s -oc:gui.Analyzer.WirelessStrength=§6Уровень сигнала§f: %s -oc:gui.Assembler.Collect=Заберите результат сборки -oc:gui.Assembler.Complexity=Сложность: %s/%s -oc:gui.Assembler.InsertCase=Вставьте системный блок -oc:gui.Assembler.InsertCPU=Вставьте процессор -oc:gui.Assembler.InsertRAM=Вставьте память -oc:gui.Assembler.Progress=Прогресс: %s%% (%s) -oc:gui.Assembler.Run=Собрать -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Видеокарта -oc:gui.Assembler.Warning.Inventory=Улучшение "Инвентарь" -oc:gui.Assembler.Warning.Keyboard=Клавиатура -oc:gui.Assembler.Warning.OS=Загрузочный диск -oc:gui.Assembler.Warning.Screen=Монитор -oc:gui.Assembler.Warnings=§eВнимание§7: Отсутствуют рекомендуемые компоненты. -oc:gui.Chat.NewVersion=Доступна новая версия: %s -oc:gui.Chat.TextureName=§7Имя текстуры: §a%s§f. -oc:gui.Chat.WarningClassTransformer=Возникли §cошибки§f при преобразовании классов. Пожалуйста, сообщите об этом, а также приложите файл FML §alatest.log§f/§afml-server-latest.log§f! -oc:gui.Chat.WarningFingerprint=§cВНИМАНИЕ§f - несовпадение отпечатка файла! Ожидаемый: '§a%s§f', полученный: '§e%s§f'. Если Вы не разработчик, использующий деобфусцированную версию мода, то §lкрайне§f желательно заново скачать OpenComputers, поскольку JAR-файл, возможно, был изменён. -oc:gui.Chat.WarningLink=Невозможно открыть ссылку: %s -oc:gui.Chat.WarningLuaFallback=Исходные библиотеки Lua не доступны, компьютеры не смогут сохранять своё состояние. Они перезагрузятся после выгрузки чанка. -oc:gui.Chat.WarningProjectRed=Вы используете версию Project: Red, несовместимую с OpenComputers. Попробуйте обновить Project: Red. -oc:gui.Chat.WarningRecipes=Произошла ошибка при загрузке одного или нескольких рецептов. Некоторые предметы могут не крафтиться. Проверьте логи, чобы узнать подробнее. -oc:gui.Chat.WarningSimpleComponent=Некоторый мод §eневерно§f использует интрефейс §aSimpleComponent§f. Логика компонента не смогла быть внедрена. Проверьте логи, чобы узнать подробнее. -oc:gui.Drive.Managed=Файловый режим -oc:gui.Drive.Unmanaged=Блочный режим -oc:gui.Drive.Warning=§lВнимание§r: переключение режимов очищает диск! -oc:gui.Error.ComponentOverflow=Слишком много компонентов подключено к компьютеру. -oc:gui.Error.InternalError=Возникла внутренняя ошибка, пожалуйста, посмотрите лог-файл. Возможно, это баг. -oc:gui.Error.NoCPU=Не установлен процессор. -oc:gui.Error.NoEnergy=Недостаточно энергии. -oc:gui.Error.NoRAM=Не установлена память. -oc:gui.Error.OutOfMemory=Недостаточно памяти. -oc:gui.Manual.Blocks=Блоки OpenComputers -oc:gui.Manual.Home=Главная страница -oc:gui.Manual.Items=Предметы OpenComputers -oc:gui.Manual.Warning.BlockMissing=Блок недоступен. -oc:gui.Manual.Warning.ImageMissing=Изображение не найдено. -oc:gui.Manual.Warning.ItemMissing=Предмет недоступен. -oc:gui.Manual.Warning.OreDictMissing=Запись Ore Dictionary недоступна. -oc:gui.Raid.Warning=§4Добавление диска очистит его.[nl] Удаление диска очистит RAID. -oc:gui.Robot.Power=Энергия -oc:gui.Robot.TurnOff=Выключить -oc:gui.Robot.TurnOn=Включить -oc:gui.Rack.Back=Сзади -oc:gui.Rack.Bottom=Снизу -oc:gui.Rack.Left=Слева -oc:gui.Rack.None=Нет -oc:gui.Rack.Right=Справа -oc:gui.Rack.Enabled=Включено -oc:gui.Rack.Disabled=Выключено -oc:gui.Rack.Top=Сверху -oc:gui.Switch.TransferRate=Цикличность -oc:gui.Switch.PacketsPerCycle=Пакеты / цикл -oc:gui.Switch.QueueSize=Размер очереди -oc:gui.Terminal.InvalidKey=Неверный ключ, скорее всего, к серверу уже подключен другой терминал. -oc:gui.Terminal.OutOfRange=Нет сигнала. - -# Containers -oc:container.accesspoint=Точка доступа -oc:container.adapter=Адаптер -oc:container.case=Компьютер -oc:container.charger=Зарядное устройство -oc:container.disassembler=Разборщик -oc:container.diskdrive=Дисковод -oc:container.printer=Принтер -oc:container.raid=RAID -oc:container.relay=Ретранслятор -oc:container.server=Сервер -oc:container.rack=Серверная стойка -oc:container.switch=Коммутатор -oc:container.tabletwrapper=Планшет - -# Keybinds -key.clipboardPaste=Вставить из буфера обмена -key.materialCosts=Показать стоимость в материалах - -# Item / Block Tooltips -oc:tooltip.accesspoint=Работает как коммутатор, но и с беспроводными сетями. -oc:tooltip.abstractbuscard=Обеспечивает взаимодействие с абстрактной шиной из §fStargateTech 2§7 (отправка и приём LIP-пакетов). -oc:tooltip.acid=Высокотоксичная псевдо-жидкость, которую обычно пьют только определённые пираты. Однако может быть полезна и при других применениях. -oc:tooltip.adapter=Используется для контроля некомпонентных блоков, таких как обычные блоки или блоки из других модов. -oc:tooltip.alu=Выполняет арифметические вычисления, так что вам не придётся возиться с ними. -oc:tooltip.analyzer=Используется для отображения информации о блоках, такой как §fадрес§7 или §fимя компонента§7.[nl] Также отображает ошибку, вызвавшую сбой компьютера. -oc:tooltip.apu=Это процессор со встроенной видеокартой, полезный, когда вам требуется освободить ещё один слот для карт.[nl] Поддерживается компонентов: §f%s§7[nl] Максимальное разрешение: §f%sx%s§7[nl] Максимальная глубина цвета: §f%s§7[nl] Операций в тик: §f%s§7 -oc:tooltip.assembler=Позволяет собирать роботов и другие устройства из ряда различных частей компьютера. -oc:tooltip.cable=Простой и дешёвый способ соединить компоненты между собой. -oc:tooltip.capacitor=Накапливает энергию для дальнейшего использования. Может быть очень быстро заполнен и опустошён. -oc:tooltip.cardbase=Как видно из названия, это основа для всех карт расширения. -oc:tooltip.case=Системный блок - основа компьютера, содержащий §fкарты расширения§7, §fОЗУ§7 и §fжёсткие диски§7.[nl] Слотов: §f%s§7 -oc:tooltip.chamelium=Базовый материал для 3D печати. Не глотать: может привести к слепоте и временным отсутствием присутствия. -oc:tooltip.chameliumblock=Удобен для окрашенных 3D моделей или просто как цветной блок для вашей базы. -oc:tooltip.charger=Передаёт энергию из аккумуляторов вплотную находящимся роботам и дронам. Скорость передачи зависит от §fсилы сигнала красного камня§7: отсутствие сигнала означает "не заряжать", а полный сигнал - "заряжать на полной скорости". -oc:tooltip.circuitboard=Мы уже очень близки. Может быть вытравлена, чтобы получить печатную плату. -oc:tooltip.controlunit=Это штука, которая... контролирует... что-то. Она необходима, чтобы создать центральный процессор. Так что, да, она очень важна. -oc:tooltip.componentbus=Позволяет серверам взаимодействовать с большим количеством компонентов.[nl] Поддерживается компонентов: §f%s§7 -oc:tooltip.cpu=Является важным компонентом в компьютере. Тактовая частота немного нестабильна, но что вы ожидали, если он работает на карманных солнечных часах. Количество поддерживаемых компонентов: §f%s§7 -oc:tooltip.cpu.architecture=Архитектура: §f%s§7 -oc:tooltip.cuttingwire=Используется для нарезки глиняных блоков в пластины. Рвётся после использования, что делает её, пожалуй, самым неэффективным инструментом. -oc:tooltip.datacard0=Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как хеширование и сжатие. -oc:tooltip.datacard1=Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как AES-шифрование, хеширование и сжатие. -oc:tooltip.datacard2=Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как AES-шифрование, эллиптическая криптография, хеширование и сжатие. -oc:tooltip.debugcard=Креативный предмет, позволяет манипулировать игровым миром. Используйте на свой страх и риск. -oc:tooltip.debugger=Может быть использован для вывода отладочной информации о внутренней сети. Используйте только тогда, когда попросил разработчик. -oc:tooltip.diamondchip=Небольшой кусочек некогда сияющего алмаза. Он больше никогда не будет таким, как прежде. -oc:tooltip.disassembler=Разделяет предметы на исходные компоненты. §lВнимание§7: возвращённые предметы имеют шанс %s%% сломаться! -oc:tooltip.disk=Примитивный носитель, который может быть использован для создания постоянных запоминающих устройств. -oc:tooltip.diskdrive.cc=§aПоддерживаются§7 дискеты из ComputerCraft. -oc:tooltip.diskdrive=Позволяет читать и записывать дискеты. Может быть установлен в роботов, что позволит затем вставлять дискеты. -oc:tooltip.diskdrivemountable=Работает так же, как и обычный дисковод, но может быть вставлен только в серверную стойку. -oc:tooltip.diskusage=Занято %s/%s байт -oc:tooltip.diskmodemanaged=Режим: файловый -oc:tooltip.diskmodeunmanaged=Режим: блочный -oc:tooltip.drone=Дроны это легкие и быстрые устройства, но с ограниченным инвентарем. -oc:tooltip.dronecase=Корпус, предназначенный для создания дрона в сборщике. Вмещает малое количество компонентов и предоставляет левитацию с помощью силы камня края. -oc:tooltip.eeprom=Маленькое программируемое хранилище, которое содержит BIOS для запуска компьютеров. -oc:tooltip.fakeendstone=Почти такой же, как и настоящий камень края, даже эмулирует его легкость! -oc:tooltip.geolyzer=Позволяет сканировать твёрдость блоков в окрестности. Эта информация может быть полезна для создания голограммы области или для обнаружения руд. -oc:tooltip.graphicscard=Используется для изменения того, что отображается на мониторах.[nl] Максимальное разрешение: §f%sx%s§7[nl] Максимальная глубина цвета:§f%s§7[nl] Операций/такт: §f%s§7 -oc:tooltip.hoverboots=Прыгайте выше, падайте глубже, бегайте быстрее. Это и многое другое с нашими новыми Парящими ботинками (TM). -oc:tooltip.inkcartridge=Используется в 3D печати. По таинственным причинам ему не нужно оставаться в принтере. -oc:tooltip.inkcartridgeempty=Этот картридж выглядит пустым. Заправьте его с помощью красителей. Или выбросьте. -oc:tooltip.internetcard=Эта карта позволяет делать HTTP-запросы и использовать реальные TCP сокеты. -oc:tooltip.interweb=Поздравляем, вы выиграли одну (1) интерсеть. Подключиться к ней можно с помощью Интернет-платы. Осторожно: не кормите троллей. -oc:tooltip.ironnugget=Самородок, созданный из железа. Может, именно поэтому он и назван железным самородком? Хм... -oc:tooltip.keyboard=Присоединяется к монитору, позволяя вводить информацию. -oc:tooltip.hologram0=Объёмный дисплей, управляемый компьютером и использующийся для отображения произвольных воксельных структур.[nl] Разрешение: §f48x32x48§7 [nl] Максимальный масштаб: §f3x§7 [nl] Глубина цвета: §fМонохромный§7 -oc:tooltip.hologram1=Объёмный дисплей, управляемый компьютером и использующийся для отображения произвольных воксельных структур.[nl] Разрешение: §f48x32x48§7 [nl] Максимальный масштаб: §f4x§7 [nl] Глубина цвета: §fТрёхцветный§7 -oc:tooltip.linkedcard=Они изготавливаются в парах и могут общаться только с партнёром. Тем не менее, общаться они могут на любом расстоянии - даже между измерениями. Однако для отправки сообщения нужно довольно много энергии. -oc:tooltip.linkedcard_channel=§8Канал: %s§7 -oc:tooltip.manual=Содержит всю информацию о моде OpenComputers, которая вам может когда-либо понадобиться. И даже больше. По невероятно низкой цене... §oнажмите R для продолжения§7. -oc:tooltip.materialcosts=Удерживайте [§f%s§7] для показа материалов. -oc:tooltip.materials=Материалы: -oc:tooltip.memory=Необходима для запуска компьютера. Чем больше ОЗУ, тем более требовательные программы Вы можете запустить. -oc:tooltip.microchip=Чип, ранее известный как интегральная схема. Понятия не имею почему, но он работает с красным камнем. -oc:tooltip.microcontroller=Микроконтроллеры - это компьютеры, сведённые к самым базовым вещам. Они используются для выполнения специфических задач и выполняют программу, записанную на EEPROM.[nl] §cНе могут взаимодействовать с внешними компонентами.§7 -oc:tooltip.microcontrollercase=Базовый компонент для создания микроконтроллеров. Положите в сборщик, чтобы установить компоненты, и соберите микроконтроллер. -oc:tooltip.motionsensor=Может обнаружить движение ближайших живых существ. Требуется прямая линия видимости. -oc:tooltip.nanomachines=Блок управления и кучка нанороботов для приема внутрь, если вы решились. -oc:tooltip.networkcard=Позволяет обмениваться сообщениями между соединённым друг с другом - с помощью кабеля или других блоков - компьютерами. -oc:tooltip.poweracceptor=Скорость преобразования энергии: §f%s/t§7 -oc:tooltip.powerconverter.buildcraft=§fBuildCraft MJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.factorization=§fFactorization Charge§7: §a%s:%s§7 -oc:tooltip.powerconverter.industrialcraft2=§fIndustrialCraft² EU§7: §a%s:%s§7 -oc:tooltip.powerconverter.mekanism=§fMekanism Joules§7: §a%s:%s§7 -oc:tooltip.powerconverter.thermalexpansion=§fThermal Expansion RF§7: §a%s:%s§7 -oc:tooltip.powerconverter.resonantengine=§fResonant Engine Coulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=Преобразует энергию из других модов во внутренний тип энергии. Соотношение энергии: -oc:tooltip.powerdistributor=Передаёт энергию между различными сетями. Полезно, если требуется запитать от одного преобразователя несколько сетей, которые должны оставаться разделёнными. -oc:tooltip.present=Откройте этот подарок, чтобы получить немного §kчего-то§7![nl]§8Крафтите предметы OpenComputers в нужное время, чтобы получить шанс получить подарок.§7 -oc:tooltip.print.beaconbase=§8Используется в качестве основы для маяка. -oc:tooltip.print.lightvalue=§8Сила излучаемого света: %s. -oc:tooltip.print.redstonelevel=§8Сила выдаваемого красного сигнала: %s. -oc:tooltip.printedcircuitboard=Основа плат, памяти и прочего. -oc:tooltip.printer=Позволяет печатать пользовательские 3D-модели, используя хамелиум и картриджи с чернилами. Должен быть настроен с помощью компьютера. Держите подальше от детей. -oc:tooltip.raid=Позволяет соединять три жестких диска в единую файловую систему, которую могут использовать все подключенные компьютеры. -oc:tooltip.rawcircuitboard=Может быть закалена в печи для получения печатной платы. -oc:tooltip.redstone=Позволяет считывать и подавать сигналы красного камня вокруг блока. Контролируется любым подключенным компьютером. Иными словами, это что-то вроде внешней платы на красном камне. -oc:tooltip.redstonecard.projectred=Мод §fProjectRed§7 §aподдерживается§7. -oc:tooltip.redstonecard.redlogic=Мод §fRedLogic§7 §aподдерживается§7. -oc:tooltip.redstonecard.rednet=Мод §fRedNet§7 §aподдерживается§7. -oc:tooltip.redstonecard.wirelesscbe=Мод §fWireless Redstone (ChickenBones)§7 §aподдерживается§7. -oc:tooltip.redstonecard.wirelesssv=Мод §fWireless Redstone (SlimeVoid)§7 §aподдерживается§7. -oc:tooltip.redstonecard=Позволяет считывать и подавать сигналы красного камня вокруг компьютера или робота. -oc:tooltip.relay=Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями не будут доступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними. -oc:tooltip.robot=В отличие от компьютеров, роботы могут передвигаться и взаимодействовать с миром, как игрок.[nl] §cНе могут подключаться к внешним компонентам.§7 -# The underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fУровень§7: §a%s§7. -oc:tooltip.robot_storedenergy=§fНакопленная энергия§7: §a%s§7. -oc:tooltip.screen=Отображает текст, передаваемый видеокартой.[nl] Максимальное разрешение: §f%sx%s§7[nl] Максимальная глубина цвета: §f%s§7 -oc:tooltip.server=Это сервер. Есть много других, похожих на него, но только этот может быть улучшен компонентами, как системный блок. Можно включить после размещения в серверную стойку. -oc:tooltip.server.components=Установленные компоненты: -oc:tooltip.rack=Обеспечивает работу до четырёх серверов или других монтируемых компонентов. -oc:tooltip.switch=Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями не будут доступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними. -oc:tooltip.tablet=Планшет, полностью готовый к работе с Lua. Может быть отключен нажатием правой кнопки мыши + Shift. -oc:tooltip.tabletcase=Простой корпус для планшета. Поместите в сборщик, чтобы вставить компоненты, и соберите себе планшетный компьютер. -oc:tooltip.terminal=Позволяет дистанционно управлять сервером, пока вы находитесь в радиусе его действия. Действует как портативный дисплей с клавиатурой.[nl] Shift+ПКМ по серверу терминалов в стойке для привязки. -oc:tooltip.terminalserver=Компонент, к которому можно подключить беспроводной терминал. Имеет виртуальный экран и клавиатуру. -oc:tooltip.texturepicker=Простой инструмент, позволяющий узнать название текстуры блока, которое можно использовать в 3D печати. -oc:tooltip.tier=§8Уровень %s -oc:tooltip.netsplitter=Работает как переключатель. Соединение каждой стороны переключается ключем. При подаче сигнала красного камня все соединения инвертируются. -oc:tooltip.toolong=Удерживайте [§f%s§7], чтобы отобразить описание. -oc:tooltip.transistor=Базовый элемент для большинства частей компьютера. Он немного деформирован, но отлично выполняет свою работу. -oc:tooltip.transposer=Позволяет автоматизировать перемещение предметов и жидкостей между соседними инвентарями и хранилищами жидкости. -oc:tooltip.upgradeangel=Позволяет роботам размещать блоки в воздухе без точки опоры. -oc:tooltip.upgradebattery=Увеличивает количество энергии, которую робот может хранить, что позволяет ему работать дольше без перезарядки. -oc:tooltip.upgradechunkloader=Если робот движется в лесу и никто его не видит, действительно ли он движется? Данное улучшение гарантирует это. Оно держит чанк, в котором находится робот, загруженным, но постоянно потребляет энергию во время работы. -oc:tooltip.upgradecontainercard=Это улучшение-контейнер позволяет динамически устанавливать и снимать плату с собранного робота. [nl] Максимальный уровень: §f%s§7 -oc:tooltip.upgradecontainerupgrade=Это улучшение-контейнер позволяет динамически устанавливать и снимать другое улучшение с собранного робота. [nl] Максимальный уровень: §f%s§7 -oc:tooltip.upgradecrafting=Позволяет роботам использовать верхнюю левую часть инвентаря для крафта предметов. Вещи должны быть расположены в том же порядке, что и в верстаке. -oc:tooltip.upgradedatabase=Позволяет хранить информацию о предметах для последующего использования другими компонентами.[nl] Поддерживается записей: §f%s§7 -oc:tooltip.upgradeexperience=Это улучшение позволяет роботу накапливать опыт при выполнении различных операций. Чем больше у них опыта, тем больше энергии они могут хранить, могут быстрее собирать блоки и эффективнее использовать инструменты. -oc:tooltip.upgradegenerator=Позволяет роботам генерировать энергию из сжигаемого топлива прямо на ходу.[nl] §fЭффективность§7: §a%s%%§7 -oc:tooltip.upgradehover=Данное улучшение позволяет роботам летать выше над землей без необходимости лазать по стенам.[nl] Максимальная высота: §f%s§7 -oc:tooltip.upgradeinventory=Это улучшение даёт место в инвентаре для робота. Без него роботы не смогут хранить предметы внутри. -oc:tooltip.upgradeinventorycontroller=Это улучшение позволяет роботу контролировать взаимодействие с внешними инвентарями и менять свой экипированный инструмент предметом из его инвентаря. -oc:tooltip.upgradeleash=Позволяет некоторым устройствам, таким как дроны, цеплять животных на поводок. Много животных, при этом. -oc:tooltip.upgrademf=Позволяет адаптерам взаимодействовать с блоками на удалении от них. -oc:tooltip.upgrademf.linked=§fСоединение установлено§7 -oc:tooltip.upgrademf.unlinked=§fНет соединения§7 -oc:tooltip.upgradenavigation=Позволяет определять положение и ориентацию робота. Положение определяется относительно центра карты, использованной при крафте улучшения. -oc:tooltip.upgradepiston=Позволяет двигать блоки, как поршни. §lНе§7 может двигать животных, однако. -oc:tooltip.upgradesign=Позволяет читать и писать текст на табличках. -oc:tooltip.upgradesolargenerator=Позволяет роботам генерировать энергию из солнечного света прямо на ходу. Требует прямую видимость неба над роботом. Генерирует энергию на скорости %s%% от двигателя Стирлинга. -oc:tooltip.upgradetank=Позволяет роботам и дронам взаимодействовать с жидкостями. Без него они не смогут хранить жидкость. -oc:tooltip.upgradetankcontroller=Позволяет роботам и дронам взамодействовать в внешними хранилищами жидкости, а также заливать и выливать жидкости в них. -oc:tooltip.upgradetractorbeam=Даёт роботу самую невероятно продвинутую технологию под кодовым названием "Магнит для предметов". Позволяет роботу подбирать предметы в радиусе 3 блоков от своего расположения. -oc:tooltip.waypoint=Добавляет путевую точку для устройств с навигационным улучшением. -oc:tooltip.wirelessnetworkcard=Позволяет передавать сетевые сообщения по беспроводному каналу в дополнение к проводному. Вы можете настроить §fсилу сигнала§7, чтобы контролировать, насколько далеко должны отправляться сообщения. Более высокая сила сигнала приводит к повышенному потреблению энергии. -oc:tooltip.worldsensorcard=Позволяет получать информацию об атмосфере и гравитации на планетах, добавляемых модом GalactiCraft. Используйте на свой страх и риск. Производитель не несет ответственности за материальный ущерб и увечья. У нас есть адвокаты. И деньги. Даже не пытайтесь. -oc:tooltip.wrench=Гибрид отвертки и ключа. - -#Achievements -achievement.oc.adapter=Мы едины -achievement.oc.adapter.desc=Дайте поработать с другими модами! -achievement.oc.assembler=Великолепно -achievement.oc.assembler.desc=Время захватывать мир! -achievement.oc.cable=Не трогай провода! -achievement.oc.cable.desc=... они от этого ржавеют -achievement.oc.capacitor=Батарея в комплекте -achievement.oc.capacitor.desc=Вы не можете остановить это. -achievement.oc.card=Мы принимаем карточки -achievement.oc.card.desc=Для вашего удобства. Вам это понравится. -achievement.oc.case=В случае возникновения неполадок -achievement.oc.case.desc=Открывайте боковую крышку летом. -achievement.oc.charger=Ладно, сделаем это -achievement.oc.charger.desc=Пошла зарядка.. черт, опять забыл редстоун сигнал. -achievement.oc.chip=Полезные мелочи -achievement.oc.chip.desc=Ведь электронные лампы - прошлый век -achievement.oc.cpu=Разогнанный -achievement.oc.cpu.desc=Время нагрузить его по полной. -achievement.oc.disassembler=Больше не нужно -achievement.oc.disassembler.desc=Когда великолепные идеи оказываются не такими уж и великолепными -achievement.oc.diskDrive=Карусель -achievement.oc.diskDrive.desc=Небольшая емкость, зато какой звук! -achievement.oc.drone=Вездесущее око -achievement.oc.drone.desc=Небольшой брат следит за тобой. -achievement.oc.eeprom=Он может быть только один -achievement.oc.eeprom.desc=Для каждого компьютера, то есть. Для загрузки компьютера, вы знали? -achievement.oc.floppy=Не подносить магнит -achievement.oc.floppy.desc=Дискеты не любят магниты. -achievement.oc.geolyzer=Назад на землю -achievement.oc.geolyzer.desc=Он имеет необычные свойства. -achievement.oc.graphicsCard=Последнее поколение -achievement.oc.graphicsCard.desc=Покажи это. -achievement.oc.hdd=Продавец хотдогов -achievement.oc.hdd.desc=Нет, подождите, это не то, о чем вы подумали. Не роняйте его... -achievement.oc.hologram=Гиперпространство -achievement.oc.hologram.desc=Когда двух измерений уже не хватает -achievement.oc.keyboard=Крошкохранитель-3000 -achievement.oc.keyboard.desc=Не рекомендуется переворачивать и трясти его -achievement.oc.microcontroller=Маленькая сестренка -achievement.oc.microcontroller.desc=Просто маленький компьютер -achievement.oc.motionSensor=Оно двигается -achievement.oc.motionSensor.desc=Как Майкл Джексон. -achievement.oc.networkCard=Нам надо поговорить! -achievement.oc.networkCard.desc=Оставайтесь на связи с дальними родственниками. -achievement.oc.openOS=Поехали -achievement.oc.openOS.desc=Неужели он включится. -achievement.oc.powerDistributor=Поделись с ближним -achievement.oc.powerDistributor.desc=Когда вы помогаете распределять энергию. -achievement.oc.rack=Прямо в стойку -achievement.oc.rack.desc=Не знаю, о чем думаете вы, я думал о серверной стойке. -achievement.oc.raid=Чрезмерная избыточность -achievement.oc.raid.desc=Только диски не вынимайте. -achievement.oc.ram=Случайный доступ к памяти -achievement.oc.ram.desc=Поздравляем, вы делаете это правильно. -achievement.oc.redstoneCard=Есть контакт -achievement.oc.redstoneCard.desc=Возврат к аналоговой технике. -achievement.oc.redstoneIO=Выскочка -achievement.oc.redstoneIO.desc=Когда все сигналы в нужных местах -achievement.oc.robot=Бип-пип -achievement.oc.robot.desc=УНИЧТОЖИТЬ! -achievement.oc.screen=Вы пробовали выключить и снова включить? -achievement.oc.screen.desc=Серьезно. Редстоун сигнал может выключать монитор. -achievement.oc.server=Выделенный сервер -achievement.oc.server.desc=Облачные вычисления, мы идем к вам. -achievement.oc.switch=Сложная топология -achievement.oc.switch.desc=Избегайте сложных связей и потери пакетов. -achievement.oc.tablet=Несъедобно -achievement.oc.tablet.desc=Держите подальше от маленьких детей, во избежание пропажи денег на вашей кредитной карте. -achievement.oc.transistor=Редстоун, скажи "Привет." -achievement.oc.transistor.desc=Создайте транзистор. Потом послушайте музыку. Не благодарите меня. -achievement.oc.wirelessNetworkCard=Сигналы -achievement.oc.wirelessNetworkCard.desc=Время идти туда, где никогда не появлялся сигнал. - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s был слишком жадным. -death.attack.oc.nanomachinesOverload.2=%s получил нервный срыв. -death.attack.oc.nanomachinesOverload.3=Нанороботы %s вышли из под контроля. -death.attack.oc.nanomachinesHungry.1=%s был съеден нанороботами. -death.attack.oc.nanomachinesHungry.2=%s не кормил нанороботов. -death.attack.oc.nanomachinesHungry.3=%s был переварен нанороботами. - -# NEI Integration -nei.options.inventory.oredict=Показывать имена Ore Dictionary -nei.options.inventory.oredict.true=Да -nei.options.inventory.oredict.false=Нет -nei.usage.oc.Manual=Открыть руководство - -# Waila Integration -option.oc.address=Адрес -option.oc.componentName=Название компонента -option.oc.energy=Энергия diff --git a/src/main/resources/assets/opencomputers/lang/ru_ru.json b/src/main/resources/assets/opencomputers/lang/ru_ru.json new file mode 100644 index 0000000000..ed534742d1 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/ru_ru.json @@ -0,0 +1,457 @@ +{ + "tile.oc.accesspoint": "§cТочка доступа§7", + "tile.oc.adapter": "Адаптер", + "tile.oc.assembler": "Сборщик", + "tile.oc.cable": "Кабель", + "tile.oc.capacitor": "Конденсатор энергии", + "tile.oc.case1": "Системный блок (1-ый уровень)", + "tile.oc.case2": "Системный блок (2-ой уровень)", + "tile.oc.case3": "Системный блок (3-ий уровень)", + "tile.oc.casecreative": "Системный блок (Творческий)", + "tile.oc.chameliumblock": "Блок хамелиума", + "tile.oc.charger": "Зарядное устройство", + "tile.oc.disassembler": "Разборщик", + "tile.oc.diskdrive": "Дисковод", + "tile.oc.endstone": "Камень края", + "tile.oc.geolyzer": "Геоанализатор", + "tile.oc.hologram1": "Голографический проектор (1-ый уровень)", + "tile.oc.hologram2": "Голографический проектор (2-ой уровень)", + "tile.oc.keyboard": "Клавиатура", + "tile.oc.microcontroller": "Микроконтроллер", + "tile.oc.motionsensor": "Датчик движения", + "tile.oc.netsplitter": "Сетевой переключатель", + "tile.oc.powerconverter": "Преобразователь энергии", + "tile.oc.powerdistributor": "Распределитель энергии", + "tile.oc.print": "3D модель", + "tile.oc.printer": "3D принтер", + "tile.oc.raid": "RAID", + "tile.oc.redstone": "Красный контроллер", + "tile.oc.relay": "Ретранслятор", + "tile.oc.robot": "Робот", + "tile.oc.robotafterimage": "Робот", + "tile.oc.screen1": "Монитор (1-ый уровень)", + "tile.oc.screen2": "Монитор (2-ой уровень)", + "tile.oc.screen3": "Монитор (3-ий уровень)", + "tile.oc.rack": "Серверная стойка", + "tile.oc.switch": "§cКоммутатор§7", + "tile.oc.transposer": "Транспозер", + "tile.oc.waypoint": "Путевая точка", + "item.oc.abstractbuscard": "Карта абстрактной шины", + "item.oc.acid": "Кислота", + "item.oc.alu": "Арифметико-логическое устройство (АЛУ)", + "item.oc.analyzer": "Анализатор", + "item.oc.apu0": "Процессор с видеокартой (APU) (1-ый уровень)", + "item.oc.apu1": "Процессор с видеокартой (APU) (2-ой уровень)", + "item.oc.apu2": "Процессор с видеокартой (APU) (Творческий)", + "item.oc.arrowkeys": "Клавиши со стрелками", + "item.oc.buttongroup": "Группа кнопок", + "item.oc.cardbase": "Основа для карт", + "item.oc.chamelium": "Хамелиум", + "item.oc.circuitboard": "Нетравленая печатная плата", + "item.oc.componentbus0": "Компонентная шина (1-ый уровень)", + "item.oc.componentbus1": "Компонентная шина (2-ой уровень)", + "item.oc.componentbus2": "Компонентная шина (3-ий уровень)", + "item.oc.controlunit": "Устройство управления (УУ)", + "item.oc.cpu0": "Центральный процессор (ЦП) (1-ый уровень)", + "item.oc.cpu1": "Центральный процессор (ЦП) (2-ой уровень)", + "item.oc.cpu2": "Центральный процессор (ЦП) (3-ий уровень)", + "item.oc.cuttingwire": "Проволока", + "item.oc.datacard0": "Карта данных (1-ый уровень)", + "item.oc.datacard1": "Карта данных (2-ой уровень)", + "item.oc.datacard2": "Карта данных (3-ий уровень)", + "item.oc.debugcard": "Отладочная карта", + "item.oc.debugger": "Сетевой отладчик", + "item.oc.diamondchip": "Алмазный обломок", + "item.oc.disk": "Металлический диск", + "item.oc.diskdrivemountable": "Дисковод (для серверной стойки)", + "item.oc.drone": "Дрон", + "item.oc.dronecase0": "Корпус дрона (1-ый уровень)", + "item.oc.dronecase1": "Корпус дрона (2-ой уровень)", + "item.oc.dronecase3": "Корпус дрона (Креатив)", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "Дискета", + "item.oc.graphicscard0": "Видеокарта (1-ый уровень)", + "item.oc.graphicscard1": "Видеокарта (2-ой уровень)", + "item.oc.graphicscard2": "Видеокарта (3-ий уровень)", + "item.oc.harddiskdrive0": "Жёсткий диск (1-ый уровень)", + "item.oc.harddiskdrive1": "Жёсткий диск (2-ой уровень)", + "item.oc.harddiskdrive2": "Жёсткий диск (3-ий уровень)", + "item.oc.hoverboots": "Парящие ботинки", + "item.oc.inkcartridge": "Картридж с чернилами", + "item.oc.inkcartridgeempty": "Картридж с чернилами (Пустой)", + "item.oc.internetcard": "Интернет карта", + "item.oc.interweb": "Интерпаутина", + "item.oc.ironnugget": "Железный самородок", + "item.oc.linkedcard": "Соединённая карта", + "item.oc.manual": "Руководство OpenComputers", + "item.oc.memory0": "Память (1-ый уровень)", + "item.oc.memory1": "Память (Уровень 1.5)", + "item.oc.memory2": "Память (2-ой уровень)", + "item.oc.memory3": "Память (Уровень 2.5)", + "item.oc.memory4": "Память (3-ий уровень)", + "item.oc.memory5": "Память (Уровень 3.5)", + "item.oc.microchip0": "Микрочип (1-ый уровень)", + "item.oc.microchip1": "Микрочип (2-ой уровень)", + "item.oc.microchip2": "Микрочип (3-ий уровень)", + "item.oc.microcontrollercase0": "Корпус микроконтроллера (1-ый уровень)", + "item.oc.microcontrollercase1": "Корпус микроконтроллера (2-ой уровень)", + "item.oc.microcontrollercase3": "Корпус микроконтроллера (Креатив)", + "item.oc.nanomachines": "Нанороботы", + "item.oc.networkcard": "Сетевая карта", + "item.oc.numpad": "Цифровой блок клавиш", + "item.oc.present": "Маленькое что-то...", + "item.oc.printedcircuitboard": "Печатная плата", + "item.oc.rawcircuitboard": "Основа для печатной платы", + "item.oc.redstonecard0": "Плата на красном камне (1-ый уровень)", + "item.oc.redstonecard1": "Плата на красном камне (2-ой уровень)", + "item.oc.server0": "Сервер (1-ый уровень)", + "item.oc.server1": "Сервер (2-ой уровень)", + "item.oc.server2": "Сервер (3-ий уровень)", + "item.oc.server3": "Сервер (Творческий)", + "item.oc.tablet": "Планшет", + "item.oc.tabletcase0": "Корпус планшета (1-ый уровень)", + "item.oc.tabletcase1": "Корпус планшета (2-ой уровень)", + "item.oc.tabletcase3": "Корпус планшета (Креатив)", + "item.oc.terminal": "Беспроводной терминал", + "item.oc.terminalserver": "Терминальный сервер", + "item.oc.texturepicker": "Определитель текстур", + "item.oc.transistor": "Транзистор", + "item.oc.upgradeangel": "\"Ангельское\" улучшение", + "item.oc.upgradebattery0": "Улучшение \"Ёмкость\" (1-ый уровень)", + "item.oc.upgradebattery1": "Улучшение \"Ёмкость\" (2-ой уровень)", + "item.oc.upgradebattery2": "Улучшение \"Ёмкость\" (3-ий уровень)", + "item.oc.upgradechunkloader": "Улучшение \"Загрузчик чанков\"", + "item.oc.upgradecontainercard0": "Контейнер для карты (1-ый уровень)", + "item.oc.upgradecontainercard1": "Контейнер для карты (2-ой уровень)", + "item.oc.upgradecontainercard2": "Контейнер для карты (3-ий уровень)", + "item.oc.upgradecontainerupgrade0": "Контейнер для улучшения (1-ый уровень)", + "item.oc.upgradecontainerupgrade1": "Контейнер для улучшения (2-ой уровень)", + "item.oc.upgradecontainerupgrade2": "Контейнер для улучшения (3-ий уровень)", + "item.oc.upgradecrafting": "Улучшение \"Верстак\"", + "item.oc.upgradedatabase0": "Улучшение \"База данных\" (1-ый уровень)", + "item.oc.upgradedatabase1": "Улучшение \"База данных\" (2-ой уровень)", + "item.oc.upgradedatabase2": "Улучшение \"База данных\" (3-ий уровень)", + "item.oc.upgradeexperience": "Улучшение \"Опыт\"", + "item.oc.upgradegenerator": "Улучшение \"Генератор\"", + "item.oc.upgradehover0": "Улучшение \"Парение\" (1-ый уровень)", + "item.oc.upgradehover1": "Улучшение \"Парение\" (2-ый уровень)", + "item.oc.upgradeinventory": "Улучшение \"Инвентарь\"", + "item.oc.upgradeinventorycontroller": "Улучшение \"Контроллер инвентаря\"", + "item.oc.upgradeleash": "Улучшение \"Поводок\"", + "item.oc.upgrademf": "МФУ", + "item.oc.upgradenavigation": "Улучшение \"Навигация\"", + "item.oc.upgradepiston": "Улучшение \"Поршень\"", + "item.oc.upgradesign": "Улучшение \"Контроллер табличек\"", + "item.oc.upgradesolargenerator": "Улучшение \"Солнечный генератор\"", + "item.oc.upgradetank": "Улучшение \"Бак для жидкостей\"", + "item.oc.upgradetankcontroller": "Улучшение \"Контроллер бака\"", + "item.oc.upgradetractorbeam": "Улучшение \"Притягивающий луч\"", + "item.oc.upgradetrading": "Улучшение \"Торговля\"", + "item.oc.wirelessnetworkcard0": "Плата беспроводной сети (1-ый уровень)", + "item.oc.wirelessnetworkcard1": "Плата беспроводной сети (2-ой уровень)", + "item.oc.worldsensorcard": "Карта-мировой сенсор", + "item.oc.wrench": "Ключ", + "entity.oc.Drone.name": "Дрон", + "oc:gui.Analyzer.Address": "§6Адрес§f: %s", + "oc:gui.Analyzer.AddressCopied": "Адрес скопирован в буфер обмена.", + "oc:gui.Analyzer.ChargerSpeed": "§6Скорость зарядки§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Имя компонента§f: %s", + "oc:gui.Analyzer.Components": "§6Количество подключенных компонентов§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "Кликните для копирования в буфер обмена.", + "oc:gui.Analyzer.LastError": "§6Последняя ошибка§f: %s", + "oc:gui.Analyzer.RobotName": "§6Имя§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Владелец§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Опыт§f: %s (Уровень: %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Накоплено энергии§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Всего накоплено энергии§f: %s", + "oc:gui.Analyzer.Users": "§6Пользователи§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Уровень сигнала§f: %s", + "oc:gui.Assembler.Collect": "Заберите результат сборки", + "oc:gui.Assembler.Complexity": "Сложность: %s/%s", + "oc:gui.Assembler.InsertCase": "Вставьте системный блок", + "oc:gui.Assembler.InsertCPU": "Вставьте процессор", + "oc:gui.Assembler.InsertRAM": "Вставьте память", + "oc:gui.Assembler.Progress": "Прогресс: %s%% (%s)", + "oc:gui.Assembler.Run": "Собрать", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Видеокарта", + "oc:gui.Assembler.Warning.Inventory": "Улучшение \"Инвентарь\"", + "oc:gui.Assembler.Warning.Keyboard": "Клавиатура", + "oc:gui.Assembler.Warning.OS": "Загрузочный диск", + "oc:gui.Assembler.Warning.Screen": "Монитор", + "oc:gui.Assembler.Warnings": "§eВнимание§7: Отсутствуют рекомендуемые компоненты.", + "oc:gui.Chat.NewVersion": "Доступна новая версия: %s", + "oc:gui.Chat.TextureName": "§7Имя текстуры: §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "Возникли §cошибки§f при преобразовании классов. Пожалуйста, сообщите об этом, а также приложите файл FML §alatest.log§f/§afml-server-latest.log§f!", + "oc:gui.Chat.WarningFingerprint": "§cВНИМАНИЕ§f - несовпадение отпечатка файла! Ожидаемый: '§a%s§f', полученный: '§e%s§f'. Если Вы не разработчик, использующий деобфусцированную версию мода, то §lкрайне§f желательно заново скачать OpenComputers, поскольку JAR-файл, возможно, был изменён.", + "oc:gui.Chat.WarningLink": "Невозможно открыть ссылку: %s", + "oc:gui.Chat.WarningLuaFallback": "Исходные библиотеки Lua не доступны, компьютеры не смогут сохранять своё состояние. Они перезагрузятся после выгрузки чанка.", + "oc:gui.Chat.WarningProjectRed": "Вы используете версию Project: Red, несовместимую с OpenComputers. Попробуйте обновить Project: Red.", + "oc:gui.Chat.WarningRecipes": "Произошла ошибка при загрузке одного или нескольких рецептов. Некоторые предметы могут не крафтиться. Проверьте логи, чобы узнать подробнее.", + "oc:gui.Chat.WarningSimpleComponent": "Некоторый мод §eневерно§f использует интрефейс §aSimpleComponent§f. Логика компонента не смогла быть внедрена. Проверьте логи, чобы узнать подробнее.", + "oc:gui.Drive.Managed": "Файловый режим", + "oc:gui.Drive.Unmanaged": "Блочный режим", + "oc:gui.Drive.Warning": "§lВнимание§r: переключение режимов очищает диск!", + "oc:gui.Error.ComponentOverflow": "Слишком много компонентов подключено к компьютеру.", + "oc:gui.Error.InternalError": "Возникла внутренняя ошибка, пожалуйста, посмотрите лог-файл. Возможно, это баг.", + "oc:gui.Error.NoCPU": "Не установлен процессор.", + "oc:gui.Error.NoEnergy": "Недостаточно энергии.", + "oc:gui.Error.NoRAM": "Не установлена память.", + "oc:gui.Error.OutOfMemory": "Недостаточно памяти.", + "oc:gui.Manual.Blocks": "Блоки OpenComputers", + "oc:gui.Manual.Home": "Главная страница", + "oc:gui.Manual.Items": "Предметы OpenComputers", + "oc:gui.Manual.Warning.BlockMissing": "Блок недоступен.", + "oc:gui.Manual.Warning.ImageMissing": "Изображение не найдено.", + "oc:gui.Manual.Warning.ItemMissing": "Предмет недоступен.", + "oc:gui.Manual.Warning.OreDictMissing": "Запись Ore Dictionary недоступна.", + "oc:gui.Raid.Warning": "§4Добавление диска очистит его.\nУдаление диска очистит RAID.", + "oc:gui.Robot.Power": "Энергия", + "oc:gui.Robot.TurnOff": "Выключить", + "oc:gui.Robot.TurnOn": "Включить", + "oc:gui.Rack.Back": "Сзади", + "oc:gui.Rack.Bottom": "Снизу", + "oc:gui.Rack.Left": "Слева", + "oc:gui.Rack.None": "Нет", + "oc:gui.Rack.Right": "Справа", + "oc:gui.Rack.Enabled": "Включено", + "oc:gui.Rack.Disabled": "Выключено", + "oc:gui.Rack.Top": "Сверху", + "oc:gui.Switch.TransferRate": "Цикличность", + "oc:gui.Switch.PacketsPerCycle": "Пакеты / цикл", + "oc:gui.Switch.QueueSize": "Размер очереди", + "oc:gui.Terminal.InvalidKey": "Неверный ключ, скорее всего, к серверу уже подключен другой терминал.", + "oc:gui.Terminal.OutOfRange": "Нет сигнала.", + "oc:container.accesspoint": "Точка доступа", + "oc:container.adapter": "Адаптер", + "oc:container.case": "Компьютер", + "oc:container.charger": "Зарядное устройство", + "oc:container.disassembler": "Разборщик", + "oc:container.diskdrive": "Дисковод", + "oc:container.printer": "Принтер", + "oc:container.raid": "RAID", + "oc:container.relay": "Ретранслятор", + "oc:container.server": "Сервер", + "oc:container.rack": "Серверная стойка", + "oc:container.switch": "Коммутатор", + "oc:container.tabletwrapper": "Планшет", + "key.opencomputers.clipboardPaste": "Вставить из буфера обмена", + "key.opencomputers.materialCosts": "Показать стоимость в материалах", + "oc:tooltip.accesspoint": "Работает как коммутатор, но и с беспроводными сетями.", + "oc:tooltip.abstractbuscard": "Обеспечивает взаимодействие с абстрактной шиной из §fStargateTech 2§7 (отправка и приём LIP-пакетов).", + "oc:tooltip.acid": "Высокотоксичная псевдо-жидкость, которую обычно пьют только определённые пираты. Однако может быть полезна и при других применениях.", + "oc:tooltip.adapter": "Используется для контроля некомпонентных блоков, таких как обычные блоки или блоки из других модов.", + "oc:tooltip.alu": "Выполняет арифметические вычисления, так что вам не придётся возиться с ними.", + "oc:tooltip.analyzer": "Используется для отображения информации о блоках, такой как §fадрес§7 или §fимя компонента§7.\nТакже отображает ошибку, вызвавшую сбой компьютера.", + "oc:tooltip.apu": "Это процессор со встроенной видеокартой, полезный, когда вам требуется освободить ещё один слот для карт.\nПоддерживается компонентов: §f%s§7\nМаксимальное разрешение: §f%sx%s§7\nМаксимальная глубина цвета: §f%s§7\nОпераций в тик: §f%s§7", + "oc:tooltip.assembler": "Позволяет собирать роботов и другие устройства из ряда различных частей компьютера.", + "oc:tooltip.cable": "Простой и дешёвый способ соединить компоненты между собой.", + "oc:tooltip.capacitor": "Накапливает энергию для дальнейшего использования. Может быть очень быстро заполнен и опустошён.", + "oc:tooltip.cardbase": "Как видно из названия, это основа для всех карт расширения.", + "oc:tooltip.case": "Системный блок - основа компьютера, содержащий §fкарты расширения§7, §fОЗУ§7 и §fжёсткие диски§7.\nСлотов: §f%s§7", + "oc:tooltip.chamelium": "Базовый материал для 3D печати. Не глотать: может привести к слепоте и временным отсутствием присутствия.", + "oc:tooltip.chameliumblock": "Удобен для окрашенных 3D моделей или просто как цветной блок для вашей базы.", + "oc:tooltip.charger": "Передаёт энергию из аккумуляторов вплотную находящимся роботам и дронам. Скорость передачи зависит от §fсилы сигнала красного камня§7: отсутствие сигнала означает \"не заряжать\", а полный сигнал - \"заряжать на полной скорости\".", + "oc:tooltip.circuitboard": "Мы уже очень близки. Может быть вытравлена, чтобы получить печатную плату.", + "oc:tooltip.controlunit": "Это штука, которая... контролирует... что-то. Она необходима, чтобы создать центральный процессор. Так что, да, она очень важна.", + "oc:tooltip.componentbus": "Позволяет серверам взаимодействовать с большим количеством компонентов.\nПоддерживается компонентов: §f%s§7", + "oc:tooltip.cpu": "Является важным компонентом в компьютере. Тактовая частота немного нестабильна, но что вы ожидали, если он работает на карманных солнечных часах. Количество поддерживаемых компонентов: §f%s§7", + "oc:tooltip.cpu.architecture": "Архитектура: §f%s§7", + "oc:tooltip.cuttingwire": "Используется для нарезки глиняных блоков в пластины. Рвётся после использования, что делает её, пожалуй, самым неэффективным инструментом.", + "oc:tooltip.datacard0": "Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как хеширование и сжатие.", + "oc:tooltip.datacard1": "Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как AES-шифрование, хеширование и сжатие.", + "oc:tooltip.datacard2": "Обеспечивает поддержку нескольких продвинутых алгоритмов, таких как AES-шифрование, эллиптическая криптография, хеширование и сжатие.", + "oc:tooltip.debugcard": "Креативный предмет, позволяет манипулировать игровым миром. Используйте на свой страх и риск.", + "oc:tooltip.debugger": "Может быть использован для вывода отладочной информации о внутренней сети. Используйте только тогда, когда попросил разработчик.", + "oc:tooltip.diamondchip": "Небольшой кусочек некогда сияющего алмаза. Он больше никогда не будет таким, как прежде.", + "oc:tooltip.disassembler": "Разделяет предметы на исходные компоненты. §lВнимание§7: возвращённые предметы имеют шанс %s%% сломаться!", + "oc:tooltip.disk": "Примитивный носитель, который может быть использован для создания постоянных запоминающих устройств.", + "oc:tooltip.diskdrive.cc": "§aПоддерживаются§7 дискеты из ComputerCraft.", + "oc:tooltip.diskdrive": "Позволяет читать и записывать дискеты. Может быть установлен в роботов, что позволит затем вставлять дискеты.", + "oc:tooltip.diskdrivemountable": "Работает так же, как и обычный дисковод, но может быть вставлен только в серверную стойку.", + "oc:tooltip.diskusage": "Занято %s/%s байт", + "oc:tooltip.diskmodemanaged": "Режим: файловый", + "oc:tooltip.diskmodeunmanaged": "Режим: блочный", + "oc:tooltip.drone": "Дроны это легкие и быстрые устройства, но с ограниченным инвентарем.", + "oc:tooltip.dronecase": "Корпус, предназначенный для создания дрона в сборщике. Вмещает малое количество компонентов и предоставляет левитацию с помощью силы камня края.", + "oc:tooltip.eeprom": "Маленькое программируемое хранилище, которое содержит BIOS для запуска компьютеров.", + "oc:tooltip.fakeendstone": "Почти такой же, как и настоящий камень края, даже эмулирует его легкость!", + "oc:tooltip.geolyzer": "Позволяет сканировать твёрдость блоков в окрестности. Эта информация может быть полезна для создания голограммы области или для обнаружения руд.", + "oc:tooltip.graphicscard": "Используется для изменения того, что отображается на мониторах.\nМаксимальное разрешение: §f%sx%s§7\nМаксимальная глубина цвета:§f%s§7\nОпераций/такт: §f%s§7", + "oc:tooltip.hoverboots": "Прыгайте выше, падайте глубже, бегайте быстрее. Это и многое другое с нашими новыми Парящими ботинками (TM).", + "oc:tooltip.inkcartridge": "Используется в 3D печати. По таинственным причинам ему не нужно оставаться в принтере.", + "oc:tooltip.inkcartridgeempty": "Этот картридж выглядит пустым. Заправьте его с помощью красителей. Или выбросьте.", + "oc:tooltip.internetcard": "Эта карта позволяет делать HTTP-запросы и использовать реальные TCP сокеты.", + "oc:tooltip.interweb": "Поздравляем, вы выиграли одну (1) интерсеть. Подключиться к ней можно с помощью Интернет-платы. Осторожно: не кормите троллей.", + "oc:tooltip.ironnugget": "Самородок, созданный из железа. Может, именно поэтому он и назван железным самородком? Хм...", + "oc:tooltip.keyboard": "Присоединяется к монитору, позволяя вводить информацию.", + "oc:tooltip.hologram0": "Объёмный дисплей, управляемый компьютером и использующийся для отображения произвольных воксельных структур.\nРазрешение: §f48x32x48§7\nМаксимальный масштаб: §f3x§7\nГлубина цвета: §fМонохромный§7", + "oc:tooltip.hologram1": "Объёмный дисплей, управляемый компьютером и использующийся для отображения произвольных воксельных структур.\nРазрешение: §f48x32x48§7\nМаксимальный масштаб: §f4x§7\nГлубина цвета: §fТрёхцветный§7", + "oc:tooltip.linkedcard": "Они изготавливаются в парах и могут общаться только с партнёром. Тем не менее, общаться они могут на любом расстоянии - даже между измерениями. Однако для отправки сообщения нужно довольно много энергии.", + "oc:tooltip.linkedcard_channel": "§8Канал: %s§7", + "oc:tooltip.manual": "Содержит всю информацию о моде OpenComputers, которая вам может когда-либо понадобиться. И даже больше. По невероятно низкой цене... §oнажмите R для продолжения§7.", + "oc:tooltip.materialcosts": "Удерживайте [§f%s§7] для показа материалов.", + "oc:tooltip.materials": "Материалы:", + "oc:tooltip.memory": "Необходима для запуска компьютера. Чем больше ОЗУ, тем более требовательные программы Вы можете запустить.", + "oc:tooltip.microchip": "Чип, ранее известный как интегральная схема. Понятия не имею почему, но он работает с красным камнем.", + "oc:tooltip.microcontroller": "Микроконтроллеры - это компьютеры, сведённые к самым базовым вещам. Они используются для выполнения специфических задач и выполняют программу, записанную на EEPROM.\n§cНе могут взаимодействовать с внешними компонентами.§7", + "oc:tooltip.microcontrollercase": "Базовый компонент для создания микроконтроллеров. Положите в сборщик, чтобы установить компоненты, и соберите микроконтроллер.", + "oc:tooltip.motionsensor": "Может обнаружить движение ближайших живых существ. Требуется прямая линия видимости.", + "oc:tooltip.nanomachines": "Блок управления и кучка нанороботов для приема внутрь, если вы решились.", + "oc:tooltip.networkcard": "Позволяет обмениваться сообщениями между соединённым друг с другом - с помощью кабеля или других блоков - компьютерами.", + "oc:tooltip.poweracceptor": "Скорость преобразования энергии: §f%s/t§7", + "oc:tooltip.powerconverter.buildcraft": "§fBuildCraft MJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.factorization": "§fFactorization Charge§7: §a%s:%s§7", + "oc:tooltip.powerconverter.industrialcraft2": "§fIndustrialCraft² EU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.mekanism": "§fMekanism Joules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.thermalexpansion": "§fThermal Expansion RF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.resonantengine": "§fResonant Engine Coulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Преобразует энергию из других модов во внутренний тип энергии. Соотношение энергии:", + "oc:tooltip.powerdistributor": "Передаёт энергию между различными сетями. Полезно, если требуется запитать от одного преобразователя несколько сетей, которые должны оставаться разделёнными.", + "oc:tooltip.present": "Откройте этот подарок, чтобы получить немного §kчего-то§7!\n§8Крафтите предметы OpenComputers в нужное время, чтобы получить шанс получить подарок.§7", + "oc:tooltip.print.beaconbase": "§8Используется в качестве основы для маяка.", + "oc:tooltip.print.lightvalue": "§8Сила излучаемого света: %s.", + "oc:tooltip.print.redstonelevel": "§8Сила выдаваемого красного сигнала: %s.", + "oc:tooltip.printedcircuitboard": "Основа плат, памяти и прочего.", + "oc:tooltip.printer": "Позволяет печатать пользовательские 3D-модели, используя хамелиум и картриджи с чернилами. Должен быть настроен с помощью компьютера. Держите подальше от детей.", + "oc:tooltip.raid": "Позволяет соединять три жестких диска в единую файловую систему, которую могут использовать все подключенные компьютеры.", + "oc:tooltip.rawcircuitboard": "Может быть закалена в печи для получения печатной платы.", + "oc:tooltip.redstone": "Позволяет считывать и подавать сигналы красного камня вокруг блока. Контролируется любым подключенным компьютером. Иными словами, это что-то вроде внешней платы на красном камне.", + "oc:tooltip.redstonecard.projectred": "Мод §fProjectRed§7 §aподдерживается§7.", + "oc:tooltip.redstonecard.redlogic": "Мод §fRedLogic§7 §aподдерживается§7.", + "oc:tooltip.redstonecard.rednet": "Мод §fRedNet§7 §aподдерживается§7.", + "oc:tooltip.redstonecard.wirelesscbe": "Мод §fWireless Redstone (ChickenBones)§7 §aподдерживается§7.", + "oc:tooltip.redstonecard.wirelesssv": "Мод §fWireless Redstone (SlimeVoid)§7 §aподдерживается§7.", + "oc:tooltip.redstonecard": "Позволяет считывать и подавать сигналы красного камня вокруг компьютера или робота.", + "oc:tooltip.relay": "Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями не будут доступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними.", + "oc:tooltip.robot": "В отличие от компьютеров, роботы могут передвигаться и взаимодействовать с миром, как игрок.\n§cНе могут подключаться к внешним компонентам.§7", + "oc:tooltip.robot_level": "§fУровень§7: §a%s§7.", + "oc:tooltip.robot_storedenergy": "§fНакопленная энергия§7: §a%s§7.", + "oc:tooltip.screen": "Отображает текст, передаваемый видеокартой.\nМаксимальное разрешение: §f%sx%s§7\nМаксимальная глубина цвета: §f%s§7", + "oc:tooltip.server": "Это сервер. Есть много других, похожих на него, но только этот может быть улучшен компонентами, как системный блок. Можно включить после размещения в серверную стойку.", + "oc:tooltip.server.components": "Установленные компоненты:", + "oc:tooltip.rack": "Обеспечивает работу до четырёх серверов или других монтируемых компонентов.", + "oc:tooltip.switch": "Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями не будут доступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними.", + "oc:tooltip.tablet": "Планшет, полностью готовый к работе с Lua. Может быть отключен нажатием правой кнопки мыши + Shift.", + "oc:tooltip.tabletcase": "Простой корпус для планшета. Поместите в сборщик, чтобы вставить компоненты, и соберите себе планшетный компьютер.", + "oc:tooltip.terminal": "Позволяет дистанционно управлять сервером, пока вы находитесь в радиусе его действия. Действует как портативный дисплей с клавиатурой.\nShift+ПКМ по серверу терминалов в стойке для привязки.", + "oc:tooltip.terminalserver": "Компонент, к которому можно подключить беспроводной терминал. Имеет виртуальный экран и клавиатуру.", + "oc:tooltip.texturepicker": "Простой инструмент, позволяющий узнать название текстуры блока, которое можно использовать в 3D печати.", + "oc:tooltip.tier": "§8Уровень %s", + "oc:tooltip.netsplitter": "Работает как переключатель. Соединение каждой стороны переключается ключем. При подаче сигнала красного камня все соединения инвертируются.", + "oc:tooltip.toolong": "Удерживайте [§f%s§7], чтобы отобразить описание.", + "oc:tooltip.transistor": "Базовый элемент для большинства частей компьютера. Он немного деформирован, но отлично выполняет свою работу.", + "oc:tooltip.transposer": "Позволяет автоматизировать перемещение предметов и жидкостей между соседними инвентарями и хранилищами жидкости.", + "oc:tooltip.upgradeangel": "Позволяет роботам размещать блоки в воздухе без точки опоры.", + "oc:tooltip.upgradebattery": "Увеличивает количество энергии, которую робот может хранить, что позволяет ему работать дольше без перезарядки.", + "oc:tooltip.upgradechunkloader": "Если робот движется в лесу и никто его не видит, действительно ли он движется? Данное улучшение гарантирует это. Оно держит чанк, в котором находится робот, загруженным, но постоянно потребляет энергию во время работы.", + "oc:tooltip.upgradecontainercard": "Это улучшение-контейнер позволяет динамически устанавливать и снимать плату с собранного робота.\nМаксимальный уровень: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Это улучшение-контейнер позволяет динамически устанавливать и снимать другое улучшение с собранного робота.\nМаксимальный уровень: §f%s§7", + "oc:tooltip.upgradecrafting": "Позволяет роботам использовать верхнюю левую часть инвентаря для крафта предметов. Вещи должны быть расположены в том же порядке, что и в верстаке.", + "oc:tooltip.upgradedatabase": "Позволяет хранить информацию о предметах для последующего использования другими компонентами.\nПоддерживается записей: §f%s§7", + "oc:tooltip.upgradeexperience": "Это улучшение позволяет роботу накапливать опыт при выполнении различных операций. Чем больше у них опыта, тем больше энергии они могут хранить, могут быстрее собирать блоки и эффективнее использовать инструменты.", + "oc:tooltip.upgradegenerator": "Позволяет роботам генерировать энергию из сжигаемого топлива прямо на ходу.\n§fЭффективность§7: §a%s%%§7", + "oc:tooltip.upgradehover": "Данное улучшение позволяет роботам летать выше над землей без необходимости лазать по стенам.\nМаксимальная высота: §f%s§7", + "oc:tooltip.upgradeinventory": "Это улучшение даёт место в инвентаре для робота. Без него роботы не смогут хранить предметы внутри.", + "oc:tooltip.upgradeinventorycontroller": "Это улучшение позволяет роботу контролировать взаимодействие с внешними инвентарями и менять свой экипированный инструмент предметом из его инвентаря.", + "oc:tooltip.upgradeleash": "Позволяет некоторым устройствам, таким как дроны, цеплять животных на поводок. Много животных, при этом.", + "oc:tooltip.upgrademf": "Позволяет адаптерам взаимодействовать с блоками на удалении от них.", + "oc:tooltip.upgrademf.linked": "§fСоединение установлено§7", + "oc:tooltip.upgrademf.unlinked": "§fНет соединения§7", + "oc:tooltip.upgradenavigation": "Позволяет определять положение и ориентацию робота. Положение определяется относительно центра карты, использованной при крафте улучшения.", + "oc:tooltip.upgradepiston": "Позволяет двигать блоки, как поршни. §lНе§7 может двигать животных, однако.", + "oc:tooltip.upgradesign": "Позволяет читать и писать текст на табличках.", + "oc:tooltip.upgradesolargenerator": "Позволяет роботам генерировать энергию из солнечного света прямо на ходу. Требует прямую видимость неба над роботом. Генерирует энергию на скорости %s%% от двигателя Стирлинга.", + "oc:tooltip.upgradetank": "Позволяет роботам и дронам взаимодействовать с жидкостями. Без него они не смогут хранить жидкость.", + "oc:tooltip.upgradetankcontroller": "Позволяет роботам и дронам взамодействовать в внешними хранилищами жидкости, а также заливать и выливать жидкости в них.", + "oc:tooltip.upgradetractorbeam": "Даёт роботу самую невероятно продвинутую технологию под кодовым названием \"Магнит для предметов\". Позволяет роботу подбирать предметы в радиусе 3 блоков от своего расположения.", + "oc:tooltip.waypoint": "Добавляет путевую точку для устройств с навигационным улучшением.", + "oc:tooltip.wirelessnetworkcard": "Позволяет передавать сетевые сообщения по беспроводному каналу в дополнение к проводному. Вы можете настроить §fсилу сигнала§7, чтобы контролировать, насколько далеко должны отправляться сообщения. Более высокая сила сигнала приводит к повышенному потреблению энергии.", + "oc:tooltip.worldsensorcard": "Позволяет получать информацию об атмосфере и гравитации на планетах, добавляемых модом GalactiCraft. Используйте на свой страх и риск. Производитель не несет ответственности за материальный ущерб и увечья. У нас есть адвокаты. И деньги. Даже не пытайтесь.", + "oc:tooltip.wrench": "Гибрид отвертки и ключа.", + "achievement.oc.adapter": "Мы едины", + "achievement.oc.adapter.desc": "Дайте поработать с другими модами!", + "achievement.oc.assembler": "Великолепно", + "achievement.oc.assembler.desc": "Время захватывать мир!", + "achievement.oc.cable": "Не трогай провода!", + "achievement.oc.cable.desc": "... они от этого ржавеют", + "achievement.oc.capacitor": "Батарея в комплекте", + "achievement.oc.capacitor.desc": "Вы не можете остановить это.", + "achievement.oc.card": "Мы принимаем карточки", + "achievement.oc.card.desc": "Для вашего удобства. Вам это понравится.", + "achievement.oc.case": "В случае возникновения неполадок", + "achievement.oc.case.desc": "Открывайте боковую крышку летом.", + "achievement.oc.charger": "Ладно, сделаем это", + "achievement.oc.charger.desc": "Пошла зарядка.. черт, опять забыл редстоун сигнал.", + "achievement.oc.chip": "Полезные мелочи", + "achievement.oc.chip.desc": "Ведь электронные лампы - прошлый век", + "achievement.oc.cpu": "Разогнанный", + "achievement.oc.cpu.desc": "Время нагрузить его по полной.", + "achievement.oc.disassembler": "Больше не нужно", + "achievement.oc.disassembler.desc": "Когда великолепные идеи оказываются не такими уж и великолепными", + "achievement.oc.diskDrive": "Карусель", + "achievement.oc.diskDrive.desc": "Небольшая емкость, зато какой звук!", + "achievement.oc.drone": "Вездесущее око", + "achievement.oc.drone.desc": "Небольшой брат следит за тобой.", + "achievement.oc.eeprom": "Он может быть только один", + "achievement.oc.eeprom.desc": "Для каждого компьютера, то есть. Для загрузки компьютера, вы знали?", + "achievement.oc.floppy": "Не подносить магнит", + "achievement.oc.floppy.desc": "Дискеты не любят магниты.", + "achievement.oc.geolyzer": "Назад на землю", + "achievement.oc.geolyzer.desc": "Он имеет необычные свойства.", + "achievement.oc.graphicsCard": "Последнее поколение", + "achievement.oc.graphicsCard.desc": "Покажи это.", + "achievement.oc.hdd": "Продавец хотдогов", + "achievement.oc.hdd.desc": "Нет, подождите, это не то, о чем вы подумали. Не роняйте его...", + "achievement.oc.hologram": "Гиперпространство", + "achievement.oc.hologram.desc": "Когда двух измерений уже не хватает", + "achievement.oc.keyboard": "Крошкохранитель-3000", + "achievement.oc.keyboard.desc": "Не рекомендуется переворачивать и трясти его", + "achievement.oc.microcontroller": "Маленькая сестренка", + "achievement.oc.microcontroller.desc": "Просто маленький компьютер", + "achievement.oc.motionSensor": "Оно двигается", + "achievement.oc.motionSensor.desc": "Как Майкл Джексон.", + "achievement.oc.networkCard": "Нам надо поговорить!", + "achievement.oc.networkCard.desc": "Оставайтесь на связи с дальними родственниками.", + "achievement.oc.openOS": "Поехали", + "achievement.oc.openOS.desc": "Неужели он включится.", + "achievement.oc.powerDistributor": "Поделись с ближним", + "achievement.oc.powerDistributor.desc": "Когда вы помогаете распределять энергию.", + "achievement.oc.rack": "Прямо в стойку", + "achievement.oc.rack.desc": "Не знаю, о чем думаете вы, я думал о серверной стойке.", + "achievement.oc.raid": "Чрезмерная избыточность", + "achievement.oc.raid.desc": "Только диски не вынимайте.", + "achievement.oc.ram": "Случайный доступ к памяти", + "achievement.oc.ram.desc": "Поздравляем, вы делаете это правильно.", + "achievement.oc.redstoneCard": "Есть контакт", + "achievement.oc.redstoneCard.desc": "Возврат к аналоговой технике.", + "achievement.oc.redstoneIO": "Выскочка", + "achievement.oc.redstoneIO.desc": "Когда все сигналы в нужных местах", + "achievement.oc.robot": "Бип-пип", + "achievement.oc.robot.desc": "УНИЧТОЖИТЬ!", + "achievement.oc.screen": "Вы пробовали выключить и снова включить?", + "achievement.oc.screen.desc": "Серьезно. Редстоун сигнал может выключать монитор.", + "achievement.oc.server": "Выделенный сервер", + "achievement.oc.server.desc": "Облачные вычисления, мы идем к вам.", + "achievement.oc.switch": "Сложная топология", + "achievement.oc.switch.desc": "Избегайте сложных связей и потери пакетов.", + "achievement.oc.tablet": "Несъедобно", + "achievement.oc.tablet.desc": "Держите подальше от маленьких детей, во избежание пропажи денег на вашей кредитной карте.", + "achievement.oc.transistor": "Редстоун, скажи \"Привет.\"", + "achievement.oc.transistor.desc": "Создайте транзистор. Потом послушайте музыку. Не благодарите меня.", + "achievement.oc.wirelessNetworkCard": "Сигналы", + "achievement.oc.wirelessNetworkCard.desc": "Время идти туда, где никогда не появлялся сигнал.", + "death.attack.oc.nanomachinesOverload.1": "%s был слишком жадным.", + "death.attack.oc.nanomachinesOverload.2": "%s получил нервный срыв.", + "death.attack.oc.nanomachinesOverload.3": "Нанороботы %s вышли из под контроля.", + "death.attack.oc.nanomachinesHungry.1": "%s был съеден нанороботами.", + "death.attack.oc.nanomachinesHungry.2": "%s не кормил нанороботов.", + "death.attack.oc.nanomachinesHungry.3": "%s был переварен нанороботами.", + "nei.options.inventory.oredict": "Показывать имена Ore Dictionary", + "nei.options.inventory.oredict.true": "Да", + "nei.options.inventory.oredict.false": "Нет", + "nei.usage.oc.Manual": "Открыть руководство", + "option.oc.address": "Адрес", + "option.oc.componentName": "Название компонента", + "option.oc.energy": "Энергия" +} diff --git a/src/main/resources/assets/opencomputers/lang/tr_TR.lang b/src/main/resources/assets/opencomputers/lang/tr_TR.lang deleted file mode 100644 index c5f2217a52..0000000000 --- a/src/main/resources/assets/opencomputers/lang/tr_TR.lang +++ /dev/null @@ -1,496 +0,0 @@ -# Based off /src/main/resources/assets/opencomputers/lang/en_US.lang -# Initial localization by H. Utku Maden (Other authors should append their names in the list.) -# ------ -# Some translational loses may have been introduced. (Language translations may not be 100% correct) -# Please comment awkward translational situations. - -# Blocks -#===== - -tile.oc.adapter.name=Dönüştürücü -tile.oc.assembler.name=Elektronik Birleştirici -tile.oc.cable.name=Kablo - -# This translation is prefered. Do not change to 'kapasitör' or 'kondansatör'. -tile.oc.capacitor.name=Sığaç -# Sounds wierd, but was made with 'halı' to keep consistancy with minecraft's own localization. -tile.oc.carpetedcapacitor.name=Halılı Sığaç - -# Direct translation of tier does not exist, nearest is 'sınıf', but higher numbers are a worse object; so 'seviye' (level) was used -tile.oc.case1.name=Bilgisayar Kasası (1. Seviye) -tile.oc.case2.name=Bilgisayar Kasası (2. Seviye) -tile.oc.case3.name=Bilgisayar Kasası (3. Seviye) -tile.oc.casecreative.name=Bilgisayar Kasası (Yaratıcı) - -# Fictional things get fictional names -tile.oc.chameliumblock.name=Şamelyum Küpü -tile.oc.charger.name=Şarj Cihazı -tile.oc.disassembler.name=Ayrıştırıcı -tile.oc.diskdrive.name=Disket Sürücüsü -tile.oc.endstone.name=End Taşı -tile.oc.geolyzer.name=Jeolizer -tile.oc.hologram1.name=Hologram Projektörü (1. Seviye) -tile.oc.hologram2.name=Hologram Projektörü (2. Seviye) -tile.oc.keyboard.name=Klavye -tile.oc.microcontroller.name=Mikroişlemci -tile.oc.motionsensor.name=Hareket Algılayıcı -tile.oc.netsplitter.name=Ağ Bölücü -tile.oc.powerconverter.name=Güç Dönüştürücü -tile.oc.powerdistributor.name=Güç Dağıtıcı -tile.oc.print.name=3B Çıktı -tile.oc.printer.name=3B Yazıcı -tile.oc.raid.name=RAID -tile.oc.redstone.name=Kızıltaş G/Ç Küpü -tile.oc.relay.name=Ağ Tamponu -tile.oc.robot.name=Robot -tile.oc.robotafterimage.name=Robot -tile.oc.screen1.name=Ekran (1. Seviye) -tile.oc.screen2.name=Ekran (2. Seviye) -tile.oc.screen3.name=Ekran (3. Seviye) -tile.oc.rack.name=Sunucu Rafı -tile.oc.transposer.name=İletici -tile.oc.waypoint.name=Yer İşareti - -# Items -item.oc.abstractbuscard.name=Soyut Veriyolu Kartı -item.oc.acid.name=İspirto? -item.oc.alu.name=Aritmetik Mantık Birimi (ALU) -item.oc.analyzer.name=Analiz Aleti -item.oc.apu0.name=Eklentili İşlemci (APU) (1. Seviye) -item.oc.apu1.name=Eklentili İşlemci (APU) (2. Seviye) -item.oc.apu2.name=Eklentili İşlemci (APU) (Yaratıcı) -item.oc.arrowkeys.name=Ok Tuşları -item.oc.buttongroup.name=Harf Takımı -item.oc.cardbase.name=Ham Bilgisayar Kartı -item.oc.chamelium.name=Şamelyum -item.oc.circuitboard.name=Devre Kartı -item.oc.componentbus0.name=Cihaz Veriyolu (1. Seviye) -item.oc.componentbus1.name=Cihaz Veriyolu (2. Seviye) -item.oc.componentbus2.name=Cihaz Veriyolu (3. Seviye) -item.oc.componentbus3.name=Cihaz Veriyolu (Yaratıcı) -item.oc.controlunit.name=Yönetim Birimi (CU) -item.oc.cpu0.name=İşlemci (CPU) (1. Seviye) -item.oc.cpu1.name=İşlemci (CPU) (2. Seviye) -item.oc.cpu2.name=İşlemci (CPU) (3. Seviye) -item.oc.cuttingwire.name=Kesme Teli -item.oc.datacard0.name=Veri Kartı (1. Seviye) -item.oc.datacard1.name=Veri Kartı (2. Seviye) -item.oc.datacard2.name=Veri Kartı (3. Seviye) -item.oc.debugcard.name=Hata Ayıklama Kartı -item.oc.debugger.name=Ağ Hata Ayıklama Aleti -item.oc.diamondchip.name=Elmas Kesiti -item.oc.disk.name=Disk Bileşeni -item.oc.diskdrivemountable.name=Disk -item.oc.drone.name=Drone -item.oc.dronecase0.name=Drone Kasası (1. Seviye) -item.oc.dronecase1.name=Drone Kasası (2. Seviye) -item.oc.dronecase3.name=Drone Kasası (3. Seviye) -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=Disket -item.oc.graphicscard0.name=Ekran Kartı (1. Seviye) -item.oc.graphicscard1.name=Ekran Kartı (2. Seviye) -item.oc.graphicscard2.name=Ekran Kartı (3. Seviye) -item.oc.harddiskdrive0.name=Hard Disk (1. Seviye) -item.oc.harddiskdrive1.name=Hard Disk (2. Seviye) -item.oc.harddiskdrive2.name=Hard Disk (3. Seviye) -item.oc.hoverboots.name=Uçma Botu -item.oc.inkcartridge.name=Mürekkep Kartuşu -item.oc.inkcartridgeempty.name=Mürekkep Kartuşu (Boş) -item.oc.internetcard.name=İnternet Kartı -item.oc.interweb.name=Ağ? -item.oc.ironnugget.name=Demir Parçası -item.oc.linkedcard.name=Bağlantılı Kart -item.oc.manual.name=OpenComputers Kullanma Kılavuzu -item.oc.memory0.name=Bellek (1. Seviye) -item.oc.memory1.name=Bellek (1,5. Seviye) -item.oc.memory2.name=Bellek (2. Seviye) -item.oc.memory3.name=Bellek (2,5. Seviye) -item.oc.memory4.name=Bellek (3. Seviye) -item.oc.memory5.name=Bellek (3,5. Seviye) -item.oc.microchip0.name=Entegre Devre (1. Seviye) -item.oc.microchip1.name=Entegre Devre (2. Seviye) -item.oc.microchip2.name=Entegre Devre (3. Seviye) -item.oc.microcontrollercase0.name=Mikroişlemci Kasası (1. Seviye) -item.oc.microcontrollercase1.name=Mikroişlemci Kasası (2. Seviye) -item.oc.microcontrollercase3.name=Mikroişlemci Kasası (Yaratıcı) -item.oc.nanomachines.name=Nanomakine -item.oc.networkcard.name=Ağ Kartı -item.oc.numpad.name=Tuş Takımı -item.oc.present.name=Çam Sakızı... -item.oc.printedcircuitboard.name=Devre Kartı (PCB) -item.oc.rawcircuitboard.name=Ham Devre Kartı -item.oc.redstonecard0.name=Kızıltaş Kartı (1. Seviye) -item.oc.redstonecard1.name=Kızıltaş Kartı (2. Seviye) -item.oc.server0.name=Sunucu (1. Seviye) -item.oc.server1.name=Sunucu (2. Seviye) -item.oc.server2.name=Sunucu (3. Seviye) -item.oc.server3.name=Sunucu (Yaratıcı) -item.oc.tablet.name=Tablet -item.oc.tabletcase0.name=Tablet Kasası (1. Seviye) -item.oc.tabletcase1.name=Tablet Kasası 500640(2. Seviye) -item.oc.tabletcase3.name=Tablet Kasası (Yaratıcı) -item.oc.terminal.name=Uzaktan Uçbirim -item.oc.terminalserver.name=Uçbirim Sunucusu -item.oc.texturepicker.name=Doku Seçici -item.oc.transistor.name=Transistör -item.oc.upgradeangel.name=Melek Eklentisi -item.oc.upgradebattery0.name=Batarya Eklentisi (1. Seviye) -item.oc.upgradebattery1.name=Batarya Eklentisi (2. Seviye) -item.oc.upgradebattery2.name=Batarya Eklentisi (3. Seviye) -item.oc.upgradechunkloader.name=Kesiti-yüklü-tut Eklentisi -item.oc.upgradecontainercard0.name=Kart Yuvası (1. Seviye) -item.oc.upgradecontainercard1.name=Kart Yuvası (2. Seviye) -item.oc.upgradecontainercard2.name=Kart Yuvası (3. Seviye) -item.oc.upgradecontainerupgrade0.name=Eklenti Yuvası (1. Seviye) -item.oc.upgradecontainerupgrade1.name=Eklenti Yuvası (2. Seviye) -item.oc.upgradecontainerupgrade2.name=Eklenti Yuvası (3. Seviye) -item.oc.upgradecrafting.name=Üretim Eklentisi -item.oc.upgradedatabase0.name=Veritabanı Eklentisi (1. Seviye) -item.oc.upgradedatabase1.name=Veritabanı Eklentisi (2. Seviye) -item.oc.upgradedatabase2.name=Veritabanı Eklentisi (3. Seviye) -item.oc.upgradeexperience.name=Deneyim Eklentisi -item.oc.upgradegenerator.name=Jeneratör Eklentisi -item.oc.upgradehover0.name=Uçma Eklentisi (1. Seviye) -item.oc.upgradehover1.name=Uçma Eklentisi (2. Seviye) -item.oc.upgradeinventory.name=Envanter Eklentisi -item.oc.upgradeinventorycontroller.name=Envanter Yönetimi Eklentisi -item.oc.upgradeleash.name=Kayış Eklentisi -item.oc.upgrademf.name=MFU -item.oc.upgradenavigation.name=Yönbulma Eklentisi -item.oc.upgradepiston.name=Piston Eklentisi -item.oc.upgradesign.name=Tabela G/Ç Eklentisi -item.oc.upgradesolargenerator.name=Güneş Enerjisi Eklentisi -item.oc.upgradetank.name=Tank Eklentisi -item.oc.upgradetankcontroller.name=Tank Yönetimi Eklentisi -item.oc.upgradetractorbeam.name=Çekme Işını Eklentisi -item.oc.upgradetrading.name=Ticaret Eklentisi -item.oc.wirelessnetworkcard0.name=Kablosuz Ağ Kartı (1. Seviye) -item.oc.wirelessnetworkcard1.name=Kablosuz Ağ Kartı (2. Seviye) -item.oc.worldsensorcard.name=Dünya Algılayıcı Kartı -item.oc.wrench.name=Tornanahtar - -# Entities -entity.oc.Drone.name=Drone - -# GUI -oc:gui.Analyzer.Address=§6Adres§f: %s -oc:gui.Analyzer.AddressCopied=Adres panoya kopyalandı -oc:gui.Analyzer.ChargerSpeed=§6Şarj hızı§f: %s -oc:gui.Analyzer.ComponentName=§6Parça adı§f: %s -oc:gui.Analyzer.Components=§6Bağlı parça sayısı§f: %s -oc:gui.Analyzer.CopyToClipboard=Panoya kopyalamak için tıklayın. -oc:gui.Analyzer.LastError=§6Son hata§f: %s -oc:gui.Analyzer.RobotName=§6Adı§f: %s -oc:gui.Analyzer.RobotOwner=§6Sahibi§f: %s -oc:gui.Analyzer.RobotXp=§6Deneyimi§f: %s (Seviye %s) -oc:gui.Analyzer.StoredEnergy=§6Depoladığı enerji§f: %s -oc:gui.Analyzer.TotalEnergy=§6Maksimum enerji miktarı§f: %s -oc:gui.Analyzer.Users=§6Kullanıcılar§f: %s -oc:gui.Analyzer.WirelessStrength=§6Sinyal gücü§f: %s -oc:gui.Assembler.Collect=Çıktıyı al -oc:gui.Assembler.Complexity=Karmaşıklık: %s/%s -oc:gui.Assembler.InsertCase=Kasa koyun -oc:gui.Assembler.InsertCPU=İşlemci koyun -oc:gui.Assembler.InsertRAM=Bellek koyun -oc:gui.Assembler.Progress=İlerleme: %s%% (%s) -oc:gui.Assembler.Run=Birleştir -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=Ekran Kartı -oc:gui.Assembler.Warning.Inventory=Envanter Eklentisi -oc:gui.Assembler.Warning.Keyboard=Klavye -oc:gui.Assembler.Warning.OS=Başlangıç Bölümü -oc:gui.Assembler.Warning.Screen=Ekran -oc:gui.Assembler.Warnings=§eUyarı§7: Önerilen parçalar eksik. -oc:gui.Chat.NewVersion=Yeni bir sürüm çıktı: %s -oc:gui.Chat.TextureName=§7Doku adı §a%s§f. -oc:gui.Chat.WarningClassTransformer=Sınıf dönüştürücü çalışırken §chatalar§f oluştu. Lütfen bunu mod yazarlarına (bütün!) FML §alatest.log§f/§afml-server-latest.log§f kayıt dosyasıyla birlikte gönderiniz. Şimdiden teşekkür ederiz. -#There were §cerrors§f running the class transformer. Please report this, together with your (full!) FML §alatest.log§f/§afml-server-latest.log§f logfile, thank you! -oc:gui.Chat.WarningFingerprint=§cUYARI§f - Parmakizi uyuşmazlığı! '§a%s§f' beklerken '§e%s§f' bulduk. Eğer karılmamış sürümünü kullanan bir mode yazarı değilseniz OpenComputers modunu yeniden indirmeniz §lşiddetle§f tavsiye edilir; çünkü elinizdeki JAR dosyası ile oynanmış olabilir. -# §cWARNING§f - fingerprint mismatch! Expected '§a%s§f' but got '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with. -oc:gui.Chat.WarningLink=Bağlantı açılamadı: %s -oc:gui.Chat.WarningLuaFallback=Orijinal Lua kütüphaneleri bulunamadı ve bilgisayarlar durumlarını koruyamayacak. Bölümler yeniden yüklenince bilgisayarlar yeniden başlayacak. (Yedek kütüphaneler kullanılıyor) -# Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads. -oc:gui.Chat.WarningProjectRed=OpenComputers'ı desteklemeyen bir Project: Red sürümünü kullanıyorsunuz. Project: Red'i güncellemeyi deneyin. -# You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red. -oc:gui.Chat.WarningRecipes=Bir veya birden fazla tarifi yüklerken hata oluştu. Bazı nesneler üretilemeyebilir. Daha fazla bilgi için kayıt defteri dosyanızı bakın. -# There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information. -oc:gui.Chat.WarningSimpleComponent= §aSimpleComponent§f arayüzünü kullanan bir mod eklentisi (acep sizin mi?) §eyanlış bir şeyler§f yaptı. Parça mantığı enjekte edilemedi. Daha fazla bilgi için kayıt defteri dosyanıza bakın. -# An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information. -oc:gui.Drive.Managed=Denetimli -oc:gui.Drive.Unmanaged=Denetimsiz -oc:gui.Drive.Warning=§lUyarı§r: kip değiştirmek disk üzerindeki bütün verinin kaybına sebep olur. -oc:gui.Error.ComponentOverflow=Bilgisayara çok parça takılı. -oc:gui.Error.InternalError=İçsel bir hata, lütfen kayıt defterine bakın. Bu büyük ihtimalle bir yazılım sorunu. -oc:gui.Error.NoCPU=Bilgisayarın işlemcisi yok. -oc:gui.Error.NoEnergy=Güç yeterli değil. -oc:gui.Error.NoRAM=Bilgisayarda bellek yok. -oc:gui.Error.OutOfMemory=Bellek taştı. -oc:gui.Manual.Blocks=OpenComputers Küpler -oc:gui.Manual.Home=Ev -oc:gui.Manual.Items=OpenComputers Nesneler -oc:gui.Manual.Warning.BlockMissing=Küp yok. -oc:gui.Manual.Warning.ImageMissing=Resim bulunamadı. -oc:gui.Manual.Warning.ItemMissing=Nesne yok. -oc:gui.Manual.Warning.OreDictMissing=Cevher Muadilleri Sözlüğü yok. -oc:gui.Raid.Warning=§4Disk eklemek onu siler.[nl] Disk çıkarmak RAID'i siler. -oc:gui.Robot.Power=Güç -oc:gui.Robot.TurnOff=Kapat -oc:gui.Robot.TurnOn=Aç[nl]§7Hata çözmek için analiz aletini kullanın.§r -oc:gui.Rack.Back=Arka -oc:gui.Rack.Bottom=Alt -oc:gui.Rack.Left=Sol -oc:gui.Rack.None=Hiç -oc:gui.Rack.Right=Sağ -oc:gui.Rack.Enabled=Aktif -oc:gui.Rack.Disabled=İnaktif -oc:gui.Rack.Top=Üst -oc:gui.Switch.PacketsPerCycle=Paket / devir -oc:gui.Switch.QueueSize=Sıra Uzunluğu -oc:gui.Switch.TransferRate=Devir hızı -oc:gui.Terminal.InvalidKey=Geçersiz anahtar. Büyük ihtimalle başka bir uçbirim sunuzuya bağlandı. -oc:gui.Terminal.OutOfRange=Sinyal yok. - -# Containers -oc:container.adapter=Dönüştürücü -oc:container.case=Bilgisayar -oc:container.charger=Şarj Aleti -oc:container.disassembler=Ayrıştırıcı -oc:container.diskdrive=Disket Sürücüsü -oc:container.printer=Yazıcı -oc:container.raid=RAID -oc:container.relay=Ağ Tamponu -oc:container.server=Sunucu -oc:container.rack=Sunucu Rafı -oc:container.tabletwrapper=Tablet - -# Keybinds -key.clipboardPaste=Panodan Yapıştır - -# Item / Block Tooltips -oc:tooltip.abstractbuscard=LIP paketleri alıp ileterek §fStargateTech 2§7'in soyut veriyolu ile iletişim kurmanızı sağlar. -oc:tooltip.acid=Zehirli, alkol kokan sıvımtırak bir şey. Belki askerde bazı manyak herifler kafayı bulmak için içer. Başka kullanım alanları da olabilir. -oc:tooltip.adapter=Parça olmayan blokları (örneğin "vanilla" bloklar veya diğer modların bloklarını) kumanda etmek için kullanılır. -oc:tooltip.alu=Siz yorulmayın diye sizin için işlem yapar. Belki böylesi daha iyidir. -oc:tooltip.analyzer=Blokların §fadresi§7 ve §fparça ismi§7 gibi özelliklerini gösterir.[nl] Ayrıca bilgisayarın beklenmedik bir şekilde kapanmasına sebep olan hatayı da gösterir. -# Used to display information about blocks, such as their §faddress§7 and §fcomponent name§7.[nl] Also displays the error that caused a computer to crash if it did not shut down normally. -oc:tooltip.apu= Ekran kartı olan bir işlemci, bir kart yuvasına daha ihtiyacınız olursa diye.[nl] Desteklenen parçalar: §f%s§7[nl] Maksimum çözünürlük: §f%sx%s§7[nl] Maksimum renk derinliği: §f%s§7[nl] İşlem/tick: §f%s§7 -# This is a CPU with an integrated GPU (or IGP), when you just need that extra card slot.[nl] Supported components: §f%s§7[nl] Maximum resolution: §f%sx%s§7[nl] Maximum color depth: §f%s§7[nl] Operations/tick: §f%s§7 -oc:tooltip.assembler=Bilgisayar parçalarından robot ve başka cihazları üretmenize yarar. -oc:tooltip.cable=Blokları bağlamanın ucuz bir yolu. -oc:tooltip.capacitor=Daha sonra kullanılmak üzere enerji depolar. Çok hızlı şarj ve desarj olabilir. -oc:tooltip.carpetedcapacitor=Daha sonra kullanılmak üzere enerji depolar. Çok hızlı şarj ve desarj olabilir. Bir Oselo veya Koyun üstünde yürürse şarj olur. -oc:tooltip.cardbase=Adı üstünde, tüm bilgisayar kartlarının en basit yapıtaşı. -oc:tooltip.case=Bilgisayar kasası bilgisayarın yapıtaşıdır ve bilgisayarın §fkartlarını§7, §fbelleğini§7 ve §fsürücülerimi§7 bünyesimde barındırır.[nl] Yuvalar: §f%s§7 -# The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.[nl] Slots: §f%s§7 -oc:tooltip.chamelium=3B çıktılar için hammaddedir. Yutmayınız, körlük ve geçici bilinç kaybına sebep olabilir. -oc:tooltip.chameliumblock=Hoş ve temiz. 3B çıktılarda renkli şekiller yaratmak veya üssünüzü temiz ve renkli bir blokla dekore etmek için kullanılabilir. -oc:tooltip.charger=Sığaçlardan takındaki robotlara ve dronlara enerji aktarır. Aktarım hızı gelen §fkızıltaş sinyaline§7 bağlıdır; tam güç tam hızla şarj olması, hiç sinyal olmaması ise şarj olmaması demektir. Tabletleri de şarj etmek, hatta sürücülerine erişmek için kullanılabilir. -oc:tooltip.circuitboard=İşte şimdi yol alıyoruz. İşleme tabi tutularak devre kartı elde edilebilir. -oc:tooltip.controlunit=Bu birim... yönetiyor işte. İşlemciye ihtiyacın var o yüzden önemli, değil mi? -oc:tooltip.componentbus=Bu eklenti sunucuların aynı anda daha çok parça ile bağlantı kurmasını sağlar, işlemciler gibi.[nl] Desteklenen parçalar §f%s§7 -oc:tooltip.cpu=Her bilgisayara lazım. Saati biraz istikrarsız ama cep saatinden o kadar oluyor. Çok sarsma.[nl]Desteklenen parçalar: §f%s§7 -oc:tooltip.cpu.Architecture=Mimari: §f%s§7 -oc:tooltip.cuttingwire=Kil blokları devre kartı şekline sokmak için kullanılır. Tek kullanımda kopar. Ne iğrenç bir alet bu. -oc:tooltip.datacard0="hashing", "deflate"/"inflate" gibi birkaç gelişmiş algoritma sunar. -oc:tooltip.datacard1="hashing", "deflate"/"inflate", AES şifreleme gibi birkaç gelişmiş algoritma sunar. -oc:tooltip.datacard2="hashing", "deflate"/"inflate", AES şifreleme, eliptik eğri kriptografisi gibi birkaç gelişmiş algoritma sunar. -oc:tooltip.debugcard=Test etmeyi kolaylaştıran, dünyayı manüpile eden Yaratıcı mod aracı. Yetişkin denetimi altında kullanın. -oc:tooltip.debugger=OpenComputers'ın iç ağının hata ayıklama bilgisini toplamak için kullanılır. Sadece bir geliştirici isterse kullanınız. -oc:tooltip.diamondchip=Parıltılı bir elmasın naçiz bir parçası. Asla eskisi gibi olamayacak. -oc:tooltip.disassembler=Nesneleri ihtiva ettiği parçalarına ayrıştırır. §lUyarı§7: ayrışan parçaların ayrışma sürecinde %%%s bozulma olasılığı vardır. -oc:tooltip.disk=Daimi veri depolama cihazları yapılabilen ilkel veri saklama dairesi. -oc:tooltip.diskdrive.CC=ComputerCraft disketler §açalışır§7. -oc:tooltip.diskdrive=Disket okuma ve yazmaya yarar. Daha sonra disket takmak için robotlara da takılabilir. -oc:tooltip.diskdrivemountable=Normal bir sürücüyle aynı işlevi görür fakat bir sunucu rafına takılmalıdır. -oc:tooltip.diskusage=Disk kullanımı: %s/%s Byte -oc:tooltip.diskmodemanaged=Kip: Denetimli -oc:tooltip.diskmodeunmanaged=Kip: Denetimsiz -oc:tooltip.drone=Dronlar hafif, hızlı, az yük kapasiteli uçan cihazlardır. -oc:tooltip.dronecase=Bu kasa birleştirici ile Drone yapmak için kullanılır. Sınırlı parça için alanı var ve end taşı ile güçlendirilmiş uçma kabiliyeti var. -oc:tooltip.eeprom=Elektrikle silinebilir, programlanabilir salt okunur hafıza. Bilgisayarların çalışması için gerekli BIOS kodunu içerir. -oc:tooltip.fakeendstone=Neredeyse aynısı, bu da havalanmaya meyilli. -oc:tooltip.geolyzer=Yakındaki blokların sertliğini ölçmeye yarar. Bu bilgi cevher bulmak için bölgenin holgramını yapmaya yarayabilir. -oc:tooltip.graphicscard=Ekranları güzel bilgilerle doldurur.[nl] Azami Çözünürlük: §f%sx%s§7[nl] Azami renk derinliği: §f%s§7[nl] İşlem/tick: §f%s§7 -oc:tooltip.hoverboots=Daha yükseğe sıçra, daha derine in, daha iyi yürü. Hepsi Uçan Bot (TM) sayesinde. -oc:tooltip.inkcartridge=3B yazıcılara mürekkep koymak için kullanılır. Nedense makineye takılı kalmıyor. -oc:tooltip.inkcartridgeempty=Yazıcının kartuşu yine birmiş. Mürekkebi doldurabilirsin. Ya da at gitsin. Nasıl olsa bir daha aynı yazmaz. -oc:tooltip.internetcard=Bu kart ile gerçekten internete bağlanıp, TCP üzerinden HTTP istemlerinde bulunabilirsiniz. -oc:tooltip.interweb=Tebrikler, bir ağ (?) kazandınız. Internete bununla bağlanabilirsiniz. Trollerin seviyesine inmmeyin. -oc:tooltip.ironnugget=Demirden kopan bir parça, başka nasıl açıklayayım. -oc:tooltip.keyboard=Yazı yazmak için ekranlara takılabilir. -oc:tooltip.hologram0=Bilgisayarla kumanda edilen, voksel tabanlı yapıları göstermek için kullanılan hacimsel ekran.[nl] Çözünürlük: §f48x32x48§7 [nl] Azami Ölçek: §f3x§7 [nl] Renk derinliği: §fMonokrom§7 -oc:tooltip.hologram1=Bilgisayarla kumanda edilen, voksel tabanlı yapıları göstermek için kullanılan hacimsel ekran.[nl] Çözünürlük: §f48x32x48§7 [nl] Azami Ölçek: §f3x§7 [nl] Renk derinliği: §f3 Renkli§7 -oc:tooltip.linkedcard=Bunlar çiftler halinde üretilir ve sadece partneriyle iletişim kurarlar. İletişimleri mesafeyi ve boyutları engel tanımaz fakat çok güç ister. -oc:tooltip.linkedcard_channel=§8Kanal: %s§7 -oc:tooltip.manual=OpenComputers hakkında ihtiyaç duyabileceğiniz her bilgi ve daha fazlası. Hem de sadece ... §ofiyatı öğrenmek için R'ye basın§7. -oc:tooltip.memory=Bilgisayarların çalışması için gerekli. Ne kadar çok belleğiniz varsa o kadar karmaşık programlar çalıştırabilirsiniz. -oc:tooltip.microchip=Siyah elektrikle çalışan zımbırtılar. Neden kızıltaşla çalıştıkları beni aşıyor. -oc:tooltip.microcontroller=Mikroişlemciler bilgisayarların en asgari biçimi. Görev odaklıdırlar ve EEPROM'larında gelen tek bir programla çalışırlar.[nl] §cHarici parçalarla bağlantı kuramazlar.§7 -oc:tooltip.microcontrollercase=Mikroişlemcilerin temel bileşeni. Mikroişlemci yapmak için bir birleştirici kullanın ve başka parçalar ekleyin. -oc:tooltip.motionsensor=Yakındaki canlıların hareketini algılar. Çalışması için canlılar görüş açısında olmalıdır. -oc:tooltip.nanomachines=Kumanda birimi ve tüketilmek üzere bir sürü nanomakine, tabi sende o mide varse. -oc:tooltip.networkcard=Uzaktaki, birbirine başka bloklarla bağlı (örneğin kablolarla) bilgisayarların birbirlerine mesajlar göndererek iletişim kurmasını sağlar. -oc:tooltip.poweracceptor=Enerji dönüşüm hızı: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fBuildCraft MJ'si§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fFactorization Yükü§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fIndustrialCraft² EU'su§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fMekanism Joule'ü§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fThermal Expansion RF'i§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fResonant Engine Coulomb'u§7: §a%s:%s§7 -oc:tooltip.powerconverter=Başka modların enerjilerini OpenComputers'ın kendi enerji birimine dönüştürür. Dönüşüm oranları: -oc:tooltip.powerdistributor=Farklı ağlara enerji dağıtır. Ayrı kalması gereken alt-ağlara dönüştürücüden gelen enerjiyi dağıtmak için bire bir. -oc:tooltip.present=... çoban armağanı. Verdiğimiz rahatsızlıktan dolayı. Hediyeyi açarak §kganimetlere§7 sahip olabilirsiniz.[nl]§8Doğru zamanda OpenComputers parçaları üreterek hediye kazanabilirsiniz.§7 -oc:tooltip.print.BeaconBase=§8Fener tabanı olarak işlev görür. -oc:tooltip.print.LightValue=§8Işınan ışık: %s. -oc:tooltip.print.RedstoneLevel=§8Kızıltaş çıktısı: %s. -oc:tooltip.printedcircuitboard=Bellek, bilgisayar kartları vb.nin yapı taşı. -oc:tooltip.printer=Kullanıcı tanımlı nesnelerin şamelyum ve mürekkep kullanılarak basılmasını sağlar. Bir bilgisayar tarafından yönetilmelidir. Bazı sebeplerden dolayı, çocuklardan uzak tutun. -oc:tooltip.raid=Birden fazla diskin tek bir dosya sistemi altında birleştirilip bağlı tüm bilgisayarlar tarafından kullanılmasını sağlar. -oc:tooltip.rawcircuitboard=Herhangi bir fırında kurutulabilir. -oc:tooltip.redstone=Bloğun çevresinde kızıltaş iletimi ve çevresinden kızıltaş alıgalma yapabilir. Bağlı herhangi bir bilgisayar kumanda edebilir. Harici bir kızıltaş kartı gibi davranır. -oc:tooltip.redstonecard.ProjectRed=§fProject: Red§7 §adestekli§7. -oc:tooltip.redstonecard.RedLogic=§fRedLogic§7 §adestekli§7. -oc:tooltip.redstonecard.RedNet=§fRedNet§7 §adestekli§7. -oc:tooltip.redstonecard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 §adestekli§7. -oc:tooltip.redstonecard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 §adestekli§7. -oc:tooltip.redstonecard=Bilgisayarın veya robotun çevresine kızltaş sinyali iletimesini ve çevresinden kızıltaş sinyalı algılamasını sağlar. -oc:tooltip.relay=Farklı ağları birbirine bağlamaya yarar. Sadece ağ mesajları iletilir, parçalar görünmez. Bunu Ağ kartlarını kullanırken bilgisayarları birbirinden izole etmek için kullanabilirsiniz. -oc:tooltip.robot=Bilgisayarların aksine robotlar oyuncular gibi dünyada hareket edebilir ve ona değişiklikler yapabilir.[nl] §cHarici parçalara bağlanamaz.§7 -# the underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§fSeviye§7: §a%s§7 -oc:tooltip.robot_storedenergy=§fDepolu enerji§7: §a%s§7 -oc:tooltip.screen=Yazı göster, kasadaki bir ekran kartı aracılığıyla. [nl] Azami çözünürlük: §f%sx%s§7[nl] Azami renk derinliği: §f%s§7 -oc:tooltip.server=Bu bir sunucu. Bundan çokça olabilir fakat bu (bir bilgisayar gibi) başka parçalarla geliştirilebilir. Sunucu rafına takılarak çalıştırılabilir. -oc:tooltip.server.Components=Takılı Parçalar: -oc:tooltip.rack=4 sunucunun veya rafa konulabilen parçaların takılmasını sağlar. -oc:tooltip.tablet=Lua ile eğlence için bir tablet bilgisayar. Çömelirken aktifleştirirseniz kapanmaya sorlarsınız. -oc:tooltip.tabletcase=Basit bir tablet kasası. Bir birleştirici ve başka parçalar ile bir tablet yapabilirsiniz.. -oc:tooltip.terminal=Menzili içinde bir sunucuyu uzaktan kumanda etmenize yarar. Taşınabilir ekran ve klavye gibi davranır. Shift+Sağ-Tık ile raftaki bir sunucuya bağlayabilirsiniz. -oc:tooltip.terminalserver=Uzaktan Uçbirimin yöneticisi. Sanal bir ekranı ve klavyesi var. -oc:tooltip.texturepicker= Bu alet, 3B yazıcıda şekil tanımlamak üzere bir bloğun yüzeyini ifade eden bir metin gösterir. Kesinlikle dokunun ismi değil. Sana yalan borcum mu var? -oc:tooltip.tier=§8%s. Seviye -oc:tooltip.netsplitter=Ayarlanabilir bir bağlantı noktası gibi davranır. Her yüzün bağlantısı bir İngiliz anahtarı ile değiştirilebilir. Her yüzün bağlantısı bir kızıltaş sinyali ile tersine dönüştürülebilr. -oc:tooltip.toolong=Şuna basılı tutarak daha detaylı bilgi alabilirsiniz: [§f%s§7] -oc:tooltip.transistor=Çoğu bilgisayar parçasının basit bir bileşeni. Biraz yamulmuş ama iş görür. -oc:tooltip.transposer=Bitişiğindeki envanterler arasında katı ve sıvı değişimini sağlar. -oc:tooltip.upgradeangel=Robotlara boşlukta (tutunacak yer yokken) blok koyma yetisi sağlar. -oc:tooltip.upgradebattery=Cihazın depolayabileceği enerji miktarını arttırır. Böylece cihaz şarj olmadan daha uzun süre çalışabilir. [nl] Kapasite: §f%s§7 -oc:tooltip.upgradechunkloader=Robot tek başına ormana giderse ve onu kimse görmezse hareket eder mi? Bu eklenti çalıştığından emin olur. Bir bölümü(16x16x256) sürekli yüklü tutar fakat sürekli enerji harcar. -oc:tooltip.upgradecontainercard=Bu yuva birleştirilmiş cihazda kartların rahatlıkla takılıp çıkarılmasını sağlar. [nl] Azami Seviye: §f%s§7 -oc:tooltip.upgradecontainerupgrade=Bu yuva birleştirilmiş cihazda eklentilerin rahatlıkla takılıp çıkarılmasını sağlar. [nl] Azami Seviye: §f%s§7 -oc:tooltip.upgradecrafting=Robotun envanterinin sol üst köşesini çalışma masası gibi kullanmasını sağlar. Nesnelerin tarife göre yerleşmesi şarttır. -oc:tooltip.upgradedatabase=Bu eklenti deste bilgisinin başka parçalar tarafından kullanılmak üzere saklanmasını sağlar.[nl] Desteklenen satır sayısı: §f%s§7 -oc:tooltip.upgradeexperience=Bu eklenti çeşitli eylemler aracalığıyla robotların deneyim kazanmasını sağlar. Robotlar ne kadar deneyimliyse o kadar enerji depolayabilir, o kadar hızlı blok toplayabilir ve aletleri o kadar etkili kullanabilir. -oc:tooltip.upgradegenerator=Yolda yakıttan enerji üretimini sağlar. Yakıtları enerji değerlerine göre enerji üretmek için kullanır.[nl] §fVerimlilik§7: §a%s%%§7 -oc:tooltip.upgradehover=Bu eklenti robotun duvar tırmanmak zorunda kalmadan daha yükseğe çıkmasını sağlar.[nl] Azami irtifa: §f%s§7 -oc:tooltip.upgradeinventory=Bu eklenti bir robota veya drona envanter sağlar. Bu olmadan nesne tutamazlar. -oc:tooltip.upgradeinventorycontroller=Bu eklenti robotların ve dronların harici envanterlerle iletişim kurmasını ve onlardaki aletlerle kendi aletini değişmesini sağlar. -oc:tooltip.upgrademf=Dönüştürücülerin bitişiğinde olmadığı nesnelerle iletişim kurmasını sağlar. -oc:tooltip.upgrademf.Linked=§fBağlantı kuruldu§7 -oc:tooltip.upgrademf.Unlinked=§fBağlantı yok§7 -oc:tooltip.upgradeleash=Drone gibi bazı cihazların belediye görevlilerine sataş...-özür dilerim. Bu cihaz aslında canlı(lar)ı bağlamak için kullanılıyormuş. İlğinç. -oc:tooltip.upgradenavigation=Cihazın yönünü ve yerini tespit etmek için kullanılır. Yer bilgisi eklentiyi üretirken kullanılan haritanın merkez noktasına göredir. -oc:tooltip.upgradepiston=Bu eklenti çok itici. Piston gibi blokları hareket ettirmeye yarar. Canlıları ve partikülleri hareket ettir§lmez§7. -oc:tooltip.upgradesign=Tabelalardan yazı okumayı ve onlara yazı yazmayı sağlar. -oc:tooltip.upgradesolargenerator=Güneşten enerji üretimini sağlar. Gökyüzü görüş açısında olmalıdır. Bir Stirling motorunun %%%s katı hızı kadar hızlı enerji üretebilir. -oc:tooltip.upgradetank=Robotlara ve dronlara sıvı depolama alanı sağlar. Bu olmadan sıvı depolayamazlar. -oc:tooltip.upgradetankcontroller=Bu eklenti robotlara ve dronlara harici sıvı depoları ile iletişim yeteneği sağlar. Harici depodan dahiliye, dahiliden hariciye sıvı iletimi sağlar. -oc:tooltip.upgradetractorbeam=Nesne mıknatısı adı verilen çok gelişmiş bir teknolojiti edinin. Cihazın 3 block menzilinden nesneleri almasını sağlar. -oc:tooltip.waypoint=Yönbulma eklentisi olan cihazlar için referans noktası oluşturur. -oc:tooltip.wirelessnetworkcard=Normal ağ mesajlarının yanı sıra kablosuz ağ mesajlarının iletimini sağlar. §fSinyal gücünü§7 ayarlayarak mesajların ne kadar uzağa iletildiğini kontrol edebilirsiniz. Yüksek sinyal gücü daha çok enerji tüketir. -oc:tooltip.worldsensorcard=Dünya hakkında yerçekimi ve nefes alınabilir bir atmosferi olup olmadığı gibi bilgileri toplar. Bilgiler kesin değildir. Üretici firma karttan edilen bilgilere göre verilen kararlar sonucu oluşan maddi ve manevi hiçbir hasardan sorumlu değildir. Avukatlarımız var. Paramız da. Denemeyi aklınızdan bile geçirmeyin. -oc:tooltip.Wrench=İngiliz anahtarı tornavida karışımı bir şey. Öğrenmesi basit, ustalaşması zor. - -#Achievements -achievement.oc.adapter=Tak-Çalıştır -achievement.oc.adapter.desc=Diğer modların ve elbette vanilla Minecraft blokları ile iletişim kur. -achievement.oc.assembler=Harika -achievement.oc.assembler.desc=Dünyayı ele geçirme vakti. -achievement.oc.cable=Temiz Kablo -achievement.oc.cable.desc=Patentli dolanma önleyici teknolojisi ile. -achievement.oc.capacitor=Piller Ürüne Dahildir. -achievement.oc.capacitor.desc=Durduramazsın. -achievement.oc.card=Kredi Kartı Geçerlidir. -achievement.oc.card.desc=Sizin için. Kesinlikle bir art niyetimiz yok. -achievement.oc.case=Yangın Durumunda ... -achievement.oc.case.desc=Çünkü küp kasalar en iyisi. -achievement.oc.charger=Haydi şu işi bitirelim. -achievement.oc.charger.desc=Şarj etsene seni... Kızıltaş sinyalini unutmuşum. -achievement.oc.chip=En Küçüğünden Lütfen -achievement.oc.chip.desc=Çünkü tüpler eskide kaldı. -achievement.oc.cpu=Modifiye -achievement.oc.cpu.desc=Haydi bu makineyi biraz zorlayalım. -achievement.oc.disassembler=Ali Yazar, Veli Bozar -achievement.oc.disassembler.desc=Nasıl olsa Sarı Çizmeli Mehmet Ağa bir gün öder hesabı. -achievement.oc.diskDrive=Dönmedolap -achievement.oc.diskDrive.desc=Az alan, güzel ses. -achievement.oc.drone=Su gibi git... -achievement.oc.drone.desc=... su gibi gel. Pahalısın lazımsın bize. -achievement.oc.eeprom=Ya ben, ya hiç! -achievement.oc.eeprom.desc=Bilgisayar için, önemli bir parça. -achievement.oc.floppy=Güneş Tutulması -achievement.oc.floppy.desc=Hepsini bozduk 1999'da. -achievement.oc.geolyzer=Zonguldak -achievement.oc.geolyzer.desc=Toprak ne ki, yeri bir metre kaz kömür çıkıyor. -achievement.oc.graphicsCard=Yeni Nesil -achievement.oc.graphicsCard.desc=Döviz kuru düşseydi fakir kalmazdım. -achievement.oc.hdd=Hıyar Domates Dirhem -achievement.oc.hdd.desc=Yanlış oldu, bekle dilimin ucunda. -achievement.oc.hologram=Gelecek Boyut -achievement.oc.hologram.desc=2B'tan sıkıldın mı? -achievement.oc.keyboard=TozMıknatısı3000 -achievement.oc.keyboard.desc=Odanıza alın, odanız tertemiz olsun*. *TozMıknatısı300 dışında. -achievement.oc.microcontroller=Kardeş -achievement.oc.microcontroller.desc=Bilgisayarın küçük kardeşi. -achievement.oc.motionSensor=Tuvalet Kabusu -achievement.oc.motionSensor.desc=İşerken ışık söner mi acaba? -achievement.oc.networkCard=Feysbuk -achievement.oc.networkCard.desc=Hep birlikte kimsenin takmadığı şeyleri paylaşıp sosyal olduğumuzu sanıyoruz. -achievement.oc.openOS=iŞlEtİm SiStEmİ NeY? -achievement.oc.openOS.desc=Bilgisayarcıya git format atsın ablacım. -achievement.oc.powerDistributor=Hayat Paylaşınca Güzel -achievement.oc.powerDistributor.desc=Reklam yapmıyoruz. Güzel sözün telif hakkı mı olur? -achievement.oc.rack="Bizim salondaki raf olur mu?" -achievement.oc.rack.desc=Tartmaz bir kere. -achievement.oc.raid=Yerim Kalmadı. -achievement.oc.raid.desc=Ne kadar çok "aile fotoğrafı"n varsa artık. -achievement.oc.ram=Seçici Hatıra -achievement.oc.ram.desc=Nedense sevdiklerimi daha çok hatırlıyorum. -achievement.oc.redstoneCard=Regülatörü Tak. -achievement.oc.redstoneCard.desc=Daha tüpler ısınacak. -achievement.oc.redstoneIO=TRT -achievement.oc.redstoneIO.desc=İlk yayın birazdan başlayacak. -achievement.oc.robot="Gerginlik yaratma robot o robot." -achievement.oc.robot.desc="Robot musun sen?" "Evet." -achievement.oc.screen=Aç Kapa Çalışır. -achievement.oc.screen.desc=Gerçekten, kızıltaş sinyalleri etkiliyor. -achievement.oc.server=e-Devlet Kapısı -achievement.oc.server.desc=Ne varsa orada, bu bilgileri kim saklayacak. -achievement.oc.switch=Birini düzelt, diğerini boz. -achievement.oc.switch.desc=Telekomcular niye işlerini yapamıyor. -achievement.oc.tablet=Bu bir ilaç değildir. -achievement.oc.tablet.desc=Çocuklardan uzak tutun. İstenmeyen harcamalara neden olabilir. -achievement.oc.transistor=Düğmemtırak -achievement.oc.transistor.desc=Bizim radyodada vardı bunlardan. -achievement.oc.wirelessNetworkCard="Işınla bizi Scotty" -achievement.oc.wirelessNetworkCard.desc="Barış içinde size bir ileti getirdik." - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s elini çok sıkı tuttu. -death.attack.oc.nanomachinesOverload.2=%s kafayı yedi. -death.attack.oc.nanomachinesOverload.3=%s öldü. Nanomakine varmış diyorlar. -death.attack.oc.nanomachinesHungry.1=%s sizlere ömür. Nanomakineler diri diri yemiş. -death.attack.oc.nanomachinesHungry.2=%s nanomakinelerine bakmadı. -death.attack.oc.nanomachinesHungry.3=%s sindirildi. - -# NEI Integration -nei.options.inventory.oredict=Cevher Muadilleri Sözlüğü Adlarını Göster -nei.options.inventory.oredict.true=Evet -nei.options.inventory.oredict.false=Hayır -nei.usage.oc.Manual=Kılavuzu Aç - -# Waila Integration -option.oc.address=Adres -option.oc.componentName=Parça Adı -option.oc.energy=Enerji diff --git a/src/main/resources/assets/opencomputers/lang/tr_tr.json b/src/main/resources/assets/opencomputers/lang/tr_tr.json new file mode 100644 index 0000000000..3d9b0dc992 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/tr_tr.json @@ -0,0 +1,451 @@ +{ + "tile.oc.adapter": "Dönüştürücü", + "tile.oc.assembler": "Elektronik Birleştirici", + "tile.oc.cable": "Kablo", + "tile.oc.capacitor": "Sığaç", + "tile.oc.carpetedcapacitor": "Halılı Sığaç", + "tile.oc.case1": "Bilgisayar Kasası (1. Seviye)", + "tile.oc.case2": "Bilgisayar Kasası (2. Seviye)", + "tile.oc.case3": "Bilgisayar Kasası (3. Seviye)", + "tile.oc.casecreative": "Bilgisayar Kasası (Yaratıcı)", + "tile.oc.chameliumblock": "Şamelyum Küpü", + "tile.oc.charger": "Şarj Cihazı", + "tile.oc.disassembler": "Ayrıştırıcı", + "tile.oc.diskdrive": "Disket Sürücüsü", + "tile.oc.endstone": "End Taşı", + "tile.oc.geolyzer": "Jeolizer", + "tile.oc.hologram1": "Hologram Projektörü (1. Seviye)", + "tile.oc.hologram2": "Hologram Projektörü (2. Seviye)", + "tile.oc.keyboard": "Klavye", + "tile.oc.microcontroller": "Mikroişlemci", + "tile.oc.motionsensor": "Hareket Algılayıcı", + "tile.oc.netsplitter": "Ağ Bölücü", + "tile.oc.powerconverter": "Güç Dönüştürücü", + "tile.oc.powerdistributor": "Güç Dağıtıcı", + "tile.oc.print": "3B Çıktı", + "tile.oc.printer": "3B Yazıcı", + "tile.oc.raid": "RAID", + "tile.oc.redstone": "Kızıltaş G/Ç Küpü", + "tile.oc.relay": "Ağ Tamponu", + "tile.oc.robot": "Robot", + "tile.oc.robotafterimage": "Robot", + "tile.oc.screen1": "Ekran (1. Seviye)", + "tile.oc.screen2": "Ekran (2. Seviye)", + "tile.oc.screen3": "Ekran (3. Seviye)", + "tile.oc.rack": "Sunucu Rafı", + "tile.oc.transposer": "İletici", + "tile.oc.waypoint": "Yer İşareti", + "item.oc.abstractbuscard": "Soyut Veriyolu Kartı", + "item.oc.acid": "İspirto?", + "item.oc.alu": "Aritmetik Mantık Birimi (ALU)", + "item.oc.analyzer": "Analiz Aleti", + "item.oc.apu0": "Eklentili İşlemci (APU) (1. Seviye)", + "item.oc.apu1": "Eklentili İşlemci (APU) (2. Seviye)", + "item.oc.apu2": "Eklentili İşlemci (APU) (Yaratıcı)", + "item.oc.arrowkeys": "Ok Tuşları", + "item.oc.buttongroup": "Harf Takımı", + "item.oc.cardbase": "Ham Bilgisayar Kartı", + "item.oc.chamelium": "Şamelyum", + "item.oc.circuitboard": "Devre Kartı", + "item.oc.componentbus0": "Cihaz Veriyolu (1. Seviye)", + "item.oc.componentbus1": "Cihaz Veriyolu (2. Seviye)", + "item.oc.componentbus2": "Cihaz Veriyolu (3. Seviye)", + "item.oc.componentbus3": "Cihaz Veriyolu (Yaratıcı)", + "item.oc.controlunit": "Yönetim Birimi (CU)", + "item.oc.cpu0": "İşlemci (CPU) (1. Seviye)", + "item.oc.cpu1": "İşlemci (CPU) (2. Seviye)", + "item.oc.cpu2": "İşlemci (CPU) (3. Seviye)", + "item.oc.cuttingwire": "Kesme Teli", + "item.oc.datacard0": "Veri Kartı (1. Seviye)", + "item.oc.datacard1": "Veri Kartı (2. Seviye)", + "item.oc.datacard2": "Veri Kartı (3. Seviye)", + "item.oc.debugcard": "Hata Ayıklama Kartı", + "item.oc.debugger": "Ağ Hata Ayıklama Aleti", + "item.oc.diamondchip": "Elmas Kesiti", + "item.oc.disk": "Disk Bileşeni", + "item.oc.diskdrivemountable": "Disk", + "item.oc.drone": "Drone", + "item.oc.dronecase0": "Drone Kasası (1. Seviye)", + "item.oc.dronecase1": "Drone Kasası (2. Seviye)", + "item.oc.dronecase3": "Drone Kasası (3. Seviye)", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "Disket", + "item.oc.graphicscard0": "Ekran Kartı (1. Seviye)", + "item.oc.graphicscard1": "Ekran Kartı (2. Seviye)", + "item.oc.graphicscard2": "Ekran Kartı (3. Seviye)", + "item.oc.harddiskdrive0": "Hard Disk (1. Seviye)", + "item.oc.harddiskdrive1": "Hard Disk (2. Seviye)", + "item.oc.harddiskdrive2": "Hard Disk (3. Seviye)", + "item.oc.hoverboots": "Uçma Botu", + "item.oc.inkcartridge": "Mürekkep Kartuşu", + "item.oc.inkcartridgeempty": "Mürekkep Kartuşu (Boş)", + "item.oc.internetcard": "İnternet Kartı", + "item.oc.interweb": "Ağ?", + "item.oc.ironnugget": "Demir Parçası", + "item.oc.linkedcard": "Bağlantılı Kart", + "item.oc.manual": "OpenComputers Kullanma Kılavuzu", + "item.oc.memory0": "Bellek (1. Seviye)", + "item.oc.memory1": "Bellek (1,5. Seviye)", + "item.oc.memory2": "Bellek (2. Seviye)", + "item.oc.memory3": "Bellek (2,5. Seviye)", + "item.oc.memory4": "Bellek (3. Seviye)", + "item.oc.memory5": "Bellek (3,5. Seviye)", + "item.oc.microchip0": "Entegre Devre (1. Seviye)", + "item.oc.microchip1": "Entegre Devre (2. Seviye)", + "item.oc.microchip2": "Entegre Devre (3. Seviye)", + "item.oc.microcontrollercase0": "Mikroişlemci Kasası (1. Seviye)", + "item.oc.microcontrollercase1": "Mikroişlemci Kasası (2. Seviye)", + "item.oc.microcontrollercase3": "Mikroişlemci Kasası (Yaratıcı)", + "item.oc.nanomachines": "Nanomakine", + "item.oc.networkcard": "Ağ Kartı", + "item.oc.numpad": "Tuş Takımı", + "item.oc.present": "Çam Sakızı...", + "item.oc.printedcircuitboard": "Devre Kartı (PCB)", + "item.oc.rawcircuitboard": "Ham Devre Kartı", + "item.oc.redstonecard0": "Kızıltaş Kartı (1. Seviye)", + "item.oc.redstonecard1": "Kızıltaş Kartı (2. Seviye)", + "item.oc.server0": "Sunucu (1. Seviye)", + "item.oc.server1": "Sunucu (2. Seviye)", + "item.oc.server2": "Sunucu (3. Seviye)", + "item.oc.server3": "Sunucu (Yaratıcı)", + "item.oc.tablet": "Tablet", + "item.oc.tabletcase0": "Tablet Kasası (1. Seviye)", + "item.oc.tabletcase1": "Tablet Kasası 500640(2. Seviye)", + "item.oc.tabletcase3": "Tablet Kasası (Yaratıcı)", + "item.oc.terminal": "Uzaktan Uçbirim", + "item.oc.terminalserver": "Uçbirim Sunucusu", + "item.oc.texturepicker": "Doku Seçici", + "item.oc.transistor": "Transistör", + "item.oc.upgradeangel": "Melek Eklentisi", + "item.oc.upgradebattery0": "Batarya Eklentisi (1. Seviye)", + "item.oc.upgradebattery1": "Batarya Eklentisi (2. Seviye)", + "item.oc.upgradebattery2": "Batarya Eklentisi (3. Seviye)", + "item.oc.upgradechunkloader": "Kesiti-yüklü-tut Eklentisi", + "item.oc.upgradecontainercard0": "Kart Yuvası (1. Seviye)", + "item.oc.upgradecontainercard1": "Kart Yuvası (2. Seviye)", + "item.oc.upgradecontainercard2": "Kart Yuvası (3. Seviye)", + "item.oc.upgradecontainerupgrade0": "Eklenti Yuvası (1. Seviye)", + "item.oc.upgradecontainerupgrade1": "Eklenti Yuvası (2. Seviye)", + "item.oc.upgradecontainerupgrade2": "Eklenti Yuvası (3. Seviye)", + "item.oc.upgradecrafting": "Üretim Eklentisi", + "item.oc.upgradedatabase0": "Veritabanı Eklentisi (1. Seviye)", + "item.oc.upgradedatabase1": "Veritabanı Eklentisi (2. Seviye)", + "item.oc.upgradedatabase2": "Veritabanı Eklentisi (3. Seviye)", + "item.oc.upgradeexperience": "Deneyim Eklentisi", + "item.oc.upgradegenerator": "Jeneratör Eklentisi", + "item.oc.upgradehover0": "Uçma Eklentisi (1. Seviye)", + "item.oc.upgradehover1": "Uçma Eklentisi (2. Seviye)", + "item.oc.upgradeinventory": "Envanter Eklentisi", + "item.oc.upgradeinventorycontroller": "Envanter Yönetimi Eklentisi", + "item.oc.upgradeleash": "Kayış Eklentisi", + "item.oc.upgrademf": "MFU", + "item.oc.upgradenavigation": "Yönbulma Eklentisi", + "item.oc.upgradepiston": "Piston Eklentisi", + "item.oc.upgradesign": "Tabela G/Ç Eklentisi", + "item.oc.upgradesolargenerator": "Güneş Enerjisi Eklentisi", + "item.oc.upgradetank": "Tank Eklentisi", + "item.oc.upgradetankcontroller": "Tank Yönetimi Eklentisi", + "item.oc.upgradetractorbeam": "Çekme Işını Eklentisi", + "item.oc.upgradetrading": "Ticaret Eklentisi", + "item.oc.wirelessnetworkcard0": "Kablosuz Ağ Kartı (1. Seviye)", + "item.oc.wirelessnetworkcard1": "Kablosuz Ağ Kartı (2. Seviye)", + "item.oc.worldsensorcard": "Dünya Algılayıcı Kartı", + "item.oc.wrench": "Tornanahtar", + "entity.oc.Drone.name": "Drone", + "oc:gui.Analyzer.Address": "§6Adres§f: %s", + "oc:gui.Analyzer.AddressCopied": "Adres panoya kopyalandı", + "oc:gui.Analyzer.ChargerSpeed": "§6Şarj hızı§f: %s", + "oc:gui.Analyzer.ComponentName": "§6Parça adı§f: %s", + "oc:gui.Analyzer.Components": "§6Bağlı parça sayısı§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "Panoya kopyalamak için tıklayın.", + "oc:gui.Analyzer.LastError": "§6Son hata§f: %s", + "oc:gui.Analyzer.RobotName": "§6Adı§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6Sahibi§f: %s", + "oc:gui.Analyzer.RobotXp": "§6Deneyimi§f: %s (Seviye %s)", + "oc:gui.Analyzer.StoredEnergy": "§6Depoladığı enerji§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6Maksimum enerji miktarı§f: %s", + "oc:gui.Analyzer.Users": "§6Kullanıcılar§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6Sinyal gücü§f: %s", + "oc:gui.Assembler.Collect": "Çıktıyı al", + "oc:gui.Assembler.Complexity": "Karmaşıklık: %s/%s", + "oc:gui.Assembler.InsertCase": "Kasa koyun", + "oc:gui.Assembler.InsertCPU": "İşlemci koyun", + "oc:gui.Assembler.InsertRAM": "Bellek koyun", + "oc:gui.Assembler.Progress": "İlerleme: %s%% (%s)", + "oc:gui.Assembler.Run": "Birleştir", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "Ekran Kartı", + "oc:gui.Assembler.Warning.Inventory": "Envanter Eklentisi", + "oc:gui.Assembler.Warning.Keyboard": "Klavye", + "oc:gui.Assembler.Warning.OS": "Başlangıç Bölümü", + "oc:gui.Assembler.Warning.Screen": "Ekran", + "oc:gui.Assembler.Warnings": "§eUyarı§7: Önerilen parçalar eksik.", + "oc:gui.Chat.NewVersion": "Yeni bir sürüm çıktı: %s", + "oc:gui.Chat.TextureName": "§7Doku adı §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "Sınıf dönüştürücü çalışırken §chatalar§f oluştu. Lütfen bunu mod yazarlarına (bütün!) FML §alatest.log§f/§afml-server-latest.log§f kayıt dosyasıyla birlikte gönderiniz. Şimdiden teşekkür ederiz.", + "oc:gui.Chat.WarningFingerprint": "§cUYARI§f - Parmakizi uyuşmazlığı! '§a%s§f' beklerken '§e%s§f' bulduk. Eğer karılmamış sürümünü kullanan bir mode yazarı değilseniz OpenComputers modunu yeniden indirmeniz §lşiddetle§f tavsiye edilir; çünkü elinizdeki JAR dosyası ile oynanmış olabilir.", + "oc:gui.Chat.WarningLink": "Bağlantı açılamadı: %s", + "oc:gui.Chat.WarningLuaFallback": "Orijinal Lua kütüphaneleri bulunamadı ve bilgisayarlar durumlarını koruyamayacak. Bölümler yeniden yüklenince bilgisayarlar yeniden başlayacak. (Yedek kütüphaneler kullanılıyor)", + "oc:gui.Chat.WarningProjectRed": "OpenComputers'ı desteklemeyen bir Project: Red sürümünü kullanıyorsunuz. Project: Red'i güncellemeyi deneyin.", + "oc:gui.Chat.WarningRecipes": "Bir veya birden fazla tarifi yüklerken hata oluştu. Bazı nesneler üretilemeyebilir. Daha fazla bilgi için kayıt defteri dosyanızı bakın.", + "oc:gui.Chat.WarningSimpleComponent": " §aSimpleComponent§f arayüzünü kullanan bir mod eklentisi (acep sizin mi?) §eyanlış bir şeyler§f yaptı. Parça mantığı enjekte edilemedi. Daha fazla bilgi için kayıt defteri dosyanıza bakın.", + "oc:gui.Drive.Managed": "Denetimli", + "oc:gui.Drive.Unmanaged": "Denetimsiz", + "oc:gui.Drive.Warning": "§lUyarı§r: kip değiştirmek disk üzerindeki bütün verinin kaybına sebep olur.", + "oc:gui.Error.ComponentOverflow": "Bilgisayara çok parça takılı.", + "oc:gui.Error.InternalError": "İçsel bir hata, lütfen kayıt defterine bakın. Bu büyük ihtimalle bir yazılım sorunu.", + "oc:gui.Error.NoCPU": "Bilgisayarın işlemcisi yok.", + "oc:gui.Error.NoEnergy": "Güç yeterli değil.", + "oc:gui.Error.NoRAM": "Bilgisayarda bellek yok.", + "oc:gui.Error.OutOfMemory": "Bellek taştı.", + "oc:gui.Manual.Blocks": "OpenComputers Küpler", + "oc:gui.Manual.Home": "Ev", + "oc:gui.Manual.Items": "OpenComputers Nesneler", + "oc:gui.Manual.Warning.BlockMissing": "Küp yok.", + "oc:gui.Manual.Warning.ImageMissing": "Resim bulunamadı.", + "oc:gui.Manual.Warning.ItemMissing": "Nesne yok.", + "oc:gui.Manual.Warning.OreDictMissing": "Cevher Muadilleri Sözlüğü yok.", + "oc:gui.Raid.Warning": "§4Disk eklemek onu siler.\nDisk çıkarmak RAID'i siler.", + "oc:gui.Robot.Power": "Güç", + "oc:gui.Robot.TurnOff": "Kapat", + "oc:gui.Robot.TurnOn": "Aç\n§7Hata çözmek için analiz aletini kullanın.§r", + "oc:gui.Rack.Back": "Arka", + "oc:gui.Rack.Bottom": "Alt", + "oc:gui.Rack.Left": "Sol", + "oc:gui.Rack.None": "Hiç", + "oc:gui.Rack.Right": "Sağ", + "oc:gui.Rack.Enabled": "Aktif", + "oc:gui.Rack.Disabled": "İnaktif", + "oc:gui.Rack.Top": "Üst", + "oc:gui.Switch.PacketsPerCycle": "Paket / devir", + "oc:gui.Switch.QueueSize": "Sıra Uzunluğu", + "oc:gui.Switch.TransferRate": "Devir hızı", + "oc:gui.Terminal.InvalidKey": "Geçersiz anahtar. Büyük ihtimalle başka bir uçbirim sunuzuya bağlandı.", + "oc:gui.Terminal.OutOfRange": "Sinyal yok.", + "oc:container.adapter": "Dönüştürücü", + "oc:container.case": "Bilgisayar", + "oc:container.charger": "Şarj Aleti", + "oc:container.disassembler": "Ayrıştırıcı", + "oc:container.diskdrive": "Disket Sürücüsü", + "oc:container.printer": "Yazıcı", + "oc:container.raid": "RAID", + "oc:container.relay": "Ağ Tamponu", + "oc:container.server": "Sunucu", + "oc:container.rack": "Sunucu Rafı", + "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.clipboardPaste": "Panodan Yapıştır", + "oc:tooltip.abstractbuscard": "LIP paketleri alıp ileterek §fStargateTech 2§7'in soyut veriyolu ile iletişim kurmanızı sağlar.", + "oc:tooltip.acid": "Zehirli, alkol kokan sıvımtırak bir şey. Belki askerde bazı manyak herifler kafayı bulmak için içer. Başka kullanım alanları da olabilir.", + "oc:tooltip.adapter": "Parça olmayan blokları (örneğin \"vanilla\" bloklar veya diğer modların bloklarını) kumanda etmek için kullanılır.", + "oc:tooltip.alu": "Siz yorulmayın diye sizin için işlem yapar. Belki böylesi daha iyidir.", + "oc:tooltip.analyzer": "Blokların §fadresi§7 ve §fparça ismi§7 gibi özelliklerini gösterir.\nAyrıca bilgisayarın beklenmedik bir şekilde kapanmasına sebep olan hatayı da gösterir.", + "oc:tooltip.apu": " Ekran kartı olan bir işlemci, bir kart yuvasına daha ihtiyacınız olursa diye.\nDesteklenen parçalar: §f%s§7\nMaksimum çözünürlük: §f%sx%s§7\nMaksimum renk derinliği: §f%s§7\nİşlem/tick: §f%s§7", + "oc:tooltip.assembler": "Bilgisayar parçalarından robot ve başka cihazları üretmenize yarar.", + "oc:tooltip.cable": "Blokları bağlamanın ucuz bir yolu.", + "oc:tooltip.capacitor": "Daha sonra kullanılmak üzere enerji depolar. Çok hızlı şarj ve desarj olabilir.", + "oc:tooltip.carpetedcapacitor": "Daha sonra kullanılmak üzere enerji depolar. Çok hızlı şarj ve desarj olabilir. Bir Oselo veya Koyun üstünde yürürse şarj olur.", + "oc:tooltip.cardbase": "Adı üstünde, tüm bilgisayar kartlarının en basit yapıtaşı.", + "oc:tooltip.case": "Bilgisayar kasası bilgisayarın yapıtaşıdır ve bilgisayarın §fkartlarını§7, §fbelleğini§7 ve §fsürücülerimi§7 bünyesimde barındırır.\nYuvalar: §f%s§7", + "oc:tooltip.chamelium": "3B çıktılar için hammaddedir. Yutmayınız, körlük ve geçici bilinç kaybına sebep olabilir.", + "oc:tooltip.chameliumblock": "Hoş ve temiz. 3B çıktılarda renkli şekiller yaratmak veya üssünüzü temiz ve renkli bir blokla dekore etmek için kullanılabilir.", + "oc:tooltip.charger": "Sığaçlardan takındaki robotlara ve dronlara enerji aktarır. Aktarım hızı gelen §fkızıltaş sinyaline§7 bağlıdır; tam güç tam hızla şarj olması, hiç sinyal olmaması ise şarj olmaması demektir. Tabletleri de şarj etmek, hatta sürücülerine erişmek için kullanılabilir.", + "oc:tooltip.circuitboard": "İşte şimdi yol alıyoruz. İşleme tabi tutularak devre kartı elde edilebilir.", + "oc:tooltip.controlunit": "Bu birim... yönetiyor işte. İşlemciye ihtiyacın var o yüzden önemli, değil mi?", + "oc:tooltip.componentbus": "Bu eklenti sunucuların aynı anda daha çok parça ile bağlantı kurmasını sağlar, işlemciler gibi.\nDesteklenen parçalar §f%s§7", + "oc:tooltip.cpu": "Her bilgisayara lazım. Saati biraz istikrarsız ama cep saatinden o kadar oluyor. Çok sarsma.\nDesteklenen parçalar: §f%s§7", + "oc:tooltip.cpu.Architecture": "Mimari: §f%s§7", + "oc:tooltip.cuttingwire": "Kil blokları devre kartı şekline sokmak için kullanılır. Tek kullanımda kopar. Ne iğrenç bir alet bu.", + "oc:tooltip.datacard0": "\"hashing\", \"deflate\"/\"inflate\" gibi birkaç gelişmiş algoritma sunar.", + "oc:tooltip.datacard1": "\"hashing\", \"deflate\"/\"inflate\", AES şifreleme gibi birkaç gelişmiş algoritma sunar.", + "oc:tooltip.datacard2": "\"hashing\", \"deflate\"/\"inflate\", AES şifreleme, eliptik eğri kriptografisi gibi birkaç gelişmiş algoritma sunar.", + "oc:tooltip.debugcard": "Test etmeyi kolaylaştıran, dünyayı manüpile eden Yaratıcı mod aracı. Yetişkin denetimi altında kullanın.", + "oc:tooltip.debugger": "OpenComputers'ın iç ağının hata ayıklama bilgisini toplamak için kullanılır. Sadece bir geliştirici isterse kullanınız.", + "oc:tooltip.diamondchip": "Parıltılı bir elmasın naçiz bir parçası. Asla eskisi gibi olamayacak.", + "oc:tooltip.disassembler": "Nesneleri ihtiva ettiği parçalarına ayrıştırır. §lUyarı§7: ayrışan parçaların ayrışma sürecinde %%%s bozulma olasılığı vardır.", + "oc:tooltip.disk": "Daimi veri depolama cihazları yapılabilen ilkel veri saklama dairesi.", + "oc:tooltip.diskdrive.CC": "ComputerCraft disketler §açalışır§7.", + "oc:tooltip.diskdrive": "Disket okuma ve yazmaya yarar. Daha sonra disket takmak için robotlara da takılabilir.", + "oc:tooltip.diskdrivemountable": "Normal bir sürücüyle aynı işlevi görür fakat bir sunucu rafına takılmalıdır.", + "oc:tooltip.diskusage": "Disk kullanımı: %s/%s Byte", + "oc:tooltip.diskmodemanaged": "Kip: Denetimli", + "oc:tooltip.diskmodeunmanaged": "Kip: Denetimsiz", + "oc:tooltip.drone": "Dronlar hafif, hızlı, az yük kapasiteli uçan cihazlardır.", + "oc:tooltip.dronecase": "Bu kasa birleştirici ile Drone yapmak için kullanılır. Sınırlı parça için alanı var ve end taşı ile güçlendirilmiş uçma kabiliyeti var.", + "oc:tooltip.eeprom": "Elektrikle silinebilir, programlanabilir salt okunur hafıza. Bilgisayarların çalışması için gerekli BIOS kodunu içerir.", + "oc:tooltip.fakeendstone": "Neredeyse aynısı, bu da havalanmaya meyilli.", + "oc:tooltip.geolyzer": "Yakındaki blokların sertliğini ölçmeye yarar. Bu bilgi cevher bulmak için bölgenin holgramını yapmaya yarayabilir.", + "oc:tooltip.graphicscard": "Ekranları güzel bilgilerle doldurur.\nAzami Çözünürlük: §f%sx%s§7\nAzami renk derinliği: §f%s§7\nİşlem/tick: §f%s§7", + "oc:tooltip.hoverboots": "Daha yükseğe sıçra, daha derine in, daha iyi yürü. Hepsi Uçan Bot (TM) sayesinde.", + "oc:tooltip.inkcartridge": "3B yazıcılara mürekkep koymak için kullanılır. Nedense makineye takılı kalmıyor.", + "oc:tooltip.inkcartridgeempty": "Yazıcının kartuşu yine birmiş. Mürekkebi doldurabilirsin. Ya da at gitsin. Nasıl olsa bir daha aynı yazmaz.", + "oc:tooltip.internetcard": "Bu kart ile gerçekten internete bağlanıp, TCP üzerinden HTTP istemlerinde bulunabilirsiniz.", + "oc:tooltip.interweb": "Tebrikler, bir ağ (?) kazandınız. Internete bununla bağlanabilirsiniz. Trollerin seviyesine inmmeyin.", + "oc:tooltip.ironnugget": "Demirden kopan bir parça, başka nasıl açıklayayım.", + "oc:tooltip.keyboard": "Yazı yazmak için ekranlara takılabilir.", + "oc:tooltip.hologram0": "Bilgisayarla kumanda edilen, voksel tabanlı yapıları göstermek için kullanılan hacimsel ekran.\nÇözünürlük: §f48x32x48§7\nAzami Ölçek: §f3x§7\nRenk derinliği: §fMonokrom§7", + "oc:tooltip.hologram1": "Bilgisayarla kumanda edilen, voksel tabanlı yapıları göstermek için kullanılan hacimsel ekran.\nÇözünürlük: §f48x32x48§7\nAzami Ölçek: §f3x§7\nRenk derinliği: §f3 Renkli§7", + "oc:tooltip.linkedcard": "Bunlar çiftler halinde üretilir ve sadece partneriyle iletişim kurarlar. İletişimleri mesafeyi ve boyutları engel tanımaz fakat çok güç ister.", + "oc:tooltip.linkedcard_channel": "§8Kanal: %s§7", + "oc:tooltip.manual": "OpenComputers hakkında ihtiyaç duyabileceğiniz her bilgi ve daha fazlası. Hem de sadece ... §ofiyatı öğrenmek için R'ye basın§7.", + "oc:tooltip.memory": "Bilgisayarların çalışması için gerekli. Ne kadar çok belleğiniz varsa o kadar karmaşık programlar çalıştırabilirsiniz.", + "oc:tooltip.microchip": "Siyah elektrikle çalışan zımbırtılar. Neden kızıltaşla çalıştıkları beni aşıyor.", + "oc:tooltip.microcontroller": "Mikroişlemciler bilgisayarların en asgari biçimi. Görev odaklıdırlar ve EEPROM'larında gelen tek bir programla çalışırlar.\n§cHarici parçalarla bağlantı kuramazlar.§7", + "oc:tooltip.microcontrollercase": "Mikroişlemcilerin temel bileşeni. Mikroişlemci yapmak için bir birleştirici kullanın ve başka parçalar ekleyin.", + "oc:tooltip.motionsensor": "Yakındaki canlıların hareketini algılar. Çalışması için canlılar görüş açısında olmalıdır.", + "oc:tooltip.nanomachines": "Kumanda birimi ve tüketilmek üzere bir sürü nanomakine, tabi sende o mide varse.", + "oc:tooltip.networkcard": "Uzaktaki, birbirine başka bloklarla bağlı (örneğin kablolarla) bilgisayarların birbirlerine mesajlar göndererek iletişim kurmasını sağlar.", + "oc:tooltip.poweracceptor": "Enerji dönüşüm hızı: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fBuildCraft MJ'si§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fFactorization Yükü§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fIndustrialCraft² EU'su§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fMekanism Joule'ü§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fThermal Expansion RF'i§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fResonant Engine Coulomb'u§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "Başka modların enerjilerini OpenComputers'ın kendi enerji birimine dönüştürür. Dönüşüm oranları:", + "oc:tooltip.powerdistributor": "Farklı ağlara enerji dağıtır. Ayrı kalması gereken alt-ağlara dönüştürücüden gelen enerjiyi dağıtmak için bire bir.", + "oc:tooltip.present": "... çoban armağanı. Verdiğimiz rahatsızlıktan dolayı. Hediyeyi açarak §kganimetlere§7 sahip olabilirsiniz.\n§8Doğru zamanda OpenComputers parçaları üreterek hediye kazanabilirsiniz.§7", + "oc:tooltip.print.BeaconBase": "§8Fener tabanı olarak işlev görür.", + "oc:tooltip.print.LightValue": "§8Işınan ışık: %s.", + "oc:tooltip.print.RedstoneLevel": "§8Kızıltaş çıktısı: %s.", + "oc:tooltip.printedcircuitboard": "Bellek, bilgisayar kartları vb.nin yapı taşı.", + "oc:tooltip.printer": "Kullanıcı tanımlı nesnelerin şamelyum ve mürekkep kullanılarak basılmasını sağlar. Bir bilgisayar tarafından yönetilmelidir. Bazı sebeplerden dolayı, çocuklardan uzak tutun.", + "oc:tooltip.raid": "Birden fazla diskin tek bir dosya sistemi altında birleştirilip bağlı tüm bilgisayarlar tarafından kullanılmasını sağlar.", + "oc:tooltip.rawcircuitboard": "Herhangi bir fırında kurutulabilir.", + "oc:tooltip.redstone": "Bloğun çevresinde kızıltaş iletimi ve çevresinden kızıltaş alıgalma yapabilir. Bağlı herhangi bir bilgisayar kumanda edebilir. Harici bir kızıltaş kartı gibi davranır.", + "oc:tooltip.redstonecard.ProjectRed": "§fProject: Red§7 §adestekli§7.", + "oc:tooltip.redstonecard.RedLogic": "§fRedLogic§7 §adestekli§7.", + "oc:tooltip.redstonecard.RedNet": "§fRedNet§7 §adestekli§7.", + "oc:tooltip.redstonecard.WirelessCBE": "§fWireless Redstone (ChickenBones)§7 §adestekli§7.", + "oc:tooltip.redstonecard.WirelessSV": "§fWireless Redstone (SlimeVoid)§7 §adestekli§7.", + "oc:tooltip.redstonecard": "Bilgisayarın veya robotun çevresine kızltaş sinyali iletimesini ve çevresinden kızıltaş sinyalı algılamasını sağlar.", + "oc:tooltip.relay": "Farklı ağları birbirine bağlamaya yarar. Sadece ağ mesajları iletilir, parçalar görünmez. Bunu Ağ kartlarını kullanırken bilgisayarları birbirinden izole etmek için kullanabilirsiniz.", + "oc:tooltip.robot": "Bilgisayarların aksine robotlar oyuncular gibi dünyada hareket edebilir ve ona değişiklikler yapabilir.\n§cHarici parçalara bağlanamaz.§7", + "oc:tooltip.robot_level": "§fSeviye§7: §a%s§7", + "oc:tooltip.robot_storedenergy": "§fDepolu enerji§7: §a%s§7", + "oc:tooltip.screen": "Yazı göster, kasadaki bir ekran kartı aracılığıyla.\nAzami çözünürlük: §f%sx%s§7\nAzami renk derinliği: §f%s§7", + "oc:tooltip.server": "Bu bir sunucu. Bundan çokça olabilir fakat bu (bir bilgisayar gibi) başka parçalarla geliştirilebilir. Sunucu rafına takılarak çalıştırılabilir.", + "oc:tooltip.server.Components": "Takılı Parçalar:", + "oc:tooltip.rack": "4 sunucunun veya rafa konulabilen parçaların takılmasını sağlar.", + "oc:tooltip.tablet": "Lua ile eğlence için bir tablet bilgisayar. Çömelirken aktifleştirirseniz kapanmaya sorlarsınız.", + "oc:tooltip.tabletcase": "Basit bir tablet kasası. Bir birleştirici ve başka parçalar ile bir tablet yapabilirsiniz..", + "oc:tooltip.terminal": "Menzili içinde bir sunucuyu uzaktan kumanda etmenize yarar. Taşınabilir ekran ve klavye gibi davranır. Shift+Sağ-Tık ile raftaki bir sunucuya bağlayabilirsiniz.", + "oc:tooltip.terminalserver": "Uzaktan Uçbirimin yöneticisi. Sanal bir ekranı ve klavyesi var.", + "oc:tooltip.texturepicker": " Bu alet, 3B yazıcıda şekil tanımlamak üzere bir bloğun yüzeyini ifade eden bir metin gösterir. Kesinlikle dokunun ismi değil. Sana yalan borcum mu var?", + "oc:tooltip.tier": "§8%s. Seviye", + "oc:tooltip.netsplitter": "Ayarlanabilir bir bağlantı noktası gibi davranır. Her yüzün bağlantısı bir İngiliz anahtarı ile değiştirilebilir. Her yüzün bağlantısı bir kızıltaş sinyali ile tersine dönüştürülebilr.", + "oc:tooltip.toolong": "Şuna basılı tutarak daha detaylı bilgi alabilirsiniz: [§f%s§7]", + "oc:tooltip.transistor": "Çoğu bilgisayar parçasının basit bir bileşeni. Biraz yamulmuş ama iş görür.", + "oc:tooltip.transposer": "Bitişiğindeki envanterler arasında katı ve sıvı değişimini sağlar.", + "oc:tooltip.upgradeangel": "Robotlara boşlukta (tutunacak yer yokken) blok koyma yetisi sağlar.", + "oc:tooltip.upgradebattery": "Cihazın depolayabileceği enerji miktarını arttırır. Böylece cihaz şarj olmadan daha uzun süre çalışabilir.\nKapasite: §f%s§7", + "oc:tooltip.upgradechunkloader": "Robot tek başına ormana giderse ve onu kimse görmezse hareket eder mi? Bu eklenti çalıştığından emin olur. Bir bölümü(16x16x256) sürekli yüklü tutar fakat sürekli enerji harcar.", + "oc:tooltip.upgradecontainercard": "Bu yuva birleştirilmiş cihazda kartların rahatlıkla takılıp çıkarılmasını sağlar.\nAzami Seviye: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "Bu yuva birleştirilmiş cihazda eklentilerin rahatlıkla takılıp çıkarılmasını sağlar.\nAzami Seviye: §f%s§7", + "oc:tooltip.upgradecrafting": "Robotun envanterinin sol üst köşesini çalışma masası gibi kullanmasını sağlar. Nesnelerin tarife göre yerleşmesi şarttır.", + "oc:tooltip.upgradedatabase": "Bu eklenti deste bilgisinin başka parçalar tarafından kullanılmak üzere saklanmasını sağlar.\nDesteklenen satır sayısı: §f%s§7", + "oc:tooltip.upgradeexperience": "Bu eklenti çeşitli eylemler aracalığıyla robotların deneyim kazanmasını sağlar. Robotlar ne kadar deneyimliyse o kadar enerji depolayabilir, o kadar hızlı blok toplayabilir ve aletleri o kadar etkili kullanabilir.", + "oc:tooltip.upgradegenerator": "Yolda yakıttan enerji üretimini sağlar. Yakıtları enerji değerlerine göre enerji üretmek için kullanır.\n§fVerimlilik§7: §a%s%%§7", + "oc:tooltip.upgradehover": "Bu eklenti robotun duvar tırmanmak zorunda kalmadan daha yükseğe çıkmasını sağlar.\nAzami irtifa: §f%s§7", + "oc:tooltip.upgradeinventory": "Bu eklenti bir robota veya drona envanter sağlar. Bu olmadan nesne tutamazlar.", + "oc:tooltip.upgradeinventorycontroller": "Bu eklenti robotların ve dronların harici envanterlerle iletişim kurmasını ve onlardaki aletlerle kendi aletini değişmesini sağlar.", + "oc:tooltip.upgrademf": "Dönüştürücülerin bitişiğinde olmadığı nesnelerle iletişim kurmasını sağlar.", + "oc:tooltip.upgrademf.Linked": "§fBağlantı kuruldu§7", + "oc:tooltip.upgrademf.Unlinked": "§fBağlantı yok§7", + "oc:tooltip.upgradeleash": "Drone gibi bazı cihazların belediye görevlilerine sataş...-özür dilerim. Bu cihaz aslında canlı(lar)ı bağlamak için kullanılıyormuş. İlğinç.", + "oc:tooltip.upgradenavigation": "Cihazın yönünü ve yerini tespit etmek için kullanılır. Yer bilgisi eklentiyi üretirken kullanılan haritanın merkez noktasına göredir.", + "oc:tooltip.upgradepiston": "Bu eklenti çok itici. Piston gibi blokları hareket ettirmeye yarar. Canlıları ve partikülleri hareket ettir§lmez§7.", + "oc:tooltip.upgradesign": "Tabelalardan yazı okumayı ve onlara yazı yazmayı sağlar.", + "oc:tooltip.upgradesolargenerator": "Güneşten enerji üretimini sağlar. Gökyüzü görüş açısında olmalıdır. Bir Stirling motorunun %%%s katı hızı kadar hızlı enerji üretebilir.", + "oc:tooltip.upgradetank": "Robotlara ve dronlara sıvı depolama alanı sağlar. Bu olmadan sıvı depolayamazlar.", + "oc:tooltip.upgradetankcontroller": "Bu eklenti robotlara ve dronlara harici sıvı depoları ile iletişim yeteneği sağlar. Harici depodan dahiliye, dahiliden hariciye sıvı iletimi sağlar.", + "oc:tooltip.upgradetractorbeam": "Nesne mıknatısı adı verilen çok gelişmiş bir teknolojiti edinin. Cihazın 3 block menzilinden nesneleri almasını sağlar.", + "oc:tooltip.waypoint": "Yönbulma eklentisi olan cihazlar için referans noktası oluşturur.", + "oc:tooltip.wirelessnetworkcard": "Normal ağ mesajlarının yanı sıra kablosuz ağ mesajlarının iletimini sağlar. §fSinyal gücünü§7 ayarlayarak mesajların ne kadar uzağa iletildiğini kontrol edebilirsiniz. Yüksek sinyal gücü daha çok enerji tüketir.", + "oc:tooltip.worldsensorcard": "Dünya hakkında yerçekimi ve nefes alınabilir bir atmosferi olup olmadığı gibi bilgileri toplar. Bilgiler kesin değildir. Üretici firma karttan edilen bilgilere göre verilen kararlar sonucu oluşan maddi ve manevi hiçbir hasardan sorumlu değildir. Avukatlarımız var. Paramız da. Denemeyi aklınızdan bile geçirmeyin.", + "oc:tooltip.Wrench": "İngiliz anahtarı tornavida karışımı bir şey. Öğrenmesi basit, ustalaşması zor.", + "achievement.oc.adapter": "Tak-Çalıştır", + "achievement.oc.adapter.desc": "Diğer modların ve elbette vanilla Minecraft blokları ile iletişim kur.", + "achievement.oc.assembler": "Harika", + "achievement.oc.assembler.desc": "Dünyayı ele geçirme vakti.", + "achievement.oc.cable": "Temiz Kablo", + "achievement.oc.cable.desc": "Patentli dolanma önleyici teknolojisi ile.", + "achievement.oc.capacitor": "Piller Ürüne Dahildir.", + "achievement.oc.capacitor.desc": "Durduramazsın.", + "achievement.oc.card": "Kredi Kartı Geçerlidir.", + "achievement.oc.card.desc": "Sizin için. Kesinlikle bir art niyetimiz yok.", + "achievement.oc.case": "Yangın Durumunda ...", + "achievement.oc.case.desc": "Çünkü küp kasalar en iyisi.", + "achievement.oc.charger": "Haydi şu işi bitirelim.", + "achievement.oc.charger.desc": "Şarj etsene seni... Kızıltaş sinyalini unutmuşum.", + "achievement.oc.chip": "En Küçüğünden Lütfen", + "achievement.oc.chip.desc": "Çünkü tüpler eskide kaldı.", + "achievement.oc.cpu": "Modifiye", + "achievement.oc.cpu.desc": "Haydi bu makineyi biraz zorlayalım.", + "achievement.oc.disassembler": "Ali Yazar, Veli Bozar", + "achievement.oc.disassembler.desc": "Nasıl olsa Sarı Çizmeli Mehmet Ağa bir gün öder hesabı.", + "achievement.oc.diskDrive": "Dönmedolap", + "achievement.oc.diskDrive.desc": "Az alan, güzel ses.", + "achievement.oc.drone": "Su gibi git...", + "achievement.oc.drone.desc": "... su gibi gel. Pahalısın lazımsın bize.", + "achievement.oc.eeprom": "Ya ben, ya hiç!", + "achievement.oc.eeprom.desc": "Bilgisayar için, önemli bir parça.", + "achievement.oc.floppy": "Güneş Tutulması", + "achievement.oc.floppy.desc": "Hepsini bozduk 1999'da.", + "achievement.oc.geolyzer": "Zonguldak", + "achievement.oc.geolyzer.desc": "Toprak ne ki, yeri bir metre kaz kömür çıkıyor.", + "achievement.oc.graphicsCard": "Yeni Nesil", + "achievement.oc.graphicsCard.desc": "Döviz kuru düşseydi fakir kalmazdım.", + "achievement.oc.hdd": "Hıyar Domates Dirhem", + "achievement.oc.hdd.desc": "Yanlış oldu, bekle dilimin ucunda.", + "achievement.oc.hologram": "Gelecek Boyut", + "achievement.oc.hologram.desc": "2B'tan sıkıldın mı?", + "achievement.oc.keyboard": "TozMıknatısı3000", + "achievement.oc.keyboard.desc": "Odanıza alın, odanız tertemiz olsun*. *TozMıknatısı300 dışında.", + "achievement.oc.microcontroller": "Kardeş", + "achievement.oc.microcontroller.desc": "Bilgisayarın küçük kardeşi.", + "achievement.oc.motionSensor": "Tuvalet Kabusu", + "achievement.oc.motionSensor.desc": "İşerken ışık söner mi acaba?", + "achievement.oc.networkCard": "Feysbuk", + "achievement.oc.networkCard.desc": "Hep birlikte kimsenin takmadığı şeyleri paylaşıp sosyal olduğumuzu sanıyoruz.", + "achievement.oc.openOS": "iŞlEtİm SiStEmİ NeY?", + "achievement.oc.openOS.desc": "Bilgisayarcıya git format atsın ablacım.", + "achievement.oc.powerDistributor": "Hayat Paylaşınca Güzel", + "achievement.oc.powerDistributor.desc": "Reklam yapmıyoruz. Güzel sözün telif hakkı mı olur?", + "achievement.oc.rack": "\"Bizim salondaki raf olur mu?\"", + "achievement.oc.rack.desc": "Tartmaz bir kere.", + "achievement.oc.raid": "Yerim Kalmadı.", + "achievement.oc.raid.desc": "Ne kadar çok \"aile fotoğrafı\"n varsa artık.", + "achievement.oc.ram": "Seçici Hatıra", + "achievement.oc.ram.desc": "Nedense sevdiklerimi daha çok hatırlıyorum.", + "achievement.oc.redstoneCard": "Regülatörü Tak.", + "achievement.oc.redstoneCard.desc": "Daha tüpler ısınacak.", + "achievement.oc.redstoneIO": "TRT", + "achievement.oc.redstoneIO.desc": "İlk yayın birazdan başlayacak.", + "achievement.oc.robot": "\"Gerginlik yaratma robot o robot.\"", + "achievement.oc.robot.desc": "\"Robot musun sen?\" \"Evet.\"", + "achievement.oc.screen": "Aç Kapa Çalışır.", + "achievement.oc.screen.desc": "Gerçekten, kızıltaş sinyalleri etkiliyor.", + "achievement.oc.server": "e-Devlet Kapısı", + "achievement.oc.server.desc": "Ne varsa orada, bu bilgileri kim saklayacak.", + "achievement.oc.switch": "Birini düzelt, diğerini boz.", + "achievement.oc.switch.desc": "Telekomcular niye işlerini yapamıyor.", + "achievement.oc.tablet": "Bu bir ilaç değildir.", + "achievement.oc.tablet.desc": "Çocuklardan uzak tutun. İstenmeyen harcamalara neden olabilir.", + "achievement.oc.transistor": "Düğmemtırak", + "achievement.oc.transistor.desc": "Bizim radyodada vardı bunlardan.", + "achievement.oc.wirelessNetworkCard": "\"Işınla bizi Scotty\"", + "achievement.oc.wirelessNetworkCard.desc": "\"Barış içinde size bir ileti getirdik.\"", + "death.attack.oc.nanomachinesOverload.1": "%s elini çok sıkı tuttu.", + "death.attack.oc.nanomachinesOverload.2": "%s kafayı yedi.", + "death.attack.oc.nanomachinesOverload.3": "%s öldü. Nanomakine varmış diyorlar.", + "death.attack.oc.nanomachinesHungry.1": "%s sizlere ömür. Nanomakineler diri diri yemiş.", + "death.attack.oc.nanomachinesHungry.2": "%s nanomakinelerine bakmadı.", + "death.attack.oc.nanomachinesHungry.3": "%s sindirildi.", + "nei.options.inventory.oredict": "Cevher Muadilleri Sözlüğü Adlarını Göster", + "nei.options.inventory.oredict.true": "Evet", + "nei.options.inventory.oredict.false": "Hayır", + "nei.usage.oc.Manual": "Kılavuzu Aç", + "option.oc.address": "Adres", + "option.oc.componentName": "Parça Adı", + "option.oc.energy": "Enerji" +} diff --git a/src/main/resources/assets/opencomputers/lang/zh_CN.lang b/src/main/resources/assets/opencomputers/lang/zh_CN.lang deleted file mode 100644 index de254f0db7..0000000000 --- a/src/main/resources/assets/opencomputers/lang/zh_CN.lang +++ /dev/null @@ -1,479 +0,0 @@ -# OpenComputer -# This is the Simplified Chinese file for localizations. -# Use [nl] to for a line break. 使用[nl]换行。 - -# Blocks -tile.oc.adapter.name=适配器 -tile.oc.assembler.name=装配器 -tile.oc.cable.name=线缆 -tile.oc.capacitor.name=电容 -tile.oc.carpetedcapacitor.name=踩踏发电电容 -tile.oc.case1.name=基础机箱 -tile.oc.case2.name=高级机箱 -tile.oc.case3.name=超级机箱 -tile.oc.casecreative.name=创造模式机箱 -tile.oc.chameliumblock.name=变色块 -tile.oc.charger.name=充电器 -tile.oc.disassembler.name=拆解器 -tile.oc.diskdrive.name=软盘驱动器 -tile.oc.endstone.name=末地石 -tile.oc.geolyzer.name=地质分析仪 -tile.oc.hologram1.name=基础全息地图投影仪 -tile.oc.hologram2.name=高级全息地图投影仪 -tile.oc.keyboard.name=键盘 -tile.oc.microcontroller.name=单片机 -tile.oc.motionsensor.name=运动传感器 -tile.oc.netsplitter.name=网络分配器 -tile.oc.powerconverter.name=能量转换器 -tile.oc.powerdistributor.name=能量分配器 -tile.oc.print.name=3D 打印 -tile.oc.printer.name=3D 打印机 -tile.oc.raid.name=Raid磁盘阵列 -tile.oc.redstone.name=红石 I/O 端口 -tile.oc.relay.name=中继器 -tile.oc.robot.name=机器人 -tile.oc.robotafterimage.name=机器人 -tile.oc.screen1.name=基础显示屏 -tile.oc.screen2.name=高级显示屏 -tile.oc.screen3.name=超级显示屏 -tile.oc.rack.name=机架 -tile.oc.transposer.name=转运器 -tile.oc.waypoint.name=路径点 - -# Items -item.oc.abstractbuscard.name=抽象类总线卡 -item.oc.acid.name=酸液 -item.oc.alu.name=算术逻辑单元(ALU) -item.oc.analyzer.name=分析器 -item.oc.apu0.name=高级加速运算单元(APU) -item.oc.apu1.name=超级加速运算单元(APU) -item.oc.apu2.name=创造加速运算单元(APU) -item.oc.arrowkeys.name=方向键 -item.oc.buttongroup.name=按钮组 -item.oc.cardbase.name=基板 -item.oc.chamelium.name=变色材料 -item.oc.circuitboard.name=电路板 -item.oc.componentbus0.name=基础组件总线 -item.oc.componentbus1.name=高级组件总线 -item.oc.componentbus2.name=超级组件总线 -item.oc.componentbus3.name=创造模式组件总线 -item.oc.controlunit.name=控制单元(CU) -item.oc.cpu0.name=基础中央处理器(CPU) -item.oc.cpu1.name=高级中央处理器(CPU) -item.oc.cpu2.name=超级中央处理器(CPU) -item.oc.cuttingwire.name=切割线 -item.oc.datacard0.name=基础数据卡 -item.oc.datacard1.name=高级数据卡 -item.oc.datacard2.name=超级数据卡 -item.oc.debugcard.name=调试卡 -item.oc.debugger.name=网络调试器 -item.oc.diamondchip.name=钻石芯片 -item.oc.disk.name=磁碟 -item.oc.diskdrivemountable.name=可挂载软盘驱动器 -item.oc.drone.name=无人机 -item.oc.dronecase0.name=基础无人机外壳 -item.oc.dronecase1.name=高级无人机外壳 -item.oc.dronecase3.name=创造无人机外壳 -item.oc.eeprom.name=EEPROM -item.oc.floppydisk.name=软盘 -item.oc.GraphicsCard0.name=基础显卡 -item.oc.GraphicsCard1.name=高级显卡 -item.oc.GraphicsCard2.name=超级显卡 -item.oc.HardDiskDrive0.name=基础磁盘驱动器 -item.oc.HardDiskDrive1.name=高级磁盘驱动器 -item.oc.HardDiskDrive2.name=超级磁盘驱动器 -item.oc.hoverBoots.name=悬浮靴 -item.oc.inkcartridge.name=墨盒 -item.oc.inkcartridgeempty.name=空墨盒 -item.oc.internetcard.name=因特网卡 -item.oc.Interweb.name=因特网 -item.oc.ironnugget.name=铁粒 -item.oc.linkedcard.name=连接卡 -item.oc.manual.name=开放式电脑使用手册 -item.oc.memory0.name=内存条-T1 -item.oc.memory1.name=内存条-T1.5 -item.oc.memory2.name=内存条-T2 -item.oc.memory3.name=内存条-T2.5 -item.oc.memory4.name=内存条-T3 -item.oc.memory5.name=内存条-T3.5 -item.oc.microchip0.name=简易微芯片 -item.oc.microchip1.name=高级微芯片 -item.oc.microchip2.name=超级微芯片 -item.oc.microcontrollercase0.name=基础单片机外壳 -item.oc.microcontrollercase1.name=高级单片机外壳 -item.oc.microcontrollercase3.name=创造单片机外壳 -item.oc.nanomachines.name=纳米机器 -item.oc.networkcard.name=网卡 -item.oc.numpad.name=数字键盘 -item.oc.present.name=一点小礼物…… -item.oc.printedcircuitboard.name=印刷电路板(PCB) -item.oc.rawcircuitboard.name=未加工电路板 -item.oc.redstonecard0.name=基础红石卡 -item.oc.redstonecard1.name=高级红石卡 -item.oc.server0.name=基础服务器 -item.oc.server1.name=高级服务器 -item.oc.server2.name=超级服务器 -item.oc.server3.name=创造模式服务器 -item.oc.tablet.name=平板电脑 -item.oc.tabletcase0.name=基础平板电脑外壳 -item.oc.tabletcase1.name=高级平板电脑外壳 -item.oc.tabletcase3.name=创造模式平板电脑外壳 -item.oc.terminal.name=终端 -item.oc.terminalserver.name=终端服务器 -item.oc.texturepicker.name=纹理选择器 -item.oc.transistor.name=晶体管 -item.oc.upgradeangel.name=天使方块升级 -item.oc.upgradebattery0.name=基础电池升级 -item.oc.upgradebattery1.name=高级电池升级 -item.oc.upgradebattery2.name=超级电池升级 -item.oc.upgradechunkloader.name=区块载入升级 -item.oc.upgradecontainercard0.name=基础卡槽 -item.oc.upgradecontainercard1.name=高级卡槽 -item.oc.upgradecontainercard2.name=超级卡槽 -item.oc.upgradecontainerupgrade0.name=基础升级组件容器 -item.oc.upgradecontainerupgrade1.name=高级升级组件容器 -item.oc.upgradecontainerupgrade2.name=超级升级组件容器 -item.oc.upgradecrafting.name=合成升级 -item.oc.upgradedatabase0.name=基础数据库升级 -item.oc.upgradedatabase1.name=高级数据库升级 -item.oc.upgradedatabase2.name=超级数据库升级 -item.oc.upgradeexperience.name=经验升级 -item.oc.upgradegenerator.name=发电机升级 -item.oc.upgradehover0.name=基础悬浮升级 -item.oc.upgradehover1.name=高级悬浮升级 -item.oc.upgradeinventory.name=物品栏升级 -item.oc.upgradeinventorycontroller.name=物品栏控制器升级 -item.oc.upgradeleash.name=缰绳升级 -item.oc.upgrademf.name=MFU -item.oc.upgradenavigation.name=导航升级 -item.oc.upgradepiston.name=活塞升级 -item.oc.upgradesign.name=告示牌读写升级 -item.oc.upgradesolargenerator.name=太阳能发电机升级 -item.oc.upgradetank.name=水箱升级 -item.oc.upgradetankcontroller.name=储罐升级 -item.oc.upgradetractorbeam.name=牵引光束升级 -item.oc.upgradetrading.name=交易升级 -item.oc.wirelessnetworkcard0.name=基础无线网卡 -item.oc.wirelessnetworkcard1.name=高级无线网卡 -item.oc.worldsensorcard.name=世界传感器卡 -item.oc.wrench.name=螺丝刀扳手 - -# Entities -entity.oc.Drone.name=无人机 - -# GUI -oc:gui.Analyzer.Address=§6地址§f:%s -oc:gui.Analyzer.AddressCopied=地址已复制到剪贴板。 -oc:gui.Analyzer.ChargerSpeed=§6充电速度§f:%s -oc:gui.Analyzer.ComponentName=§6组件名§f:%s -oc:gui.Analyzer.Components=§6已连接组件的数量§f:%s -oc:gui.Analyzer.CopyToClipboard=点击以复制到剪贴板。 -oc:gui.Analyzer.LastError=§6上一个错误§f:%s -oc:gui.Analyzer.RobotName=§6名称§f:%s -oc:gui.Analyzer.RobotOwner=§6主人§f:%s -oc:gui.Analyzer.RobotXp=§6经验§f:%s(等级 %s) -oc:gui.Analyzer.StoredEnergy=§6存储能量§f:%s -oc:gui.Analyzer.TotalEnergy=§6存储能量上限§f:%s -oc:gui.Analyzer.Users=§6用户§f:%s -oc:gui.Analyzer.WirelessStrength=§6信号强度§f:%s -oc:gui.Assembler.Collect=收集输出 -oc:gui.Assembler.Complexity=复杂度:%s/%s -oc:gui.Assembler.InsertCase=插入一个机箱 -oc:gui.Assembler.InsertCPU=插入一个CPU -oc:gui.Assembler.InsertRAM=插入一些存储器 -oc:gui.Assembler.Progress=进度:%s%%(%s) -oc:gui.Assembler.Run=组装 -oc:gui.Assembler.Warning.BIOS=BIOS -oc:gui.Assembler.Warning.GraphicsCard=显卡 -oc:gui.Assembler.Warning.Inventory=物品栏升级 -oc:gui.Assembler.Warning.Keyboard=键盘 -oc:gui.Assembler.Warning.OS=运行环境 -oc:gui.Assembler.Warning.Screen=显示屏 -oc:gui.Assembler.Warnings=§e警告§7:推荐的组件丢失。 -oc:gui.Chat.NewVersion=有新版本可用:%s -oc:gui.Chat.TextureName=§7材质名为§a%s§f。 -oc:gui.Chat.WarningClassTransformer=执行类转换器时发生§c错误§f。请务必反馈此问题,反馈时一并附上 FML 的日志文件 §alatest.log/fml-server-latest.log§f,谢谢! -oc:gui.Chat.WarningFingerprint=§c警告§f——指纹校验不匹配!应为 '§a%s§f',实为 '§e%s§f'。如果你不是在反混淆环境下运行游戏的模组开发者,我们§l强烈§f建议你重新下载开放式电脑模组,因为你现在使用的 JAR 文件已被修改。 -oc:gui.Chat.WarningLink=无法打开链接:%s -oc:gui.Chat.WarningLuaFallback=无法使用原生 Lua 库,电脑无法持久化当前状态。它们会在区块载入时重启。 -oc:gui.Chat.WarningProjectRed=你目前使用的 Project:Red 版本不兼容 OpenComputers,请更新 Project:Red。 -oc:gui.Chat.WarningRecipes=加载合成表时遇到一个或多个错误。部分物品或因此无法合成。查阅日志文件以获取详细信息。 -oc:gui.Chat.WarningSimpleComponent=某个扩展 Mod 使用了 §aSimpleComponent§f 接口但§e做错了一些事情§f,导致组件逻辑无法注入。查阅日志文件以获取详细信息。 -oc:gui.Drive.Managed=受管理 -oc:gui.Drive.Unmanaged=不受管理 -oc:gui.Drive.ReadOnlyLock=只读锁定 -oc:gui.Drive.ReadOnlyLockWarning=§l只读§r锁定.除非抹掉该磁盘,否则无法关闭. -oc:gui.Drive.Warning=§l警告§r:切换模式将会导致当前磁盘上所有数据丢失! -oc:gui.Error.ComponentOverflow=过多的组件连接了计算机。 -oc:gui.Error.InternalError=内部错误,请检查日志文件。这可能是个Bug。 -oc:gui.Error.NoCPU=这台计算机上没有安装CPU。 -oc:gui.Error.NoEnergy=能量不足。 -oc:gui.Error.NoRAM=这台计算机上没有安装内存。 -oc:gui.Error.OutOfMemory=内存溢出。 -oc:gui.Manual.Blocks=开放式电脑-方块 -oc:gui.Manual.Home=主页 -oc:gui.Manual.Items=开放式电脑-物品 -oc:gui.Manual.Warning.BlockMissing=该方块不可用。 -oc:gui.Manual.Warning.ImageMissing=未找到图片。 -oc:gui.Manual.Warning.ItemMissing=该物品不可用。 -oc:gui.Manual.Warning.OreDictMissing=该矿物词典条目不可用。 -oc:gui.Raid.Warning=§4添加磁盘会抹掉该磁盘。[nl]移除磁盘会使Raid无法使用。 -oc:gui.Robot.Power=能量 -oc:gui.Robot.TurnOff=关闭 -oc:gui.Robot.TurnOn=开启[nl]§7使用分析器来追踪错误。§r -oc:gui.ServerRack.Back=后 -oc:gui.ServerRack.Bottom=底部 -oc:gui.ServerRack.Left=左 -oc:gui.ServerRack.None=无 -oc:gui.ServerRack.Right=右 -oc:gui.Rack.Enabled=启用 -oc:gui.Rack.Disabled=禁用 -oc:gui.ServerRack.Top=上 -oc:gui.Switch.PacketsPerCycle=数据包 / 周期 -oc:gui.Switch.QueueSize=队列长度 -oc:gui.Switch.TransferRate=传输速率 -oc:gui.Terminal.InvalidKey=无效键,看上去另一个终端已绑定到这台服务器。 -oc:gui.Terminal.OutOfRange=无信号。 - -# Containers -oc:container.adapter=适配器 -oc:container.case=计算机 -oc:container.charger=充电器 -oc:container.disassembler=分解器 -oc:container.diskdrive=磁盘驱动器 -oc:container.printer=打印机 -oc:container.raid=Raid磁盘阵列 -oc:container.relay=中继器 -oc:container.server=服务器 -oc:container.rack=机架 -oc:container.tabletwrapper=平板电脑 - -# Keybinds -key.clipboardPaste=粘贴到剪贴板 - -# Item / Block Tooltips -oc:tooltip.abstractbuscard=允许计算机向§f星门科技2§7的抽象类总线发送或接收 LIP 数据包。 -oc:tooltip.acid=一种有毒的虚构液体,通常只有某些海盗会使用它们。然而它应该有别的用途。 -oc:tooltip.adapter=用于控制非电脑组件的方块,比如原版或者来自其它模组的方块。 -oc:tooltip.alu=用来做算术及逻辑运算而不用你亲自代劳,它更能胜任这份差事。 -oc:tooltip.analyzer=用于显示方块信息,比如它们的§f地址§7和§f组件名称§7。如果计算机未能正常关闭它也会显示使计算机崩溃的报错。 -oc:tooltip.apu=如果你只是需要一个额外的卡槽,这就是你需要的自带 GPU(或者集成图形处理器 IGP)的 CPU。[nl] 支持的组件:§f%s§7[nl] 最大分辨率:§f%sx%s§7[nl] 最高色深:§f%s§7[nl] 运算/tick:§f%s§7 -oc:tooltip.assembler=允许使用若干不同的电脑组件来组装机器人。 -oc:tooltip.cable=连接方块的廉价选择。 -oc:tooltip.capacitor=储能以备不时之需。能够非常快速地充电或放电。 -oc:tooltip.carpetedcapacitor=储能以备不时之需。能够非常快速地充电或放电。有羊或豹猫踩上去的时候就能充电。 -oc:tooltip.cardbase=正如其名,它是用来安装其它扩展卡的基本卡板。 -oc:tooltip.case=计算机机箱是构建计算机的基本方块,也是各种§f扩展卡§7,§f存储器§7以及§f硬盘§7的外壳。[nl] 格子:§f%s§7 -oc:tooltip.chamelium=3D打印的基本原材料。不要误吸:或造成失明和暂时失去意识。 -oc:tooltip.chameliumblock=整洁干净的玩意。对于彩色 3D 打印相当实用,或者只是拿它的整洁干净的色彩来装饰你的华丽的基地。 -oc:tooltip.charger=将电容器的能量传输给相邻机器人。传输效率取决于输入的§f红石信号§7强度,无信号意味着不给机器人充电,而最强信号则意味着全速给机器人充电。 -oc:tooltip.circuitboard=现在我们已经取得一些进展。可以通过蚀刻来得到印制电路板。 -oc:tooltip.controlunit=用来控制……控制东西……的单元。你需要这玩意儿来做 CPU。所以反正啦,这个非常重要。 -oc:tooltip.componentbus=这个扩展能让服务器同时与更多组件通讯,工作原理类似 CPU。[nl] 支持的组件:§f%s§7 -oc:tooltip.cpu=所有计算机最核心的组件,它的时钟频率有点不可靠,但是你考虑到它是靠便携日晷来运行的,还能指望什么呢?[nl] 支持组件:§f%s§7 -oc:tooltip.cpu.Architecture=架构:§f%s§7 -oc:tooltip.cuttingwire=用于将粘土块切割成电路板的形状。使用一次就会坏掉,这可能使得成为历来最低效的工具。 -oc:tooltip.datacard0=提供数种高级算法,例如散列、压缩和解压缩算法。 -oc:tooltip.datacard1=提供数种高级算法,例如散列、AES 加密、压缩和解压缩算法。 -oc:tooltip.datacard2=提供数种高级算法,例如散列、AES 加密、椭圆曲线加密、压缩和解压缩算法。 -oc:tooltip.debugcard=创造模式物品,它能让你更简易的操控世界来进行测试。请自己承担它带来的危险。 -oc:tooltip.debugger=用于在 OpenComputer 的内部网络格栅中输出调试信息。请在开发者指导下使用。 -oc:tooltip.diamondchip=一小粒正在发光的钻石。永远不会破镜重圆。 -oc:tooltip.disassembler=将物品分解成基础组件。§l警告§7:分解得到的物品会有 %s%% 的几率在分解过程中损坏! -oc:tooltip.disk=用于制造持久存储设备的原始媒介材料。 -oc:tooltip.diskdrive.CC=§a支持§7 ComputerCraft 的软盘。 -oc:tooltip.diskdrive=用来读写软盘。可以给机器人安装使其可以插入软盘。 -oc:tooltip.diskdrivemountable=和普通软驱用途一样,但必须装载在机架里。 -oc:tooltip.diskusage=磁盘使用:%s/%s 字节 -oc:tooltip.diskmodemanaged=模式:管理 -oc:tooltip.diskmodeunmanaged=模式:不受管理 -oc:tooltip.drone=无人机是一种轻量而快速的侦查单位,自身拥有有限的载物空间。 -oc:tooltip.dronecase=这个外壳将会在组装机中组装出无人机。这个外壳可容纳少量组件,并内建末地石驱动的悬浮系统。 -oc:tooltip.eeporm=小型的可编程存储器,可存储电脑启动所用的BIOS。 -oc:tooltip.fakeendstone=几乎可以以假乱真,甚至更加轻盈! -oc:tooltip.geolyzer=可以检测周围方块的硬度。这些信息对编写全息地图程序或检测矿物都有很大的帮助。 -oc:tooltip.graphicscard=用来改变屏幕上的显示内容。最高分辨率:§f%sx%s§7[nl] 最高色深:§f%s§7[nl] 运算/tick:§f%s§7 -oc:tooltip.hoverboots=跳得更高,摔得更深,走得更快。一切尽在 Hover Boots™,已获顶级认证。 -oc:tooltip.inkcartridge=用于重新填装3D打印机的墨水。因为某些原因这些墨水不必保留在打印机里。 -oc:tooltip.inkcartridgeempty=此墨盒经过深度干燥处理。用颜料重新装填,抑或弃置。看我脸色行事。 -oc:tooltip.internetcard=因特网卡可以让你发送HTTP请求以及使用货真价实的TCP套接字。 -oc:tooltip.interweb=恭喜,你赢得了一个因特网。你可以使用因特网卡来连接它。注意:不要水贴钓鱼 -oc:tooltip.ironnugget=颗粒状的铁,所以叫铁粒啦,蠢蠢的感觉…… -oc:tooltip.keyboard=可以连接屏幕来输入显示文本。 -oc:tooltip.hologram0=一个能通过电脑控制来投射出任何三维结构的全息投影仪.[nl] 分辨率:§f48x32x48§7 [nl] 最大显示规模:§f3x§7 [nl] 最高色深:§f黑白§7 -oc:tooltip.hologram1=使用电脑控制的高级全息投影仪。[nl] 分辨率:§f48x32x48§7 [nl] 最大显示规模:§f4x§7 [nl] 最高色深:§f三原色§7 -oc:tooltip.linkedcard=连接卡可以成对合成,并且它们只能与各自对应的连接卡交互。然而,它们之间的交互没有距离限制,甚至可以跨越不同的世界。但它们传递信息所需要的能量消耗不多不少。 -oc:tooltip.linkedcard_channel=§8频道:%s§7 -oc:tooltip.manual=包含你需要的关于 OpenComputer 的一切信息。还有更多。为此你只需要……§o请按R键继续§7。 -oc:tooltip.memory=电脑运行必备。内存越大,你就可以运行越复杂的程序。 -oc:tooltip.microchip=通常也管这种芯片叫集成电路。我也不知道为什么能和红石一起运行,但事实如此。 -oc:tooltip.microcontroller=单片机是被压缩到不能再压缩的电脑。它们更多是用于执行特殊任务,并只运行一个内建于EEPROM中的简单程序。[nl] §c不能和外置组件相连接。§7 -oc:tooltip.microcontrollercase=单片机基础组件。在组装机中为其添加各种组件并组装单片机。 -oc:tooltip.motionsensor=可以检测附近生物的动向。检测需要无遮挡物的环境。 -oc:tooltip.nanomachines=控制单元,同时还是一堆可以吸入的纳米机器(如果你愿意的话)。 -oc:tooltip.networkcard=允许通过其他方块(比如线缆)相连的远程计算机能够向彼此发送信息进行交互。 -oc:tooltip.poweracceptor=能量转换速度:§f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§fMJ§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§fCharge§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§fEU§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§fJoules§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§fRF§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§fCoulombs§7: §a%s:%s§7 -oc:tooltip.powerconverter=将其它模组的能量转化为本模组的内部能源形式。转换比: -oc:tooltip.powerdistributor=在不同网络中分配能量。很实用于通过包含多个理论上独立子网络的转换器在系统中共享能量。 -oc:tooltip.present=……它随时为你的麻烦待命。打开此礼物将有机会获得 §kphat lewt§7![nl]§8在时机合适时,合成 OpenComputers 的物品。§7 -oc:tooltip.print.BeaconBase=§8可用作信标底座。 -oc:tooltip.print.LightValue=§8光强:%s。 -oc:tooltip.print.RedstoneLevel=§8红石信号强度:%s。 -oc:tooltip.printedcircuitboard=扩展卡、内存等组件的基础板。 -oc:tooltip.printer=利用变色物质和墨盒打印出用户自定义的形状。需要用电脑进行配置。不要让幼童接触。就是这样。 -oc:tooltip.raid=允许将三块硬盘组成一个大文件系统供所有与其连接的电脑使用。 -oc:tooltip.rawcircuitboard=能在支持原版熔炉配方的炉子中加工强化。 -oc:tooltip.redstone=能够在方块周围接收或发送红石信号。可以被与之相连的计算机控制。基本上就像一个外置红石卡。 -oc:tooltip.redstonecard.Charset=§a兼容§7 §fSimpleLogic§7。 -oc:tooltip.redstonecard.ProjectRed=§a兼容§7 §fProject Red§7。 -oc:tooltip.redstonecard.RedLogic=§a兼容§7 §fRedLogic§7。 -oc:tooltip.redstonecard.RedNet=§a兼容§7 §fRedNet§7。 -oc:tooltip.redstonecard.WirelessCBE=§a兼容§7 §f无线红石(ChickenBones)§7。 -oc:tooltip.redstonecard.WirelessSV=§a兼容§7 §f无线红石(SlimeVoid)§7。 -oc:tooltip.redstonecard=允许在计算机和机器人四周接收或发送红石信号。 -oc:tooltip.relay=可将不同网络连接在一起。仅信息可通过此设备传递,其它元件仍不可见。用法举例:可用于保持多个独立网络的通信。 -oc:tooltip.robot=和计算机不同,机器人能够移动并且像玩家那样与世界中的东西交互。然而,它们§o不能§r§7直接与外设进行交互! -# The underscore makes sure this isn't hidden with the rest of the tooltip。 -oc:tooltip.robot_level=§f等级§7:§a%s§7。 -oc:tooltip.robot_storedenergy=§f存储能量§7:§a%s§7。 -oc:tooltip.screen=显示文本,运作需要机箱中的显卡。[nl] 最高分辨率:§f%sx%s§7[nl] 最高色深:§f%s§7 -oc:tooltip.server=这就是服务器,它与许多其它的服务器拥有相似功能,但这台服务器可以使用组件进行升级,就像升级机箱一样。它也可以放入服务器机架中运行。 -oc:tooltip.server.Components=已安装的组件: -oc:tooltip.rack=它能装下四台服务器或者别的设备。 -oc:tooltip.tablet=一台平板电脑,为 Lua 新人准备。潜行时右击可强制关机。 -oc:tooltip.tabletcase=平板电脑的基础外壳。在组装机中添加组件以制造平板电脑。 -oc:tooltip.terminal=可以远程控制服务器,不过前提是你处于信号范围内。使用方法相同于显示屏与键盘. Shift 右击机架中的服务器可以绑定对应的终端。 -oc:tooltip.terminalserver=在工作范围内可与终端连接,以提供远程控制功能。内建有虚拟显示器和键盘。 -oc:tooltip.texturepicker=这个工具可以显示放块表面的描述,可用于3D打印定义中。完全不是材质名,完全不是。很抱歉先生,不是。 -oc:tooltip.tier=§8等级 %s -oc:tooltip.netsplitter=作为动态连接器的存在。每个面的连接设定可通过扳手切换。红石信号可反相所有面的连接设定。 -oc:tooltip.toolong=按住 [§f%s§7] 显示详细物品信息。 -oc:tooltip.transistor=大多数电脑组件中最基础的元素。看上去有些扭曲,但它是有用的。 -oc:tooltip.transposer=可在相邻容器之间自动转移物品和液体。 -oc:tooltip.upgradeangel=允许机器人凭空放置方块,甚至是在没有任何依附的情况下。 -oc:tooltip.upgradebattery=增加机器人存储能量的上限,让其能够运作很长一段时间也不必充电。[nl] 电容:§f%s§7 -oc:tooltip.upgradechunkloader=如果机器人走进了一片森林,周围没有任何生物载入区块,那么它还能继续移动吗? 这个升级组件可以让你确定这点。它能让机器人所处的区块保持载入,但这会让能量不断地消耗。 -oc:tooltip.upgradecontainercard=卡容器组件可以让你方便随意的将卡装入机器人或从中移出。[nl] 最高等级:§f%s§7 -oc:tooltip.upgradecontainerupgrade=卡容器升级组件可以让你方便的将卡装入另一个卡容器或从中移出。[nl] 最高等级:§f%s§7 -oc:tooltip.upgradecrafting=能让机器人的物品栏左上角部分成为合成界面。物品必须在合成界面里排列整齐。 -oc:tooltip.upgradedatabase=能对物品信息进行分类,供其它组件稍后取用。[nl] 可支持条目数量:§f%s§7 -oc:tooltip.upgradeexperience=这个升级能让机器人在执行一些工作时获取经验并累积。它们拥有越多的累积经验,就能够存储越多的能量,越快的挖掘方块并且使用工具时拥有更高的效率。 -oc:tooltip.upgradegenerator=可以让机器人通过燃料充能。燃烧物品得到的发电量取决于燃料的等级。[nl] §f发电效率§7:§a%s%%§7 -oc:tooltip.upgradehover=这个升级组件可让机器人飞得更高而无需爬墙。[nl] 最大高度:§f%s§7 -oc:tooltip.upgradeinventory=这个升级组件为机器人提供了物品背包。如果没有这个升级,机器人将不能存储物品。 -oc:tooltip.upgradeinventorycontroller=这个升级组件可以控制机器人使其与外部容器互动,并能允许机器人将装备上的工具替换成其物品栏中的其它物品。 -oc:tooltip.upgrademf=能让适配器访问不与其相邻的方块。 -oc:tooltip.upgrademf.Linked=§f已连接§7 -oc:tooltip.upgrademf.Unlinked=§f没有连接§7 -oc:tooltip.upgradeleash=这个升级组件可让诸如无人机之类的设备绑在 Isaa——不好意思,*清清嗓子* 我刚才说错了。我只是被告知这原本是用来拴动物的绳子。可以拴很多动物,好奇怪啊。 -oc:tooltip.upgradenavigation=可以让机器人确认所处位置以及定位。定位地点即为用于合成此升级组件用到的地图的中心点。 -oc:tooltip.upgradepiston=这个升级十分有用。它能让机器人像活塞那样移动方块,但 §l不能§7 移动实体。 -oc:tooltip.upgradesign=允许机器人读写告示牌。 -oc:tooltip.upgradesolargenerator=可以让机器人在太阳光的照射下四处奔走。机器人顶部需要无任何方块阻挡。产能速度是斯特林引擎的 %s%%。 -oc:tooltip.upgradetank=为你的机器人装上一个可存储流体的水箱。如果没有升级此功能,你的机器人将无法在内部存储流体。 -oc:tooltip.upgradetankcontroller=这个升级能让你的机器人与外界水箱交互,向其传输流体。 -oc:tooltip.upgradetractorbeam=十分先进的高科技,别名“物品磁铁”。它能让机器人在任何地方捡起3格范围内的掉落物。 -oc:tooltip.waypoint=为安装有导航升级的设备提供参考点。 -oc:tooltip.wirelessnetworkcard=允许在发送正常信息的情况下发送无线网络信息。请确保已设置好 §f信号强度§7 否则不会发出信息。越高的信号强度会导致越高的能量消耗。 -oc:tooltip.worldsensorcard=可读取关于世界的各种信息,例如重力加速度和是否有可供呼吸的大气。使用风险自负。制造商对由卡片输出结果造成的损失不承担任何法律责任。我们不仅有律师,还有现金。不要试图告倒我们。 -oc:tooltip.wrench=螺丝刀和扳手混合体,新手友好,高手毁灭者。 - -#Achievements -achievement.oc.adapter=Plug In Baby -achievement.oc.adapter.desc=和其它 MOD 的方块(甚至是原版 Minecraft 方块)进行互动! -achievement.oc.assembler=完美 -achievement.oc.assembler.desc=是时候征服世界了! -achievement.oc.cable=并不是脏兮兮的电线 -achievement.oc.cable.desc=搭配权威认证的反 SCP-229 科技 -achievement.oc.capacitor=本设备含电池 -achievement.oc.capacitor.desc=不可能阻止它 -achievement.oc.card=我们接受卡片 -achievement.oc.card.desc=这是为方便您的使用。我们保证这不是别有用心。 -achievement.oc.case=以备后患 -achievement.oc.case.desc=Because cuboid towers are the best. -achievement.oc.charger=那就开始做吧! -achievement.oc.charger.desc=开始充——等等!又忘记红石信号了。 -achievement.oc.chip=全是些小东西 -achievement.oc.chip.desc=曾经真空管也是这样的。 -achievement.oc.cpu=超频 -achievement.oc.cpu.desc=是时候好好利用这些计算循环了 -achievement.oc.disassembler=销毁! -achievement.oc.disassembler.desc=当你发现你的想法很美好,但现实其实很残酷的时候,就用这个吧。 -achievement.oc.diskDrive=Roundabout -achievement.oc.diskDrive.desc=Inferior capacity but such delicious sound. -achievement.oc.drone=啊!飞走了! -achievement.oc.drone.desc=冷静冷静,直接用核弹让他们在轨道上瞬间爆炸 -achievement.oc.eeprom=每台电脑 -achievement.oc.eeprom.desc=仅限一个,就是这样。这是用来决定最终启动顺序的,明白吗!? -achievement.oc.floppy=The One Ri- Disk -achievement.oc.floppy.desc=不要与flappy混淆。[nl]注:软盘英文为floppy disk -achievement.oc.geolyzer=深入地心 -achievement.oc.geolyzer.desc=It has extraordinary qualities. -achievement.oc.graphicsCard=LastGen -achievement.oc.graphicsCard.desc=The way it's meant to be... uh... rendered. Yeah. That. -achievement.oc.hdd=Hot Dog Dealer(热狗推销员) -achievement.oc.hdd.desc=等等我不是那个意思。让我想想,好像想起来什么意思了…… -achievement.oc.hologram=下一个次元 -achievement.oc.hologram.desc=因为 2D 很无聊啊。或者本来就这样? -achievement.oc.keyboard=吸尘器 3000 型 -achievement.oc.keyboard.desc=强烈建议使用时保持将其翻过来并持续摇晃的习惯,以清理其中异物。 -achievement.oc.microcontroller=Little Sisters -achievement.oc.microcontroller.desc=计算机的小妹妹哦 -achievement.oc.motionSensor=Got the Moves -achievement.oc.motionSensor.desc=Like Steve Swagger. -achievement.oc.networkCard=现在我们在聊天了! -achievement.oc.networkCard.desc=Keep in touch with those distant relatives via transitive relations. -achievement.oc.openOS=启动 -achievement.oc.openOS.desc=One OS to - wait, I used that one already? Dang. -achievement.oc.powerDistributor=分享即关怀 -achievement.oc.powerDistributor.desc=当你在平衡供电时需要帮助的时候。 -achievement.oc.rack=Dat Rack -achievement.oc.rack.desc=我不知道你想歪到哪里去了,我已经说得很明白了,就是服务器架。 -achievement.oc.raid=LFG -achievement.oc.raid.desc=Heroic plzkthx. -achievement.oc.ram=随机存取存储器 -achievement.oc.ram.desc=恭喜,你的操作完全正确。 -achievement.oc.redstoneCard=联系 -achievement.oc.redstoneCard.desc=模拟信号时间到。 -achievement.oc.redstoneIO=The Outsider -achievement.oc.redstoneIO.desc=将红石信号传播到你需要的地方! -achievement.oc.robot=Beep Boop -achievement.oc.robot.desc=灭绝! -achievement.oc.screen=试过反复开关这屏幕没? -achievement.oc.screen.desc=真没有。毕竟红石信号就可以开关这屏幕了。 -achievement.oc.server=专用 -achievement.oc.server.desc=云服务,现已正式推出。 -achievement.oc.switch=复杂拓扑学 -achievement.oc.switch.desc=远离易碎货物,因为包裹可能会掉下来。 -achievement.oc.tablet=不要吸入 -achievement.oc.tablet.desc=同时请远离儿童,以避免不必要的信用卡过度透支。 -achievement.oc.transistor=Tell Red I said "Hi." -achievement.oc.transistor.desc=制作一个晶体管然后让它工作。然后听录音带。不用谢我。 -achievement.oc.wirelessNetworkCard=信号 -achievement.oc.wirelessNetworkCard.desc=是时候让数据包踏遍每一个角落了。 - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code。 -death.attack.oc.nanomachinesOverload.1=%s 过于贪婪。 -death.attack.oc.nanomachinesOverload.2=%s 神经断裂。 -death.attack.oc.nanomachinesOverload.3=%s 的纳米机器失控暴走了。 -death.attack.oc.nanomachinesHungry.1=%s 被纳米机器吃掉了。 -death.attack.oc.nanomachinesHungry.2=%s 忘记喂饱纳米机器了。 -death.attack.oc.nanomachinesHungry.3=%s 已被分解吸收了。 - -# NEI Integration -nei.options.inventory.oredict=显示矿物词典名 -nei.options.inventory.oredict.true=是 -nei.options.inventory.oredict.false=否 -nei.usage.oc.Manual=打开手册 - -# Waila Integration -option.oc.address=地址 -option.oc.componentName=组件名 -option.oc.energy=能量 diff --git a/src/main/resources/assets/opencomputers/lang/zh_TW.lang b/src/main/resources/assets/opencomputers/lang/zh_TW.lang deleted file mode 100644 index 0f7717bab8..0000000000 --- a/src/main/resources/assets/opencomputers/lang/zh_TW.lang +++ /dev/null @@ -1,468 +0,0 @@ -# OpenComputer -# This is the Traditional Chinese file for localizations. by mymag - -# Blocks -tile.oc.accesspoint.name=§c橋接器§7 -tile.oc.adapter.name=連接器 -tile.oc.assembler.name=機器組裝台 -tile.oc.cable.name=線纜 -tile.oc.capacitor.name=電容 -tile.oc.case1.name=基礎電腦機殼 -tile.oc.case2.name=高級電腦機殼 -tile.oc.case3.name=超級電腦機殼 -tile.oc.casecreative.name=電腦機殼(創造模式) -tile.oc.chameliumblock.name=變色凝膠磚 -tile.oc.charger.name=充電器 -tile.oc.disassembler.name=拆解機 -tile.oc.diskdrive.name=磁碟機 -tile.oc.endstone.name=終界石 -tile.oc.geolyzer.name=硬度分析機 -tile.oc.hologram1.name=全息投影機 (1級) -tile.oc.hologram2.name=全息投影機 (2級) -tile.oc.keyboard.name=鍵盤 -tile.oc.microcontroller.name=微型控制器 -tile.oc.motionsensor.name=動作感應器 -tile.oc.netsplitter.name=網路路由器 -tile.oc.powerconverter.name=能源轉換器 -tile.oc.powerdistributor.name=能源分配器 -tile.oc.print.name=3D 列印 -tile.oc.printer.name=3D 列印機 -tile.oc.raid.name=磁碟陣列機(NAS) -tile.oc.redstone.name=紅石I/O -tile.oc.relay.name=中繼器 -tile.oc.robot.name=機器人 -tile.oc.robotafterimage.name=機器人 -tile.oc.screen1.name=黑白電腦顯示器 -tile.oc.screen2.name=彩色電腦顯示器 -tile.oc.screen3.name=高畫質電腦顯示器 -tile.oc.rack.name=伺服器機架 -tile.oc.switch.name=路由器 -tile.oc.transposer.name=差轉機 -tile.oc.waypoint.name=路點 - -# Items -item.oc.abstractbusCard.name=抽象的介面卡 -item.oc.acid.name=蝕刻藥水 -item.oc.alu.name=算邏運算單元(ALU) -item.oc.analyzer.name=分析器 -item.oc.apu0.name=加速處理單元 (APU) (1級) -item.oc.apu1.name=加速處理單元 (APU) (2級) -item.oc.apu2.name=加速處理單元 (APU) (創造模式) -item.oc.arrowkeys.name=方向鍵 -item.oc.buttongroup.name=按鈕組 -item.oc.cardbase.name=主板 -item.oc.chamelium.name=變色凝膠 -item.oc.circuitboard.name=電路板 -item.oc.componentbus0.name=基礎排線 -item.oc.componentbus1.name=高級排線 -item.oc.componentbus2.name=超級排線 -item.oc.controlunit.name=控制單元 (CU) -item.oc.cpu0.name=中央處理器 (CPU) (1級) -item.oc.cpu1.name=中央處理器 (CPU) (2級) -item.oc.cpu2.name=中央處理器 (CPU) (3級) -item.oc.cuttingwire.name=切割線 -item.oc.datacard0.name=資料卡 (1級) -item.oc.datacard1.name=資料卡 (2級) -item.oc.datacard2.name=資料卡 (3級) -item.oc.debugcard.name=除錯卡 -item.oc.debugger.name=網路除錯卡 -item.oc.disk.name=磁碟 -item.oc.drone.name=無人機 -item.oc.dronecase0.name=無人機外殼 (1級) -item.oc.dronecase1.name=無人機外殼 (2級) -item.oc.dronecase3.name=無人機外殼 (創造模式) -item.oc.eeprom.name=電子抹除式可複寫唯讀記憶體(EEPROM) -item.oc.floppydisk.name=磁碟片 -item.oc.graphicscard0.name=顯示卡 -item.oc.graphicscard1.name=彩色顯示卡 -item.oc.graphicscard2.name=高畫質顯示卡 -item.oc.harddiskdrive0.name=硬碟機 (1級) -item.oc.harddiskdrive1.name=硬碟機 (2級) -item.oc.harddiskdrive2.name=硬碟機 (3級) -item.oc.hoverboots.name=懸停靴 -item.oc.inkcartridge.name=墨水匣 -item.oc.inkcartridgeempty.name=墨水匣(空) -item.oc.internetcard.name=上網卡 -item.oc.interweb.name=因特網 -item.oc.ironnugget.name=鐵粒 -item.oc.linkedcard.name=連結卡 -item.oc.manual.name=開放式電腦手冊 -item.oc.memory0.name=記憶卡 (1級) -item.oc.memory1.name=記憶卡 (1.5級) -item.oc.memory2.name=記憶卡 (2級) -item.oc.memory3.name=記憶卡 (2.5級) -item.oc.memory4.name=記憶卡 (3級) -item.oc.memory5.name=記憶卡 (3.5級) -item.oc.microchip0.name=微晶片 (1級) -item.oc.microchip1.name=微晶片 (2級) -item.oc.microchip2.name=微晶片 (3級) -item.oc.microcontrollercase0.name=微控制器外殼 (Tier 1) -item.oc.microcontrollercase1.name=微控制器外殼 (Tier 2) -item.oc.microcontrollercase3.name=微控制器外殼 (Creative) -item.oc.nanomachines.name=納米機器 -item.oc.networkcard.name=網路卡 -item.oc.numpad.name=數字鍵盤 -item.oc.present.name=一個小東西... -item.oc.printedcircuitboard.name=印刷電路板(PCB) -item.oc.rawcircuitboard.name=電路板材料 -item.oc.redstonecard0.name=紅石卡 (1級) -item.oc.redstonecard1.name=紅石卡 (2級) -item.oc.server0.name=伺服器 (1級) -item.oc.server1.name=伺服器 (2級) -item.oc.server2.name=伺服器 (3級) -item.oc.server3.name=伺服器 (創造模式) -item.oc.tablet.name=平板電腦 -item.oc.tabletcase0.name=平板電腦保護套 (1級) -item.oc.tabletcase1.name=平板電腦保護套 (2級) -item.oc.tabletcase3.name=平板電腦保護套 (創造模式) -item.oc.terminal.name=遠端終端機 -item.oc.texturepicker.name=紋理選擇器 -item.oc.transistor.name=電晶體 -item.oc.upgradeangel.name=天使升級 -item.oc.upgradebattery0.name=電池升級 (1級) -item.oc.upgradebattery1.name=電池升級 (2級) -item.oc.upgradebattery2.name=電池升級 (3級) -item.oc.upgradechunkloader.name=區塊載入器升級 -item.oc.upgradecontainercard0.name=卡片集裝箱 (1級) -item.oc.upgradecontainercard1.name=卡片集裝箱 (2級) -item.oc.upgradecontainercard2.name=卡片集裝箱 (3級) -item.oc.upgradecontainerupgrade0.name=集裝箱升級 (1級) -item.oc.upgradecontainerupgrade1.name=集裝箱升級 (2級) -item.oc.upgradecontainerupgrade2.name=集裝箱升級 (3級) -item.oc.upgradecrafting.name=合成升級 -item.oc.upgradedatabase0.name=資料庫升級 (1級) -item.oc.upgradedatabase1.name=資料庫升級 (2級) -item.oc.upgradedatabase2.name=資料庫升級 (3級) -item.oc.upgradeexperience.name=經驗升級 -item.oc.upgradegenerator.name=發電機升級 -item.oc.upgradehover0.name=懸停升級 (1級) -item.oc.upgradehover1.name=懸停升級 (2級) -item.oc.upgradeinventory.name=物品欄升級 -item.oc.upgradeinventorycontroller.name=物品控制器升級 -item.oc.upgradeleash.name=皮帶升級 -item.oc.upgradenavigation.name=導航升級 -item.oc.upgradepiston.name=活塞升級 -item.oc.upgradesign.name=告示牌 I/O 升級 -item.oc.upgradesolargenerator.name=太陽能發電升級 -item.oc.upgradetank.name=水箱升級 -item.oc.upgradetankcontroller.name=高級水箱升級 -item.oc.upgradetractorbeam.name=牽引光束升級 -item.oc.wirelessnetworkcard0.name=無線網卡 -item.oc.wirelessnetworkcard1.name=無線網卡 -item.oc.worldsensorcard.name=世界傳感器卡 -item.oc.wrench.name=螺絲刀扳手 - -# Entities -entity.oc.drone.name=無人機 - -# GUI -oc:gui.Analyzer.Address=§6位址§f: %s -oc:gui.Analyzer.AddressCopied=位址複製到剪貼簿. -oc:gui.Analyzer.ChargerSpeed=§6充電速度§f: %s -oc:gui.Analyzer.ComponentName=§6設備元件名稱§f: %s -oc:gui.Analyzer.Components=§6連接元件的數量§f: %s -oc:gui.Analyzer.CopyToClipboard=點擊複製到剪貼簿。 -oc:gui.Analyzer.LastError=§6上一個錯誤§f: %s -oc:gui.Analyzer.RobotName=§6名字§f: %s -oc:gui.Analyzer.RobotOwner=§6持有者§f: %s -oc:gui.Analyzer.RobotXp=§6經驗值§f: %s -oc:gui.Analyzer.StoredEnergy=§6儲存能量§f: %s -oc:gui.Analyzer.TotalEnergy=§6總儲存能量§f: %s -oc:gui.Analyzer.Users=§6使用者§f: %s -oc:gui.Analyzer.WirelessStrength=§6信號強度§f: %s -oc:gui.Assembler.Collect=收集機器人 -oc:gui.Assembler.Complexity=複雜性: %s/%s -oc:gui.Assembler.InsertCase=裝入電腦機殼(CASE) -oc:gui.Assembler.InsertCPU=裝入中央處理器(CPU) -oc:gui.Assembler.InsertRAM=裝入一些記憶體(RAM) -oc:gui.Assembler.Progress=進度: %s%% (%s) -oc:gui.Assembler.Run=開始組裝機器人 -oc:gui.Assembler.Warnings=§e警告§7: 缺少某些建議的零組件 -oc:gui.Assembler.Warning.GraphicsCard=顯示卡 -oc:gui.Assembler.Warning.Inventory=物品欄升級組件 -oc:gui.Assembler.Warning.Keyboard=鍵盤 -oc:gui.Assembler.Warning.OS=開機(作業系統) 中等 -oc:gui.Assembler.Warning.Screen=螢幕 -oc:gui.Assembler.Warnings=§e警告§7: 缺少建議的零組件 -oc:gui.Chat.NewVersion=有新版本可用: %s -oc:gui.Chat.TextureName=§7紋理名稱是 §a%s§f. -oc:gui.Chat.WarningClassTransformer=There were §cerrors§f running the class transformer. Please report this, together with your (full!) FML §alatest.log§f/§afml-server-latest.log§f logfile, thank you! -oc:gui.Chat.WarningFingerprint=§c警告§f - 指紋不符!預期 '§a%s§f' 但得到 '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with. -oc:gui.Chat.WarningLink=無法打開鏈接: %s -oc:gui.Chat.WarningLuaFallback=本機 LUA 函式庫無法使用,電腦將無法維持狀態,他會重新啟動並且載入區塊 -oc:gui.Chat.WarningProjectRed=你正在使用的 Project: Red 模組與 OpenComputers 不相容. 請嘗試更新 Project: Red. -oc:gui.Chat.WarningRecipes=There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information. -oc:gui.Chat.WarningSimpleComponent=An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information. -oc:gui.Drive.Managed=受管 -oc:gui.Drive.Unmanaged=非受管 -oc:gui.Drive.Warning=§l警告§r: 切換模式會導致目前儲存在硬碟上的所有資料遺失! -oc:gui.Error.ComponentOverflow=太多的元件連接到電腦上了 -oc:gui.Error.InternalError=內部錯誤,請查看日誌文件。這可能是一個錯誤。 -oc:gui.Error.NoCPU=電腦內沒有安裝中央處理器(CPU). -oc:gui.Error.NoEnergy=沒有能源 -oc:gui.Error.NoRAM=電腦內沒有安裝記憶卡 -oc:gui.Error.OutOfMemory=記憶體不足 -oc:gui.Manual.Blocks=開放式電腦方塊 -oc:gui.Manual.Home=首頁 -oc:gui.Manual.Items=開放式電腦物品 -oc:gui.Manual.Warning.BlockMissing=方塊不存在 -oc:gui.Manual.Warning.ImageMissing=沒有找到圖片 -oc:gui.Manual.Warning.ItemMissing=物品不存在 -oc:gui.Manual.Warning.OreDictMissing=礦物字典不存在 -oc:gui.Raid.Warning=§4放入的硬碟機會清除硬碟資料.[nl] 移除硬碟機會刪除磁碟陣列機(NAS)資料. -oc:gui.Robot.Power=能量 -oc:gui.Robot.TurnOff=關閉 -oc:gui.Robot.TurnOn=開啟 -oc:gui.Rack.None=無 -oc:gui.Rack.Back=背面 -oc:gui.Rack.Bottom=底部 -oc:gui.Rack.Left=左 -oc:gui.Rack.Right=右 -oc:gui.Rack.Top=上 -oc:gui.Switch.TransferRate=週期率 -oc:gui.Switch.PacketsPerCycle=封包 / 週期 -oc:gui.Switch.QueueSize=隊列大小 -oc:gui.Terminal.InvalidKey=按鍵無效時,最有可能另一端已經綁定到伺服器。 -oc:gui.Terminal.OutOfRange=沒訊號. - -# Containers -oc:container.accesspoint=橋接器 -oc:container.adapter=連接器 -oc:container.case=電腦 -oc:container.charger=充電器 -oc:container.disassembler=拆解機 -oc:container.diskdrive=硬碟機 -oc:container.printer=印表機 -oc:container.raid=磁碟陣列機(NAS) -oc:container.relay=中繼器 -oc:container.server=伺服器 -oc:container.rack=伺服器機架 -oc:container.switch=路由器 -oc:container.tabletwrapper=平板電腦 - -# Keybinds -key.clipboardPaste=貼上剪貼簿 -key.materialCosts=顯示材料成本 - -# Item / Block Tooltips -oc:tooltip.accesspoint=就像一個切換器,但它會接收無線封包並且轉換無線的封包變成有線的封包. -oc:tooltip.abstractbuscard=允許與 §fStargateTech 2§7 抽象卡傳送與接收 LIP 封包. -oc:tooltip.acid=一種有毒的假液相物質,通常只有某些海盜會使用它們.[nl]它的腐蝕特性令它非常完美地適用于蝕刻電路板的材料. -oc:tooltip.adapter=用于控制非電腦組件的方塊,[nl]比如原版或者來自其他模組的方塊. -oc:tooltip.alu=用來做算邏運算以免你親自代勞,[nl]它更勝任這份差事. -oc:tooltip.analyzer=用于顯示方塊信息,比如它們的§f地址§7和§f組件名稱§7.[nl]如果計算機未能正常關閉它也會顯示使得計算機崩潰的報錯. -oc:tooltip.apu=這是 CPU 組成 GPU (或 IGP)的一個處理器, 如果你需要額外的卡片插槽,他可以讓你減少一片顯示卡.[nl] 支援的零組件: §f%s§7[nl] 最大分辨率: §f%sx%s§7[nl] 最大顏色深度: §f%s§7[nl] 運作/tick: §f%s§7 -oc:tooltip.assembler=機器組裝台可以從不同的電腦零組件來組裝一台機器人。 -oc:tooltip.cable=連接方塊的廉價選擇. -oc:tooltip.capacitor=儲能以備他用.[nl]能夠非常快速地充電放電. -oc:tooltip.cardbase=正如其名,它是用來安裝其他擴展卡的基本卡板. -oc:tooltip.case=計算機電腦機殼是構建計算機的基本方塊,[nl]也是各種§f介面卡§7,§f記憶體§7以及§f硬碟§7的外殼.[nl] 格子: §f%s§7 -oc:tooltip.chamelium=3D列印的原料. 不可吞食: 會導致失明與臨時性身體消失 -oc:tooltip.chameliumblock=乾淨清潔. 方便用於3D列印, 只是一個純色的乾淨方塊, 彩色方塊可以讓你發揮創意 -oc:tooltip.charger=將電容器的能量傳輸給相鄰機器人.[nl]傳輸效率取決于輸入的§f紅石信號§7強度,無信號意味著不給機器人充電,而最強信號則意味著全速給機器人充電. -oc:tooltip.circuitboard=現在我們已經取得一些進展.[nl]可以通過蝕刻來得到印制板. -oc:tooltip.controlunit=用來控制...控制東西...的單元.[nl]你需要這玩意兒來做CPU.所以反正啦,這個非常重要. -oc:tooltip.componentbus=這個擴充套件能讓伺服器同時與更多組件通訊, 工作原理類似CPU.[nl] 支援的組件: §f%s§7. -oc:tooltip.cpu=所有計算機最核心的組件,它的時鐘頻率有點不可靠,[nl]但是你考慮到它是靠便攜日晷來運行的你還能指望啥呢? -oc:tooltip.cpu.architecture=架構: §f%s§7 -oc:tooltip.cuttingwire=用于將粘土塊切割成電路板的形狀.[nl]使用一次就會壞掉,這可能使得成為歷來最低效的工具. -oc:tooltip.datacard0=提供了一些先進的演算法,如散列處理以及 deflate/inflate. -oc:tooltip.datacard1=提供了一些先進的演算法,如散列處理, AES 加密 以及 deflate/inflate. -oc:tooltip.datacard2=提供了一些先進的演算法,如散列處理, AES 加密, elliptic curve cryptography and deflate/inflate. -oc:tooltip.debugcard=創造模式物品, 它能讓你更簡易的操控世界來進行測試. 請自己承擔它帶來的危險. -oc:tooltip.debugger=可以使用輸出錯誤訊息到 OC's 網路. 只能使用在開發模式. -oc:tooltip.disassembler=拆解還原成它原來的零組件 §l警告§7: 拆解物品過程中 %s%% 有可能破壞掉這些零組件! -oc:tooltip.disk=用於制造持久存儲設備的原始媒介材料. -oc:tooltip.diskdrive.cc=已經§a支持§7使用另一個電腦模組(ComputerCraft)的軟碟. -oc:tooltip.diskdrive=用來讀寫軟碟. -oc:tooltip.diskusage=磁碟使用量: %s/%s Byte -oc:tooltip.diskmodemanaged=模式: 受管 -oc:tooltip.diskmodeunmanaged=模式: 非受管 -oc:tooltip.drone=無人機重量輕,速度快的偵察部隊,擁有有限的載貨空間。 -oc:tooltip.dronecase=這個外殼是用來建立無人機的組裝外殼。它有足夠的空間用於少量組件,並終界石供電懸浮。 -oc:tooltip.eeprom=非常小的程式化儲存裝置,可裝入電腦 BIOS 用來開機. -oc:tooltip.fakeendstone=幾乎一樣好真實的東西,甚至模仿其floatiness! -oc:tooltip.geolyzer=允許掃描周邊地區的方塊硬度,這個訊息可用於產生區域的全息圖,或用來檢測礦石是非常有用的. -oc:tooltip.graphicscard=用來改變顯示器顯示內容.[nl]最高分辨率: §f%sx%s§7[nl] 最大色深: §f%s§7 -oc:tooltip.hoverboots=跳得更高, 跌幅更深, 走得更快. 這是一個全新專利的 懸停靴 (TM). -oc:tooltip.inkcartridge=用於填充墨水的3D打印機。很神奇的原理,不必保留在印表機中。 -oc:tooltip.inkcartridgeempty=這個墨盒已經被吸乾。用染料填充它。或者把它扔掉。看看你是否在乎。 -oc:tooltip.internetcard=這個介面卡允許你發出 HTTP要求到真實的 TCP sockets. -oc:tooltip.interweb=恭喜,你贏取了 (1) 因特網,你可以使用網路卡連接它,小心,不要餵食巨魔. -oc:tooltip.ironnugget=顆粒狀的鐵,所以叫鐵粒啦,蠢蠢的感覺... -oc:tooltip.keyboard=可以連接顯示器,能夠輸入顯示文本. -oc:tooltip.hologram0=可以透過電腦進行控制以顯示任意像素體積的結構體.[nl] 解析度: §f48x32x48§7 [nl] 最大縮放: §f3x§7 [nl] 色深: §f單色§7 -oc:tooltip.hologram1=可以透過電腦進行控制以顯示任意像素體積的結構體.[nl] 解析度: §f48x32x48§7 [nl] 最大縮放: §f4x§7 [nl] 色深: §f三色§7 -oc:tooltip.linkedcard=這些都是製作成對的,而且只能與它的合作卡通訊,然而,它可以在任何距離,甚至是誇世界溝通,但是發送訊息的消耗的能量非常高. -oc:tooltip.linkedcard_channel=§8頻道: %s§7 -oc:tooltip.manual=所有你需要知道開放式電腦模組的訊息都在裡面.而且他的需求非常便宜... §o請按 R 繼續§7. -oc:tooltip.materialcosts=按著 [§f%s§7] 顯示材料成本. -oc:tooltip.materials=需求材料: -oc:tooltip.memory=電腦運行必備.[nl]記憶體越大,你就可以運行越復雜的程序. -oc:tooltip.microchip=通常也管這種芯片叫集成電路.[nl]我也不知道為啥能和紅石一起運行,但事實如此啊. -oc:tooltip.microcontroller=微控制器是一部小型電腦. 它的目的是在處理非常具體的任務, 只能執行儲存在 EEPROM 的單一獨立程式.[nl] §c無法連結外部零組件.§7 -oc:tooltip.microcontrollercase=微控制器的基本組件. 將其放入組裝機裡添加其他的零組件 -oc:tooltip.motionsensor=可以檢測到附近活物的移動,需要在視線內不能有阻擋物. -oc:tooltip.nanomachines=如果你敢,插入控制單元與一組奈米機器 -oc:tooltip.networkcard=允許通過其他方塊(比如線纜)相連的遠程計算機能夠向彼此發送信息進行交互. -oc:tooltip.poweracceptor=能源轉換速度: §f%s/t§7 -oc:tooltip.powerconverter.BuildCraft=§f建築模組minecraft焦耳(BC-MJ)§7: §a%s:%s§7 -oc:tooltip.powerconverter.Factorization=§f因式分解 (Charge)§7: §a%s:%s§7 -oc:tooltip.powerconverter.IndustrialCraft2=§f工業時代 EU(IC²-EU)§7: §a%s:%s§7 -oc:tooltip.powerconverter.Mekanism=§f通用機器焦耳(Joules)§7: §a%s:%s§7 -oc:tooltip.powerconverter.ThermalExpansion=§f熱能擴展RF(TE-RF)§7: §a%s:%s§7 -oc:tooltip.powerconverter.ResonantEngine=§f諧振引擎庫侖§7: §a%s:%s§7 -oc:tooltip.powerconverter=將其他模組的能量轉化為本模組的內部能源形式.轉換率: -oc:tooltip.powerdistributor=在不同網絡中分配能量.[nl]用來通過包含多個理論上獨立的子網絡的轉換器在系統中共享能量時很實用. -oc:tooltip.present=... for your troubles. Open this present for a chance to receive some §kphat lewt§7![nl]§8Craft OpenComputers items when the time is right for a chance to receive a present.§7 -oc:tooltip.print.BeaconBase=§8一盞燈的基座 -oc:tooltip.print.LightValue=§8發出的光: %s. -oc:tooltip.print.RedstoneLevel=§8紅石輸出: %s. -oc:tooltip.printedcircuitboard=基本的構建方塊用來裝入擴充卡與記憶體模組 -oc:tooltip.printer=允許使用玩家自定義的墨水匣 Chamelium 與 Ink Cartridges. 必須使用電腦來進行配置. 遠離小朋友 -oc:tooltip.raid=使用三個硬碟機然後連接電腦成為一個更大容量的資料儲存系統 -oc:tooltip.printedcircuitboard=擴展卡,記憶體等的基板. -oc:tooltip.rawcircuitboard=能夠在熔爐或者類似爐子中加工強化成其他材料. -oc:tooltip.redstone=能夠在方塊周圍接收以及發送紅石信號.[nl]可以被與之相連的計算機控制.基本上就像一個外置紅石卡. -oc:tooltip.redstonecard.ProjectRed=已經§a支持§fProjectRed§7. -oc:tooltip.redstonecard.RedLogic=已經§a支持§7§f紅石邏輯(RedLogic)§7中的紅石系統. -oc:tooltip.redstonecard.RedNet=已經§a支援§7MFR中的§f紅石網絡(RedNet)§7. -oc:tooltip.redstonecard.WirelessCBE=已經§a支援§f無線紅石 (ChickenBones)§7. -oc:tooltip.redstonecard.WirelessSV=已經§a支援§f無線紅石 (SlimeVoid)§7. -oc:tooltip.redstonecard=能夠在計算機和機器人四周接收以及發送紅石信號. -oc:tooltip.robot=和計算機不同,機器人能夠移動并且像玩家那些與世界之中的東西交互.[nl]然而,它們§o不能§r§7直接與外設進行交互! -# The underscore makes sure this isn't hidden with the rest of the tooltip. -oc:tooltip.robot_level=§f等級§7: §a%s§7. -oc:tooltip.robot_storedenergy=§f儲能§7: §a%s§7. -oc:tooltip.screen=由電腦機殼內的顯示卡控制來顯示文字.[nl]最高分辨率: §f%sx%s§7[nl] 最大色深: §f%s§7 -oc:tooltip.server=這是一台伺服器, 他非常棒, 但是他可以使用元件去升級他功能就像電腦一樣. 他可以插入伺服器機架上執行. -oc:tooltip.server.Components=安裝的元件: -oc:tooltip.rack=提供安裝最多達四個伺服器。為每個伺服器提供了一個內置的虛擬鍵盤和螢幕元件,相當於一個遠程終端。 -oc:tooltip.switch=允許設備相互連接不同的網絡.[nl]僅能傳遞網絡信息,通過路由器方式設備并不互相可見.[nl]例如可以通過這種方式來建立獨立網絡但仍允許其使用網卡通訊. -oc:tooltip.tablet=一台平板電腦, 可以在旅行中使用LUA. 使用潛行按鍵來啟動或是關閉他 -oc:tooltip.tabletcase=平板電腦的機殼,將它放置在機器組裝台內製作一台平板電腦 -oc:tooltip.terminal=允許遠程控制伺服器,只要你在它的範圍內。就像一個隨身型螢幕和鍵盤。按住Shift鍵並滑鼠右鍵單擊某個服務器的服務器機架終端綁定它。 -oc:tooltip.texturepicker=可以工具允許顯示方塊該面向的紋理描述, 主要用於3D印表機. 完全沒有紋理名字,沒了! -oc:tooltip.tier=§8等級 %s -oc:tooltip.netsplitter=作為一個動態連接器。每邊的連接可以通過用扳手擊中它進行切換。所有邊的連接可以通過應用紅石信號反轉。 -oc:tooltip.toolong=按住潛行鍵([§f%s§7])以查看詳細提示信息. -oc:tooltip.transistor=在多數其他計算機的零件中都很基礎的元件.[nl]引腳有點彎了,但還能用。 -oc:tooltip.transposer=允許相鄰的箱子與液體容器之間的物品液體transferral -oc:tooltip.upgradeangel=讓機器人能夠憑空放置方塊在任何地方,即使沒有參考點可以依附放置。 -oc:tooltip.upgradebattery=增加機器人可儲存的能量,讓它可以工作更長的時間不需要補充能量. [nl] 容量: §f%s§7 -oc:tooltip.upgradechunkloader=如果機器人在森林中移動,周圍沒有其他玩家,它真的會動嗎? 這個升級組件可以確保它持續地在作用中,它使區塊持續在載入狀態,但會不斷消耗能量,維持它。 -oc:tooltip.upgradecontainercard=這個集裝箱升級組件允許動態裝配機器人內的某個插卡. [nl] 最大等級: §f%s§7 -oc:tooltip.upgradecontainerupgrade=這個集裝箱升級組件允許動態裝配機器人內的某個升級組件 [nl] 最大等級: §f%s§7 -oc:tooltip.upgradecrafting=使得機器人能夠將其物品欄的左上區域作為合成網格來制做物品.[nl]物品欄內物品擺放必須與工作臺中一致. -oc:tooltip.upgradedatabase=這個升級允許儲存用於檢索與其他組件的堆疊訊息.[nl] 支持的條目: §f%s§7 -oc:tooltip.upgradeexperience=這個升級組件,能夠讓機器人執行各種操作,累積更多的經驗,然後它會獲得更多的能量,更多的儲存空間,速度更快,它收割更有效率,也能使用工具。 -oc:tooltip.upgradegenerator=用來不斷地消耗燃料發電,燃燒物品并基于它們的燃燒值隨著時間推移產生電力.[nl] §f效率§7: §a%s%%§7。 -oc:tooltip.upgradehover=可以升級可以讓機器人飛離地面更高,而且不用爬牆.[nl] 最大高度: §f%s§7 -oc:tooltip.upgradeinventory=這個升級組件提供機器人一個物品放置的空間,沒有這個升級組件,機器人將不能再內部儲存任何物品。 -oc:tooltip.upgradeinventoryController=這個升級組件可以讓機器人更好的控制如何與外部互動物品,並且允許他交換或是裝備物品欄內的工具。 -oc:tooltip.upgradeleash=允許一些設備,如無人機, 來綁定 Isaa- excuse me... *chatter* My apologies. 我只是被告知這其實是用來綁住多種動物在木樁上. -oc:tooltip.upgradenavigation=可以用來確定機器人的位置和方向。該位置是相對於被用來製作這個升級地圖的中心。 -oc:tooltip.upgradepiston=這個升級十分有用. 它能讓機器人像活塞那樣移動方塊, 但 §l不能§7 移動實體. -oc:tooltip.upgradesign=允許讀取文字與寫入文字到告示牌 -oc:tooltip.upgradesolarGenerator=在移動中可以從陽光獲取能量. 機器人上方必須保持無遮蔽狀態才能. 產生能量 %s%% 速度的能量給引擎。 -oc:tooltip.upgradetank=為你的機器人裝上一個可存儲流體的水箱. 如果沒有升級此功能, 你的機器人將無法在內部存儲流體. -oc:tooltip.upgradetankcontroller=這個升級能讓你的機器人與外界水箱交互, 向其傳輸流體. -oc:tooltip.upgradetractorbeam=裝備機器人具有非常先進的技術,綽號"物品磁鐵"。它能夠拾取三個方塊內的任何地方。 -oc:tooltip.waypoint=提供一個參考點到與導航設備的升級。 -oc:tooltip.wirelessnetworkcard=允許在發送正常信息外無線發送網絡信息.[nl]請確保已設置好§f信號強度§7否則不會發出無線數據包。 -oc:tooltip.worldsensorcard=允許讀取世界訊息,比如重力,以及是否在大氣下,傳回結果. -oc:tooltip.wrench=螺絲刀和扳手的混合體,這個工具是簡單易學,但很難掌握。 - -#Achievements -achievement.oc.adapter=插上寶寶 -achievement.oc.adapter.desc=Interact with blocks from other mods and even vanilla Minecraft! -achievement.oc.assembler=美妙 -achievement.oc.assembler.desc=Time to take over the world! -achievement.oc.cable=不是一個骯髒的電線 -achievement.oc.cable.desc=With patented anti-cable-spaghetti technology. -achievement.oc.capacitor=包括電池 -achievement.oc.capacitor.desc=You cannot stop it. -achievement.oc.card=我們接受信用卡 -achievement.oc.card.desc=For your convenience. No ulterior motive, promise. -achievement.oc.case=出現故障時 -achievement.oc.case.desc=Because cuboid towers are the best. -achievement.oc.charger=好吧,讓我們做到這一點 -achievement.oc.charger.desc=Chaaaaaaaaaarg- dang, forgot the redstone signal again. -achievement.oc.chip=所有小事 -achievement.oc.chip.desc=Because vacuum tubes are so yesteryear. -achievement.oc.cpu=超頻 -achievement.oc.cpu.desc=Time to make good use of those computing cycles. -achievement.oc.disassembler=從頭開始 -achievement.oc.disassembler.desc=In case one of your brilliant ideas turns out to be not that brilliant after all. -achievement.oc.diskDrive=環狀交叉路 -achievement.oc.diskDrive.desc=Inferior capacity but such delicious sound. -achievement.oc.drone=飛走 -achievement.oc.drone.desc=Keep calm and nuke it from orbit. -achievement.oc.eeprom=只能有1 -achievement.oc.eeprom.desc=Per computer, that is. For deterministic boot order, you know? -achievement.oc.floppy=在一個RI-磁碟 -achievement.oc.floppy.desc=Not to be confused with Flappy. -achievement.oc.geolyzer=腳踏實地 -achievement.oc.geolyzer.desc=It has extraordinary qualities. -achievement.oc.graphicsCard=LastGen -achievement.oc.graphicsCard.desc=The way it's meant to be... uh... rendered. Yeah. That. -achievement.oc.hdd=熱狗經銷商 -achievement.oc.hdd.desc=No wait, that's not what that meant. Hang on, almost got it... -achievement.oc.hologram=下一個維度 -achievement.oc.hologram.desc=Because 2D is boring. Or is it? -achievement.oc.keyboard=DirtCatcher3000 -achievement.oc.keyboard.desc=It is highly recommended to resist the urge to flip them around and shake them. -achievement.oc.microcontroller=小妹妹 -achievement.oc.microcontroller.desc=The small sibling of computers. -achievement.oc.motionSensor=能動 -achievement.oc.motionSensor.desc=Like Steve Swagger. -achievement.oc.networkCard=現在我們談論! -achievement.oc.networkCard.desc=Keep in touch with those distant relatives via transitive relations. -achievement.oc.openOS=開機 -achievement.oc.openOS.desc=One OS to - wait, I used that one already? Dang. -achievement.oc.powerDistributor=共享是關懷 -achievement.oc.powerDistributor.desc=When you need some help balancing all that power. -achievement.oc.rack=Dat Rack -achievement.oc.rack.desc=I don't know what you're thinking of, I clearly meant the server rack. -achievement.oc.raid=LFG -achievement.oc.raid.desc=Heroic plzkthx. -achievement.oc.ram=隨機存取存儲器 -achievement.oc.ram.desc=Congratulations, you're Doin' It Right. -achievement.oc.redstoneCard=聯繫 -achievement.oc.redstoneCard.desc=Time to go analog. -achievement.oc.redstoneIO=局外人 -achievement.oc.redstoneIO.desc=Taking redstone signals where you want them. -achievement.oc.robot=嗶布普 -achievement.oc.robot.desc=EXTERMINATE! -achievement.oc.screen=Have you tried turning it off and on again? -achievement.oc.screen.desc=No seriously. A redstone pulse can toggle a screen's power, after all. -achievement.oc.server=專用 -achievement.oc.server.desc=雲服務,我們來了。 -achievement.oc.switch=複雜的拓撲 -achievement.oc.switch.desc=避免易碎物品因遺失資料的可能性。 -achievement.oc.tablet=不要嚥下 -achievement.oc.tablet.desc=Also keep away from small children to avoid unexpected overdrawing of your credit card. -achievement.oc.transistor=告訴紅石說:“你好。” -achievement.oc.transistor.desc=Create a Transistor to get started. Then listen to the soundtrack. No need to thank me. -achievement.oc.wirelessNetworkCard=信號 -achievement.oc.wirelessNetworkCard.desc=Time to go where no packet has gone before. - -# Death messages. Note that the number of entries here must match the number -# set in the actual damage source in code. -death.attack.oc.nanomachinesOverload.1=%s 獲得貪婪稱號。 -death.attack.oc.nanomachinesOverload.2=%s 神經衰弱。 -death.attack.oc.nanomachinesOverload.3=奈米機器 %s 失去控制 -death.attack.oc.nanomachinesHungry.1=%s 被奈米機器吃掉 -death.attack.oc.nanomachinesHungry.2=%s 沒有定時餵食奈米機器 -death.attack.oc.nanomachinesHungry.3=%s 已被消化。 - -# NEI Integration -nei.options.inventory.oredict=顯示礦物字典名稱 -nei.options.inventory.oredict.true=真 -nei.options.inventory.oredict.false=假 -nei.usage.oc.Manual=打開手冊 - -# Waila Integration -option.oc.address=位址 -option.oc.componentName=組件名稱 -option.oc.energy=能源 diff --git a/src/main/resources/assets/opencomputers/lang/zh_cn.json b/src/main/resources/assets/opencomputers/lang/zh_cn.json new file mode 100644 index 0000000000..5b1af1af02 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/zh_cn.json @@ -0,0 +1,454 @@ +{ + "tile.oc.adapter": "适配器", + "tile.oc.assembler": "装配器", + "tile.oc.cable": "线缆", + "tile.oc.capacitor": "电容", + "tile.oc.carpetedcapacitor": "踩踏发电电容", + "tile.oc.case1": "基础机箱", + "tile.oc.case2": "高级机箱", + "tile.oc.case3": "超级机箱", + "tile.oc.casecreative": "创造模式机箱", + "tile.oc.chameliumblock": "变色块", + "tile.oc.charger": "充电器", + "tile.oc.disassembler": "拆解器", + "tile.oc.diskdrive": "软盘驱动器", + "tile.oc.endstone": "末地石", + "tile.oc.geolyzer": "地质分析仪", + "tile.oc.hologram1": "基础全息地图投影仪", + "tile.oc.hologram2": "高级全息地图投影仪", + "tile.oc.keyboard": "键盘", + "tile.oc.microcontroller": "单片机", + "tile.oc.motionsensor": "运动传感器", + "tile.oc.netsplitter": "网络分配器", + "tile.oc.powerconverter": "能量转换器", + "tile.oc.powerdistributor": "能量分配器", + "tile.oc.print": "3D 打印", + "tile.oc.printer": "3D 打印机", + "tile.oc.raid": "Raid磁盘阵列", + "tile.oc.redstone": "红石 I/O 端口", + "tile.oc.relay": "中继器", + "tile.oc.robot": "机器人", + "tile.oc.robotafterimage": "机器人", + "tile.oc.screen1": "基础显示屏", + "tile.oc.screen2": "高级显示屏", + "tile.oc.screen3": "超级显示屏", + "tile.oc.rack": "机架", + "tile.oc.transposer": "转运器", + "tile.oc.waypoint": "路径点", + "item.oc.abstractbuscard": "抽象类总线卡", + "item.oc.acid": "酸液", + "item.oc.alu": "算术逻辑单元(ALU)", + "item.oc.analyzer": "分析器", + "item.oc.apu0": "高级加速运算单元(APU)", + "item.oc.apu1": "超级加速运算单元(APU)", + "item.oc.apu2": "创造加速运算单元(APU)", + "item.oc.arrowkeys": "方向键", + "item.oc.buttongroup": "按钮组", + "item.oc.cardbase": "基板", + "item.oc.chamelium": "变色材料", + "item.oc.circuitboard": "电路板", + "item.oc.componentbus0": "基础组件总线", + "item.oc.componentbus1": "高级组件总线", + "item.oc.componentbus2": "超级组件总线", + "item.oc.componentbus3": "创造模式组件总线", + "item.oc.controlunit": "控制单元(CU)", + "item.oc.cpu0": "基础中央处理器(CPU)", + "item.oc.cpu1": "高级中央处理器(CPU)", + "item.oc.cpu2": "超级中央处理器(CPU)", + "item.oc.cuttingwire": "切割线", + "item.oc.datacard0": "基础数据卡", + "item.oc.datacard1": "高级数据卡", + "item.oc.datacard2": "超级数据卡", + "item.oc.debugcard": "调试卡", + "item.oc.debugger": "网络调试器", + "item.oc.diamondchip": "钻石芯片", + "item.oc.disk": "磁碟", + "item.oc.diskdrivemountable": "可挂载软盘驱动器", + "item.oc.drone": "无人机", + "item.oc.dronecase0": "基础无人机外壳", + "item.oc.dronecase1": "高级无人机外壳", + "item.oc.dronecase3": "创造无人机外壳", + "item.oc.eeprom": "EEPROM", + "item.oc.floppydisk": "软盘", + "item.oc.GraphicsCard0": "基础显卡", + "item.oc.GraphicsCard1": "高级显卡", + "item.oc.GraphicsCard2": "超级显卡", + "item.oc.HardDiskDrive0": "基础磁盘驱动器", + "item.oc.HardDiskDrive1": "高级磁盘驱动器", + "item.oc.HardDiskDrive2": "超级磁盘驱动器", + "item.oc.hoverBoots": "悬浮靴", + "item.oc.inkcartridge": "墨盒", + "item.oc.inkcartridgeempty": "空墨盒", + "item.oc.internetcard": "因特网卡", + "item.oc.Interweb": "因特网", + "item.oc.ironnugget": "铁粒", + "item.oc.linkedcard": "连接卡", + "item.oc.manual": "开放式电脑使用手册", + "item.oc.memory0": "内存条-T1", + "item.oc.memory1": "内存条-T1.5", + "item.oc.memory2": "内存条-T2", + "item.oc.memory3": "内存条-T2.5", + "item.oc.memory4": "内存条-T3", + "item.oc.memory5": "内存条-T3.5", + "item.oc.microchip0": "简易微芯片", + "item.oc.microchip1": "高级微芯片", + "item.oc.microchip2": "超级微芯片", + "item.oc.microcontrollercase0": "基础单片机外壳", + "item.oc.microcontrollercase1": "高级单片机外壳", + "item.oc.microcontrollercase3": "创造单片机外壳", + "item.oc.nanomachines": "纳米机器", + "item.oc.networkcard": "网卡", + "item.oc.numpad": "数字键盘", + "item.oc.present": "一点小礼物……", + "item.oc.printedcircuitboard": "印刷电路板(PCB)", + "item.oc.rawcircuitboard": "未加工电路板", + "item.oc.redstonecard0": "基础红石卡", + "item.oc.redstonecard1": "高级红石卡", + "item.oc.server0": "基础服务器", + "item.oc.server1": "高级服务器", + "item.oc.server2": "超级服务器", + "item.oc.server3": "创造模式服务器", + "item.oc.tablet": "平板电脑", + "item.oc.tabletcase0": "基础平板电脑外壳", + "item.oc.tabletcase1": "高级平板电脑外壳", + "item.oc.tabletcase3": "创造模式平板电脑外壳", + "item.oc.terminal": "终端", + "item.oc.terminalserver": "终端服务器", + "item.oc.texturepicker": "纹理选择器", + "item.oc.transistor": "晶体管", + "item.oc.upgradeangel": "天使方块升级", + "item.oc.upgradebattery0": "基础电池升级", + "item.oc.upgradebattery1": "高级电池升级", + "item.oc.upgradebattery2": "超级电池升级", + "item.oc.upgradechunkloader": "区块载入升级", + "item.oc.upgradecontainercard0": "基础卡槽", + "item.oc.upgradecontainercard1": "高级卡槽", + "item.oc.upgradecontainercard2": "超级卡槽", + "item.oc.upgradecontainerupgrade0": "基础升级组件容器", + "item.oc.upgradecontainerupgrade1": "高级升级组件容器", + "item.oc.upgradecontainerupgrade2": "超级升级组件容器", + "item.oc.upgradecrafting": "合成升级", + "item.oc.upgradedatabase0": "基础数据库升级", + "item.oc.upgradedatabase1": "高级数据库升级", + "item.oc.upgradedatabase2": "超级数据库升级", + "item.oc.upgradeexperience": "经验升级", + "item.oc.upgradegenerator": "发电机升级", + "item.oc.upgradehover0": "基础悬浮升级", + "item.oc.upgradehover1": "高级悬浮升级", + "item.oc.upgradeinventory": "物品栏升级", + "item.oc.upgradeinventorycontroller": "物品栏控制器升级", + "item.oc.upgradeleash": "缰绳升级", + "item.oc.upgrademf": "MFU", + "item.oc.upgradenavigation": "导航升级", + "item.oc.upgradepiston": "活塞升级", + "item.oc.upgradesign": "告示牌读写升级", + "item.oc.upgradesolargenerator": "太阳能发电机升级", + "item.oc.upgradetank": "水箱升级", + "item.oc.upgradetankcontroller": "储罐升级", + "item.oc.upgradetractorbeam": "牵引光束升级", + "item.oc.upgradetrading": "交易升级", + "item.oc.wirelessnetworkcard0": "基础无线网卡", + "item.oc.wirelessnetworkcard1": "高级无线网卡", + "item.oc.worldsensorcard": "世界传感器卡", + "item.oc.wrench": "螺丝刀扳手", + "entity.oc.Drone.name": "无人机", + "oc:gui.Analyzer.Address": "§6地址§f:%s", + "oc:gui.Analyzer.AddressCopied": "地址已复制到剪贴板。", + "oc:gui.Analyzer.ChargerSpeed": "§6充电速度§f:%s", + "oc:gui.Analyzer.ComponentName": "§6组件名§f:%s", + "oc:gui.Analyzer.Components": "§6已连接组件的数量§f:%s", + "oc:gui.Analyzer.CopyToClipboard": "点击以复制到剪贴板。", + "oc:gui.Analyzer.LastError": "§6上一个错误§f:%s", + "oc:gui.Analyzer.RobotName": "§6名称§f:%s", + "oc:gui.Analyzer.RobotOwner": "§6主人§f:%s", + "oc:gui.Analyzer.RobotXp": "§6经验§f:%s(等级 %s)", + "oc:gui.Analyzer.StoredEnergy": "§6存储能量§f:%s", + "oc:gui.Analyzer.TotalEnergy": "§6存储能量上限§f:%s", + "oc:gui.Analyzer.Users": "§6用户§f:%s", + "oc:gui.Analyzer.WirelessStrength": "§6信号强度§f:%s", + "oc:gui.Assembler.Collect": "收集输出", + "oc:gui.Assembler.Complexity": "复杂度:%s/%s", + "oc:gui.Assembler.InsertCase": "插入一个机箱", + "oc:gui.Assembler.InsertCPU": "插入一个CPU", + "oc:gui.Assembler.InsertRAM": "插入一些存储器", + "oc:gui.Assembler.Progress": "进度:%s%%(%s)", + "oc:gui.Assembler.Run": "组装", + "oc:gui.Assembler.Warning.BIOS": "BIOS", + "oc:gui.Assembler.Warning.GraphicsCard": "显卡", + "oc:gui.Assembler.Warning.Inventory": "物品栏升级", + "oc:gui.Assembler.Warning.Keyboard": "键盘", + "oc:gui.Assembler.Warning.OS": "运行环境", + "oc:gui.Assembler.Warning.Screen": "显示屏", + "oc:gui.Assembler.Warnings": "§e警告§7:推荐的组件丢失。", + "oc:gui.Chat.NewVersion": "有新版本可用:%s", + "oc:gui.Chat.TextureName": "§7材质名为§a%s§f。", + "oc:gui.Chat.WarningClassTransformer": "执行类转换器时发生§c错误§f。请务必反馈此问题,反馈时一并附上 FML 的日志文件 §alatest.log/fml-server-latest.log§f,谢谢!", + "oc:gui.Chat.WarningFingerprint": "§c警告§f——指纹校验不匹配!应为 '§a%s§f',实为 '§e%s§f'。如果你不是在反混淆环境下运行游戏的模组开发者,我们§l强烈§f建议你重新下载开放式电脑模组,因为你现在使用的 JAR 文件已被修改。", + "oc:gui.Chat.WarningLink": "无法打开链接:%s", + "oc:gui.Chat.WarningLuaFallback": "无法使用原生 Lua 库,电脑无法持久化当前状态。它们会在区块载入时重启。", + "oc:gui.Chat.WarningProjectRed": "你目前使用的 Project:Red 版本不兼容 OpenComputers,请更新 Project:Red。", + "oc:gui.Chat.WarningRecipes": "加载合成表时遇到一个或多个错误。部分物品或因此无法合成。查阅日志文件以获取详细信息。", + "oc:gui.Chat.WarningSimpleComponent": "某个扩展 Mod 使用了 §aSimpleComponent§f 接口但§e做错了一些事情§f,导致组件逻辑无法注入。查阅日志文件以获取详细信息。", + "oc:gui.Drive.Managed": "受管理", + "oc:gui.Drive.Unmanaged": "不受管理", + "oc:gui.Drive.ReadOnlyLock": "只读锁定", + "oc:gui.Drive.ReadOnlyLockWarning": "§l只读§r锁定.除非抹掉该磁盘,否则无法关闭.", + "oc:gui.Drive.Warning": "§l警告§r:切换模式将会导致当前磁盘上所有数据丢失!", + "oc:gui.Error.ComponentOverflow": "过多的组件连接了计算机。", + "oc:gui.Error.InternalError": "内部错误,请检查日志文件。这可能是个Bug。", + "oc:gui.Error.NoCPU": "这台计算机上没有安装CPU。", + "oc:gui.Error.NoEnergy": "能量不足。", + "oc:gui.Error.NoRAM": "这台计算机上没有安装内存。", + "oc:gui.Error.OutOfMemory": "内存溢出。", + "oc:gui.Manual.Blocks": "开放式电脑-方块", + "oc:gui.Manual.Home": "主页", + "oc:gui.Manual.Items": "开放式电脑-物品", + "oc:gui.Manual.Warning.BlockMissing": "该方块不可用。", + "oc:gui.Manual.Warning.ImageMissing": "未找到图片。", + "oc:gui.Manual.Warning.ItemMissing": "该物品不可用。", + "oc:gui.Manual.Warning.OreDictMissing": "该矿物词典条目不可用。", + "oc:gui.Raid.Warning": "§4添加磁盘会抹掉该磁盘。\n移除磁盘会使Raid无法使用。", + "oc:gui.Robot.Power": "能量", + "oc:gui.Robot.TurnOff": "关闭", + "oc:gui.Robot.TurnOn": "开启\n§7使用分析器来追踪错误。§r", + "oc:gui.ServerRack.Back": "后", + "oc:gui.ServerRack.Bottom": "底部", + "oc:gui.ServerRack.Left": "左", + "oc:gui.ServerRack.None": "无", + "oc:gui.ServerRack.Right": "右", + "oc:gui.Rack.Enabled": "启用", + "oc:gui.Rack.Disabled": "禁用", + "oc:gui.ServerRack.Top": "上", + "oc:gui.Switch.PacketsPerCycle": "数据包 / 周期", + "oc:gui.Switch.QueueSize": "队列长度", + "oc:gui.Switch.TransferRate": "传输速率", + "oc:gui.Terminal.InvalidKey": "无效键,看上去另一个终端已绑定到这台服务器。", + "oc:gui.Terminal.OutOfRange": "无信号。", + "oc:container.adapter": "适配器", + "oc:container.case": "计算机", + "oc:container.charger": "充电器", + "oc:container.disassembler": "分解器", + "oc:container.diskdrive": "磁盘驱动器", + "oc:container.printer": "打印机", + "oc:container.raid": "Raid磁盘阵列", + "oc:container.relay": "中继器", + "oc:container.server": "服务器", + "oc:container.rack": "机架", + "oc:container.tabletwrapper": "平板电脑", + "key.opencomputers.clipboardPaste": "粘贴到剪贴板", + "oc:tooltip.abstractbuscard": "允许计算机向§f星门科技2§7的抽象类总线发送或接收 LIP 数据包。", + "oc:tooltip.acid": "一种有毒的虚构液体,通常只有某些海盗会使用它们。然而它应该有别的用途。", + "oc:tooltip.adapter": "用于控制非电脑组件的方块,比如原版或者来自其它模组的方块。", + "oc:tooltip.alu": "用来做算术及逻辑运算而不用你亲自代劳,它更能胜任这份差事。", + "oc:tooltip.analyzer": "用于显示方块信息,比如它们的§f地址§7和§f组件名称§7。如果计算机未能正常关闭它也会显示使计算机崩溃的报错。", + "oc:tooltip.apu": "如果你只是需要一个额外的卡槽,这就是你需要的自带 GPU(或者集成图形处理器 IGP)的 CPU。\n支持的组件:§f%s§7\n最大分辨率:§f%sx%s§7\n最高色深:§f%s§7\n运算/tick:§f%s§7", + "oc:tooltip.assembler": "允许使用若干不同的电脑组件来组装机器人。", + "oc:tooltip.cable": "连接方块的廉价选择。", + "oc:tooltip.capacitor": "储能以备不时之需。能够非常快速地充电或放电。", + "oc:tooltip.carpetedcapacitor": "储能以备不时之需。能够非常快速地充电或放电。有羊或豹猫踩上去的时候就能充电。", + "oc:tooltip.cardbase": "正如其名,它是用来安装其它扩展卡的基本卡板。", + "oc:tooltip.case": "计算机机箱是构建计算机的基本方块,也是各种§f扩展卡§7,§f存储器§7以及§f硬盘§7的外壳。\n格子:§f%s§7", + "oc:tooltip.chamelium": "3D打印的基本原材料。不要误吸:或造成失明和暂时失去意识。", + "oc:tooltip.chameliumblock": "整洁干净的玩意。对于彩色 3D 打印相当实用,或者只是拿它的整洁干净的色彩来装饰你的华丽的基地。", + "oc:tooltip.charger": "将电容器的能量传输给相邻机器人。传输效率取决于输入的§f红石信号§7强度,无信号意味着不给机器人充电,而最强信号则意味着全速给机器人充电。", + "oc:tooltip.circuitboard": "现在我们已经取得一些进展。可以通过蚀刻来得到印制电路板。", + "oc:tooltip.controlunit": "用来控制……控制东西……的单元。你需要这玩意儿来做 CPU。所以反正啦,这个非常重要。", + "oc:tooltip.componentbus": "这个扩展能让服务器同时与更多组件通讯,工作原理类似 CPU。\n支持的组件:§f%s§7", + "oc:tooltip.cpu": "所有计算机最核心的组件,它的时钟频率有点不可靠,但是你考虑到它是靠便携日晷来运行的,还能指望什么呢?\n支持组件:§f%s§7", + "oc:tooltip.cpu.Architecture": "架构:§f%s§7", + "oc:tooltip.cuttingwire": "用于将粘土块切割成电路板的形状。使用一次就会坏掉,这可能使得成为历来最低效的工具。", + "oc:tooltip.datacard0": "提供数种高级算法,例如散列、压缩和解压缩算法。", + "oc:tooltip.datacard1": "提供数种高级算法,例如散列、AES 加密、压缩和解压缩算法。", + "oc:tooltip.datacard2": "提供数种高级算法,例如散列、AES 加密、椭圆曲线加密、压缩和解压缩算法。", + "oc:tooltip.debugcard": "创造模式物品,它能让你更简易的操控世界来进行测试。请自己承担它带来的危险。", + "oc:tooltip.debugger": "用于在 OpenComputer 的内部网络格栅中输出调试信息。请在开发者指导下使用。", + "oc:tooltip.diamondchip": "一小粒正在发光的钻石。永远不会破镜重圆。", + "oc:tooltip.disassembler": "将物品分解成基础组件。§l警告§7:分解得到的物品会有 %s%% 的几率在分解过程中损坏!", + "oc:tooltip.disk": "用于制造持久存储设备的原始媒介材料。", + "oc:tooltip.diskdrive.CC": "§a支持§7 ComputerCraft 的软盘。", + "oc:tooltip.diskdrive": "用来读写软盘。可以给机器人安装使其可以插入软盘。", + "oc:tooltip.diskdrivemountable": "和普通软驱用途一样,但必须装载在机架里。", + "oc:tooltip.diskusage": "磁盘使用:%s/%s 字节", + "oc:tooltip.diskmodemanaged": "模式:管理", + "oc:tooltip.diskmodeunmanaged": "模式:不受管理", + "oc:tooltip.drone": "无人机是一种轻量而快速的侦查单位,自身拥有有限的载物空间。", + "oc:tooltip.dronecase": "这个外壳将会在组装机中组装出无人机。这个外壳可容纳少量组件,并内建末地石驱动的悬浮系统。", + "oc:tooltip.eeporm": "小型的可编程存储器,可存储电脑启动所用的BIOS。", + "oc:tooltip.fakeendstone": "几乎可以以假乱真,甚至更加轻盈!", + "oc:tooltip.geolyzer": "可以检测周围方块的硬度。这些信息对编写全息地图程序或检测矿物都有很大的帮助。", + "oc:tooltip.graphicscard": "用来改变屏幕上的显示内容。最高分辨率:§f%sx%s§7\n最高色深:§f%s§7\n运算/tick:§f%s§7", + "oc:tooltip.hoverboots": "跳得更高,摔得更深,走得更快。一切尽在 Hover Boots™,已获顶级认证。", + "oc:tooltip.inkcartridge": "用于重新填装3D打印机的墨水。因为某些原因这些墨水不必保留在打印机里。", + "oc:tooltip.inkcartridgeempty": "此墨盒经过深度干燥处理。用颜料重新装填,抑或弃置。看我脸色行事。", + "oc:tooltip.internetcard": "因特网卡可以让你发送HTTP请求以及使用货真价实的TCP套接字。", + "oc:tooltip.interweb": "恭喜,你赢得了一个因特网。你可以使用因特网卡来连接它。注意:不要水贴钓鱼", + "oc:tooltip.ironnugget": "颗粒状的铁,所以叫铁粒啦,蠢蠢的感觉……", + "oc:tooltip.keyboard": "可以连接屏幕来输入显示文本。", + "oc:tooltip.hologram0": "一个能通过电脑控制来投射出任何三维结构的全息投影仪.\n分辨率:§f48x32x48§7\n最大显示规模:§f3x§7\n最高色深:§f黑白§7", + "oc:tooltip.hologram1": "使用电脑控制的高级全息投影仪。\n分辨率:§f48x32x48§7\n最大显示规模:§f4x§7\n最高色深:§f三原色§7", + "oc:tooltip.linkedcard": "连接卡可以成对合成,并且它们只能与各自对应的连接卡交互。然而,它们之间的交互没有距离限制,甚至可以跨越不同的世界。但它们传递信息所需要的能量消耗不多不少。", + "oc:tooltip.linkedcard_channel": "§8频道:%s§7", + "oc:tooltip.manual": "包含你需要的关于 OpenComputer 的一切信息。还有更多。为此你只需要……§o请按R键继续§7。", + "oc:tooltip.memory": "电脑运行必备。内存越大,你就可以运行越复杂的程序。", + "oc:tooltip.microchip": "通常也管这种芯片叫集成电路。我也不知道为什么能和红石一起运行,但事实如此。", + "oc:tooltip.microcontroller": "单片机是被压缩到不能再压缩的电脑。它们更多是用于执行特殊任务,并只运行一个内建于EEPROM中的简单程序。\n§c不能和外置组件相连接。§7", + "oc:tooltip.microcontrollercase": "单片机基础组件。在组装机中为其添加各种组件并组装单片机。", + "oc:tooltip.motionsensor": "可以检测附近生物的动向。检测需要无遮挡物的环境。", + "oc:tooltip.nanomachines": "控制单元,同时还是一堆可以吸入的纳米机器(如果你愿意的话)。", + "oc:tooltip.networkcard": "允许通过其他方块(比如线缆)相连的远程计算机能够向彼此发送信息进行交互。", + "oc:tooltip.poweracceptor": "能量转换速度:§f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§fMJ§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§fCharge§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§fEU§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§fJoules§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§fRF§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§fCoulombs§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "将其它模组的能量转化为本模组的内部能源形式。转换比:", + "oc:tooltip.powerdistributor": "在不同网络中分配能量。很实用于通过包含多个理论上独立子网络的转换器在系统中共享能量。", + "oc:tooltip.present": "……它随时为你的麻烦待命。打开此礼物将有机会获得 §kphat lewt§7!\n§8在时机合适时,合成 OpenComputers 的物品。§7", + "oc:tooltip.print.BeaconBase": "§8可用作信标底座。", + "oc:tooltip.print.LightValue": "§8光强:%s。", + "oc:tooltip.print.RedstoneLevel": "§8红石信号强度:%s。", + "oc:tooltip.printedcircuitboard": "扩展卡、内存等组件的基础板。", + "oc:tooltip.printer": "利用变色物质和墨盒打印出用户自定义的形状。需要用电脑进行配置。不要让幼童接触。就是这样。", + "oc:tooltip.raid": "允许将三块硬盘组成一个大文件系统供所有与其连接的电脑使用。", + "oc:tooltip.rawcircuitboard": "能在支持原版熔炉配方的炉子中加工强化。", + "oc:tooltip.redstone": "能够在方块周围接收或发送红石信号。可以被与之相连的计算机控制。基本上就像一个外置红石卡。", + "oc:tooltip.redstonecard.Charset": "§a兼容§7 §fSimpleLogic§7。", + "oc:tooltip.redstonecard.ProjectRed": "§a兼容§7 §fProject Red§7。", + "oc:tooltip.redstonecard.RedLogic": "§a兼容§7 §fRedLogic§7。", + "oc:tooltip.redstonecard.RedNet": "§a兼容§7 §fRedNet§7。", + "oc:tooltip.redstonecard.WirelessCBE": "§a兼容§7 §f无线红石(ChickenBones)§7。", + "oc:tooltip.redstonecard.WirelessSV": "§a兼容§7 §f无线红石(SlimeVoid)§7。", + "oc:tooltip.redstonecard": "允许在计算机和机器人四周接收或发送红石信号。", + "oc:tooltip.relay": "可将不同网络连接在一起。仅信息可通过此设备传递,其它元件仍不可见。用法举例:可用于保持多个独立网络的通信。", + "oc:tooltip.robot": "和计算机不同,机器人能够移动并且像玩家那样与世界中的东西交互。然而,它们§o不能§r§7直接与外设进行交互!", + "oc:tooltip.robot_level": "§f等级§7:§a%s§7。", + "oc:tooltip.robot_storedenergy": "§f存储能量§7:§a%s§7。", + "oc:tooltip.screen": "显示文本,运作需要机箱中的显卡。\n最高分辨率:§f%sx%s§7\n最高色深:§f%s§7", + "oc:tooltip.server": "这就是服务器,它与许多其它的服务器拥有相似功能,但这台服务器可以使用组件进行升级,就像升级机箱一样。它也可以放入服务器机架中运行。", + "oc:tooltip.server.Components": "已安装的组件:", + "oc:tooltip.rack": "它能装下四台服务器或者别的设备。", + "oc:tooltip.tablet": "一台平板电脑,为 Lua 新人准备。潜行时右击可强制关机。", + "oc:tooltip.tabletcase": "平板电脑的基础外壳。在组装机中添加组件以制造平板电脑。", + "oc:tooltip.terminal": "可以远程控制服务器,不过前提是你处于信号范围内。使用方法相同于显示屏与键盘. Shift 右击机架中的服务器可以绑定对应的终端。", + "oc:tooltip.terminalserver": "在工作范围内可与终端连接,以提供远程控制功能。内建有虚拟显示器和键盘。", + "oc:tooltip.texturepicker": "这个工具可以显示放块表面的描述,可用于3D打印定义中。完全不是材质名,完全不是。很抱歉先生,不是。", + "oc:tooltip.tier": "§8等级 %s", + "oc:tooltip.netsplitter": "作为动态连接器的存在。每个面的连接设定可通过扳手切换。红石信号可反相所有面的连接设定。", + "oc:tooltip.toolong": "按住 [§f%s§7] 显示详细物品信息。", + "oc:tooltip.transistor": "大多数电脑组件中最基础的元素。看上去有些扭曲,但它是有用的。", + "oc:tooltip.transposer": "可在相邻容器之间自动转移物品和液体。", + "oc:tooltip.upgradeangel": "允许机器人凭空放置方块,甚至是在没有任何依附的情况下。", + "oc:tooltip.upgradebattery": "增加机器人存储能量的上限,让其能够运作很长一段时间也不必充电。\n电容:§f%s§7", + "oc:tooltip.upgradechunkloader": "如果机器人走进了一片森林,周围没有任何生物载入区块,那么它还能继续移动吗? 这个升级组件可以让你确定这点。它能让机器人所处的区块保持载入,但这会让能量不断地消耗。", + "oc:tooltip.upgradecontainercard": "卡容器组件可以让你方便随意的将卡装入机器人或从中移出。\n最高等级:§f%s§7", + "oc:tooltip.upgradecontainerupgrade": "卡容器升级组件可以让你方便的将卡装入另一个卡容器或从中移出。\n最高等级:§f%s§7", + "oc:tooltip.upgradecrafting": "能让机器人的物品栏左上角部分成为合成界面。物品必须在合成界面里排列整齐。", + "oc:tooltip.upgradedatabase": "能对物品信息进行分类,供其它组件稍后取用。\n可支持条目数量:§f%s§7", + "oc:tooltip.upgradeexperience": "这个升级能让机器人在执行一些工作时获取经验并累积。它们拥有越多的累积经验,就能够存储越多的能量,越快的挖掘方块并且使用工具时拥有更高的效率。", + "oc:tooltip.upgradegenerator": "可以让机器人通过燃料充能。燃烧物品得到的发电量取决于燃料的等级。\n§f发电效率§7:§a%s%%§7", + "oc:tooltip.upgradehover": "这个升级组件可让机器人飞得更高而无需爬墙。\n最大高度:§f%s§7", + "oc:tooltip.upgradeinventory": "这个升级组件为机器人提供了物品背包。如果没有这个升级,机器人将不能存储物品。", + "oc:tooltip.upgradeinventorycontroller": "这个升级组件可以控制机器人使其与外部容器互动,并能允许机器人将装备上的工具替换成其物品栏中的其它物品。", + "oc:tooltip.upgrademf": "能让适配器访问不与其相邻的方块。", + "oc:tooltip.upgrademf.Linked": "§f已连接§7", + "oc:tooltip.upgrademf.Unlinked": "§f没有连接§7", + "oc:tooltip.upgradeleash": "这个升级组件可让诸如无人机之类的设备绑在 Isaa——不好意思,*清清嗓子* 我刚才说错了。我只是被告知这原本是用来拴动物的绳子。可以拴很多动物,好奇怪啊。", + "oc:tooltip.upgradenavigation": "可以让机器人确认所处位置以及定位。定位地点即为用于合成此升级组件用到的地图的中心点。", + "oc:tooltip.upgradepiston": "这个升级十分有用。它能让机器人像活塞那样移动方块,但 §l不能§7 移动实体。", + "oc:tooltip.upgradesign": "允许机器人读写告示牌。", + "oc:tooltip.upgradesolargenerator": "可以让机器人在太阳光的照射下四处奔走。机器人顶部需要无任何方块阻挡。产能速度是斯特林引擎的 %s%%。", + "oc:tooltip.upgradetank": "为你的机器人装上一个可存储流体的水箱。如果没有升级此功能,你的机器人将无法在内部存储流体。", + "oc:tooltip.upgradetankcontroller": "这个升级能让你的机器人与外界水箱交互,向其传输流体。", + "oc:tooltip.upgradetractorbeam": "十分先进的高科技,别名“物品磁铁”。它能让机器人在任何地方捡起3格范围内的掉落物。", + "oc:tooltip.waypoint": "为安装有导航升级的设备提供参考点。", + "oc:tooltip.wirelessnetworkcard": "允许在发送正常信息的情况下发送无线网络信息。请确保已设置好 §f信号强度§7 否则不会发出信息。越高的信号强度会导致越高的能量消耗。", + "oc:tooltip.worldsensorcard": "可读取关于世界的各种信息,例如重力加速度和是否有可供呼吸的大气。使用风险自负。制造商对由卡片输出结果造成的损失不承担任何法律责任。我们不仅有律师,还有现金。不要试图告倒我们。", + "oc:tooltip.wrench": "螺丝刀和扳手混合体,新手友好,高手毁灭者。", + "achievement.oc.adapter": "Plug In Baby", + "achievement.oc.adapter.desc": "和其它 MOD 的方块(甚至是原版 Minecraft 方块)进行互动!", + "achievement.oc.assembler": "完美", + "achievement.oc.assembler.desc": "是时候征服世界了!", + "achievement.oc.cable": "并不是脏兮兮的电线", + "achievement.oc.cable.desc": "搭配权威认证的反 SCP-229 科技", + "achievement.oc.capacitor": "本设备含电池", + "achievement.oc.capacitor.desc": "不可能阻止它", + "achievement.oc.card": "我们接受卡片", + "achievement.oc.card.desc": "这是为方便您的使用。我们保证这不是别有用心。", + "achievement.oc.case": "以备后患", + "achievement.oc.case.desc": "Because cuboid towers are the best.", + "achievement.oc.charger": "那就开始做吧!", + "achievement.oc.charger.desc": "开始充——等等!又忘记红石信号了。", + "achievement.oc.chip": "全是些小东西", + "achievement.oc.chip.desc": "曾经真空管也是这样的。", + "achievement.oc.cpu": "超频", + "achievement.oc.cpu.desc": "是时候好好利用这些计算循环了", + "achievement.oc.disassembler": "销毁!", + "achievement.oc.disassembler.desc": "当你发现你的想法很美好,但现实其实很残酷的时候,就用这个吧。", + "achievement.oc.diskDrive": "Roundabout", + "achievement.oc.diskDrive.desc": "Inferior capacity but such delicious sound.", + "achievement.oc.drone": "啊!飞走了!", + "achievement.oc.drone.desc": "冷静冷静,直接用核弹让他们在轨道上瞬间爆炸", + "achievement.oc.eeprom": "每台电脑", + "achievement.oc.eeprom.desc": "仅限一个,就是这样。这是用来决定最终启动顺序的,明白吗!?", + "achievement.oc.floppy": "The One Ri- Disk", + "achievement.oc.floppy.desc": "不要与flappy混淆。\n注:软盘英文为floppy disk", + "achievement.oc.geolyzer": "深入地心", + "achievement.oc.geolyzer.desc": "It has extraordinary qualities.", + "achievement.oc.graphicsCard": "LastGen", + "achievement.oc.graphicsCard.desc": "The way it's meant to be... uh... rendered. Yeah. That.", + "achievement.oc.hdd": "Hot Dog Dealer(热狗推销员)", + "achievement.oc.hdd.desc": "等等我不是那个意思。让我想想,好像想起来什么意思了……", + "achievement.oc.hologram": "下一个次元", + "achievement.oc.hologram.desc": "因为 2D 很无聊啊。或者本来就这样?", + "achievement.oc.keyboard": "吸尘器 3000 型", + "achievement.oc.keyboard.desc": "强烈建议使用时保持将其翻过来并持续摇晃的习惯,以清理其中异物。", + "achievement.oc.microcontroller": "Little Sisters", + "achievement.oc.microcontroller.desc": "计算机的小妹妹哦", + "achievement.oc.motionSensor": "Got the Moves", + "achievement.oc.motionSensor.desc": "Like Steve Swagger.", + "achievement.oc.networkCard": "现在我们在聊天了!", + "achievement.oc.networkCard.desc": "Keep in touch with those distant relatives via transitive relations.", + "achievement.oc.openOS": "启动", + "achievement.oc.openOS.desc": "One OS to - wait, I used that one already? Dang.", + "achievement.oc.powerDistributor": "分享即关怀", + "achievement.oc.powerDistributor.desc": "当你在平衡供电时需要帮助的时候。", + "achievement.oc.rack": "Dat Rack", + "achievement.oc.rack.desc": "我不知道你想歪到哪里去了,我已经说得很明白了,就是服务器架。", + "achievement.oc.raid": "LFG", + "achievement.oc.raid.desc": "Heroic plzkthx.", + "achievement.oc.ram": "随机存取存储器", + "achievement.oc.ram.desc": "恭喜,你的操作完全正确。", + "achievement.oc.redstoneCard": "联系", + "achievement.oc.redstoneCard.desc": "模拟信号时间到。", + "achievement.oc.redstoneIO": "The Outsider", + "achievement.oc.redstoneIO.desc": "将红石信号传播到你需要的地方!", + "achievement.oc.robot": "Beep Boop", + "achievement.oc.robot.desc": "灭绝!", + "achievement.oc.screen": "试过反复开关这屏幕没?", + "achievement.oc.screen.desc": "真没有。毕竟红石信号就可以开关这屏幕了。", + "achievement.oc.server": "专用", + "achievement.oc.server.desc": "云服务,现已正式推出。", + "achievement.oc.switch": "复杂拓扑学", + "achievement.oc.switch.desc": "远离易碎货物,因为包裹可能会掉下来。", + "achievement.oc.tablet": "不要吸入", + "achievement.oc.tablet.desc": "同时请远离儿童,以避免不必要的信用卡过度透支。", + "achievement.oc.transistor": "Tell Red I said \"Hi.\"", + "achievement.oc.transistor.desc": "制作一个晶体管然后让它工作。然后听录音带。不用谢我。", + "achievement.oc.wirelessNetworkCard": "信号", + "achievement.oc.wirelessNetworkCard.desc": "是时候让数据包踏遍每一个角落了。", + "death.attack.oc.nanomachinesOverload.1": "%s 过于贪婪。", + "death.attack.oc.nanomachinesOverload.2": "%s 神经断裂。", + "death.attack.oc.nanomachinesOverload.3": "%s 的纳米机器失控暴走了。", + "death.attack.oc.nanomachinesHungry.1": "%s 被纳米机器吃掉了。", + "death.attack.oc.nanomachinesHungry.2": "%s 忘记喂饱纳米机器了。", + "death.attack.oc.nanomachinesHungry.3": "%s 已被分解吸收了。", + "nei.options.inventory.oredict": "显示矿物词典名", + "nei.options.inventory.oredict.true": "是", + "nei.options.inventory.oredict.false": "否", + "nei.usage.oc.Manual": "打开手册", + "option.oc.address": "地址", + "option.oc.componentName": "组件名", + "option.oc.energy": "能量" +} diff --git a/src/main/resources/assets/opencomputers/lang/zh_tw.json b/src/main/resources/assets/opencomputers/lang/zh_tw.json new file mode 100644 index 0000000000..7cc31e1464 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/zh_tw.json @@ -0,0 +1,444 @@ +{ + "tile.oc.accesspoint": "§c橋接器§7", + "tile.oc.adapter": "連接器", + "tile.oc.assembler": "機器組裝台", + "tile.oc.cable": "線纜", + "tile.oc.capacitor": "電容", + "tile.oc.case1": "基礎電腦機殼", + "tile.oc.case2": "高級電腦機殼", + "tile.oc.case3": "超級電腦機殼", + "tile.oc.casecreative": "電腦機殼(創造模式)", + "tile.oc.chameliumblock": "變色凝膠磚", + "tile.oc.charger": "充電器", + "tile.oc.disassembler": "拆解機", + "tile.oc.diskdrive": "磁碟機", + "tile.oc.endstone": "終界石", + "tile.oc.geolyzer": "硬度分析機", + "tile.oc.hologram1": "全息投影機 (1級)", + "tile.oc.hologram2": "全息投影機 (2級)", + "tile.oc.keyboard": "鍵盤", + "tile.oc.microcontroller": "微型控制器", + "tile.oc.motionsensor": "動作感應器", + "tile.oc.netsplitter": "網路路由器", + "tile.oc.powerconverter": "能源轉換器", + "tile.oc.powerdistributor": "能源分配器", + "tile.oc.print": "3D 列印", + "tile.oc.printer": "3D 列印機", + "tile.oc.raid": "磁碟陣列機(NAS)", + "tile.oc.redstone": "紅石I/O", + "tile.oc.relay": "中繼器", + "tile.oc.robot": "機器人", + "tile.oc.robotafterimage": "機器人", + "tile.oc.screen1": "黑白電腦顯示器", + "tile.oc.screen2": "彩色電腦顯示器", + "tile.oc.screen3": "高畫質電腦顯示器", + "tile.oc.rack": "伺服器機架", + "tile.oc.switch": "路由器", + "tile.oc.transposer": "差轉機", + "tile.oc.waypoint": "路點", + "item.oc.abstractbusCard": "抽象的介面卡", + "item.oc.acid": "蝕刻藥水", + "item.oc.alu": "算邏運算單元(ALU)", + "item.oc.analyzer": "分析器", + "item.oc.apu0": "加速處理單元 (APU) (1級)", + "item.oc.apu1": "加速處理單元 (APU) (2級)", + "item.oc.apu2": "加速處理單元 (APU) (創造模式)", + "item.oc.arrowkeys": "方向鍵", + "item.oc.buttongroup": "按鈕組", + "item.oc.cardbase": "主板", + "item.oc.chamelium": "變色凝膠", + "item.oc.circuitboard": "電路板", + "item.oc.componentbus0": "基礎排線", + "item.oc.componentbus1": "高級排線", + "item.oc.componentbus2": "超級排線", + "item.oc.controlunit": "控制單元 (CU)", + "item.oc.cpu0": "中央處理器 (CPU) (1級)", + "item.oc.cpu1": "中央處理器 (CPU) (2級)", + "item.oc.cpu2": "中央處理器 (CPU) (3級)", + "item.oc.cuttingwire": "切割線", + "item.oc.datacard0": "資料卡 (1級)", + "item.oc.datacard1": "資料卡 (2級)", + "item.oc.datacard2": "資料卡 (3級)", + "item.oc.debugcard": "除錯卡", + "item.oc.debugger": "網路除錯卡", + "item.oc.disk": "磁碟", + "item.oc.drone": "無人機", + "item.oc.dronecase0": "無人機外殼 (1級)", + "item.oc.dronecase1": "無人機外殼 (2級)", + "item.oc.dronecase3": "無人機外殼 (創造模式)", + "item.oc.eeprom": "電子抹除式可複寫唯讀記憶體(EEPROM)", + "item.oc.floppydisk": "磁碟片", + "item.oc.graphicscard0": "顯示卡", + "item.oc.graphicscard1": "彩色顯示卡", + "item.oc.graphicscard2": "高畫質顯示卡", + "item.oc.harddiskdrive0": "硬碟機 (1級)", + "item.oc.harddiskdrive1": "硬碟機 (2級)", + "item.oc.harddiskdrive2": "硬碟機 (3級)", + "item.oc.hoverboots": "懸停靴", + "item.oc.inkcartridge": "墨水匣", + "item.oc.inkcartridgeempty": "墨水匣(空)", + "item.oc.internetcard": "上網卡", + "item.oc.interweb": "因特網", + "item.oc.ironnugget": "鐵粒", + "item.oc.linkedcard": "連結卡", + "item.oc.manual": "開放式電腦手冊", + "item.oc.memory0": "記憶卡 (1級)", + "item.oc.memory1": "記憶卡 (1.5級)", + "item.oc.memory2": "記憶卡 (2級)", + "item.oc.memory3": "記憶卡 (2.5級)", + "item.oc.memory4": "記憶卡 (3級)", + "item.oc.memory5": "記憶卡 (3.5級)", + "item.oc.microchip0": "微晶片 (1級)", + "item.oc.microchip1": "微晶片 (2級)", + "item.oc.microchip2": "微晶片 (3級)", + "item.oc.microcontrollercase0": "微控制器外殼 (Tier 1)", + "item.oc.microcontrollercase1": "微控制器外殼 (Tier 2)", + "item.oc.microcontrollercase3": "微控制器外殼 (Creative)", + "item.oc.nanomachines": "納米機器", + "item.oc.networkcard": "網路卡", + "item.oc.numpad": "數字鍵盤", + "item.oc.present": "一個小東西...", + "item.oc.printedcircuitboard": "印刷電路板(PCB)", + "item.oc.rawcircuitboard": "電路板材料", + "item.oc.redstonecard0": "紅石卡 (1級)", + "item.oc.redstonecard1": "紅石卡 (2級)", + "item.oc.server0": "伺服器 (1級)", + "item.oc.server1": "伺服器 (2級)", + "item.oc.server2": "伺服器 (3級)", + "item.oc.server3": "伺服器 (創造模式)", + "item.oc.tablet": "平板電腦", + "item.oc.tabletcase0": "平板電腦保護套 (1級)", + "item.oc.tabletcase1": "平板電腦保護套 (2級)", + "item.oc.tabletcase3": "平板電腦保護套 (創造模式)", + "item.oc.terminal": "遠端終端機", + "item.oc.texturepicker": "紋理選擇器", + "item.oc.transistor": "電晶體", + "item.oc.upgradeangel": "天使升級", + "item.oc.upgradebattery0": "電池升級 (1級)", + "item.oc.upgradebattery1": "電池升級 (2級)", + "item.oc.upgradebattery2": "電池升級 (3級)", + "item.oc.upgradechunkloader": "區塊載入器升級", + "item.oc.upgradecontainercard0": "卡片集裝箱 (1級)", + "item.oc.upgradecontainercard1": "卡片集裝箱 (2級)", + "item.oc.upgradecontainercard2": "卡片集裝箱 (3級)", + "item.oc.upgradecontainerupgrade0": "集裝箱升級 (1級)", + "item.oc.upgradecontainerupgrade1": "集裝箱升級 (2級)", + "item.oc.upgradecontainerupgrade2": "集裝箱升級 (3級)", + "item.oc.upgradecrafting": "合成升級", + "item.oc.upgradedatabase0": "資料庫升級 (1級)", + "item.oc.upgradedatabase1": "資料庫升級 (2級)", + "item.oc.upgradedatabase2": "資料庫升級 (3級)", + "item.oc.upgradeexperience": "經驗升級", + "item.oc.upgradegenerator": "發電機升級", + "item.oc.upgradehover0": "懸停升級 (1級)", + "item.oc.upgradehover1": "懸停升級 (2級)", + "item.oc.upgradeinventory": "物品欄升級", + "item.oc.upgradeinventorycontroller": "物品控制器升級", + "item.oc.upgradeleash": "皮帶升級", + "item.oc.upgradenavigation": "導航升級", + "item.oc.upgradepiston": "活塞升級", + "item.oc.upgradesign": "告示牌 I/O 升級", + "item.oc.upgradesolargenerator": "太陽能發電升級", + "item.oc.upgradetank": "水箱升級", + "item.oc.upgradetankcontroller": "高級水箱升級", + "item.oc.upgradetractorbeam": "牽引光束升級", + "item.oc.wirelessnetworkcard0": "無線網卡", + "item.oc.wirelessnetworkcard1": "無線網卡", + "item.oc.worldsensorcard": "世界傳感器卡", + "item.oc.wrench": "螺絲刀扳手", + "entity.oc.drone": "無人機", + "oc:gui.Analyzer.Address": "§6位址§f: %s", + "oc:gui.Analyzer.AddressCopied": "位址複製到剪貼簿.", + "oc:gui.Analyzer.ChargerSpeed": "§6充電速度§f: %s", + "oc:gui.Analyzer.ComponentName": "§6設備元件名稱§f: %s", + "oc:gui.Analyzer.Components": "§6連接元件的數量§f: %s", + "oc:gui.Analyzer.CopyToClipboard": "點擊複製到剪貼簿。", + "oc:gui.Analyzer.LastError": "§6上一個錯誤§f: %s", + "oc:gui.Analyzer.RobotName": "§6名字§f: %s", + "oc:gui.Analyzer.RobotOwner": "§6持有者§f: %s", + "oc:gui.Analyzer.RobotXp": "§6經驗值§f: %s", + "oc:gui.Analyzer.StoredEnergy": "§6儲存能量§f: %s", + "oc:gui.Analyzer.TotalEnergy": "§6總儲存能量§f: %s", + "oc:gui.Analyzer.Users": "§6使用者§f: %s", + "oc:gui.Analyzer.WirelessStrength": "§6信號強度§f: %s", + "oc:gui.Assembler.Collect": "收集機器人", + "oc:gui.Assembler.Complexity": "複雜性: %s/%s", + "oc:gui.Assembler.InsertCase": "裝入電腦機殼(CASE)", + "oc:gui.Assembler.InsertCPU": "裝入中央處理器(CPU)", + "oc:gui.Assembler.InsertRAM": "裝入一些記憶體(RAM)", + "oc:gui.Assembler.Progress": "進度: %s%% (%s)", + "oc:gui.Assembler.Run": "開始組裝機器人", + "oc:gui.Assembler.Warnings": "§e警告§7: 缺少某些建議的零組件", + "oc:gui.Assembler.Warning.GraphicsCard": "顯示卡", + "oc:gui.Assembler.Warning.Inventory": "物品欄升級組件", + "oc:gui.Assembler.Warning.Keyboard": "鍵盤", + "oc:gui.Assembler.Warning.OS": "開機(作業系統) 中等", + "oc:gui.Assembler.Warning.Screen": "螢幕", + "oc:gui.Assembler.Warnings": "§e警告§7: 缺少建議的零組件", + "oc:gui.Chat.NewVersion": "有新版本可用: %s", + "oc:gui.Chat.TextureName": "§7紋理名稱是 §a%s§f.", + "oc:gui.Chat.WarningClassTransformer": "There were §cerrors§f running the class transformer. Please report this, together with your (full!) FML §alatest.log§f/§afml-server-latest.log§f logfile, thank you!", + "oc:gui.Chat.WarningFingerprint": "§c警告§f - 指紋不符!預期 '§a%s§f' 但得到 '§e%s§f'. Unless you are a modder and are running the deobfuscated version of the mod, it is §lstrongly§f recommended to redownload OpenComputers, because the JAR you are using may have been tampered with.", + "oc:gui.Chat.WarningLink": "無法打開鏈接: %s", + "oc:gui.Chat.WarningLuaFallback": "本機 LUA 函式庫無法使用,電腦將無法維持狀態,他會重新啟動並且載入區塊", + "oc:gui.Chat.WarningProjectRed": "你正在使用的 Project: Red 模組與 OpenComputers 不相容. 請嘗試更新 Project: Red.", + "oc:gui.Chat.WarningRecipes": "There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information.", + "oc:gui.Chat.WarningSimpleComponent": "An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information.", + "oc:gui.Drive.Managed": "受管", + "oc:gui.Drive.Unmanaged": "非受管", + "oc:gui.Drive.Warning": "§l警告§r: 切換模式會導致目前儲存在硬碟上的所有資料遺失!", + "oc:gui.Error.ComponentOverflow": "太多的元件連接到電腦上了", + "oc:gui.Error.InternalError": "內部錯誤,請查看日誌文件。這可能是一個錯誤。", + "oc:gui.Error.NoCPU": "電腦內沒有安裝中央處理器(CPU).", + "oc:gui.Error.NoEnergy": "沒有能源", + "oc:gui.Error.NoRAM": "電腦內沒有安裝記憶卡", + "oc:gui.Error.OutOfMemory": "記憶體不足", + "oc:gui.Manual.Blocks": "開放式電腦方塊", + "oc:gui.Manual.Home": "首頁", + "oc:gui.Manual.Items": "開放式電腦物品", + "oc:gui.Manual.Warning.BlockMissing": "方塊不存在", + "oc:gui.Manual.Warning.ImageMissing": "沒有找到圖片", + "oc:gui.Manual.Warning.ItemMissing": "物品不存在", + "oc:gui.Manual.Warning.OreDictMissing": "礦物字典不存在", + "oc:gui.Raid.Warning": "§4放入的硬碟機會清除硬碟資料.\n移除硬碟機會刪除磁碟陣列機(NAS)資料.", + "oc:gui.Robot.Power": "能量", + "oc:gui.Robot.TurnOff": "關閉", + "oc:gui.Robot.TurnOn": "開啟", + "oc:gui.Rack.None": "無", + "oc:gui.Rack.Back": "背面", + "oc:gui.Rack.Bottom": "底部", + "oc:gui.Rack.Left": "左", + "oc:gui.Rack.Right": "右", + "oc:gui.Rack.Top": "上", + "oc:gui.Switch.TransferRate": "週期率", + "oc:gui.Switch.PacketsPerCycle": "封包 / 週期", + "oc:gui.Switch.QueueSize": "隊列大小", + "oc:gui.Terminal.InvalidKey": "按鍵無效時,最有可能另一端已經綁定到伺服器。", + "oc:gui.Terminal.OutOfRange": "沒訊號.", + "oc:container.accesspoint": "橋接器", + "oc:container.adapter": "連接器", + "oc:container.case": "電腦", + "oc:container.charger": "充電器", + "oc:container.disassembler": "拆解機", + "oc:container.diskdrive": "硬碟機", + "oc:container.printer": "印表機", + "oc:container.raid": "磁碟陣列機(NAS)", + "oc:container.relay": "中繼器", + "oc:container.server": "伺服器", + "oc:container.rack": "伺服器機架", + "oc:container.switch": "路由器", + "oc:container.tabletwrapper": "平板電腦", + "key.opencomputers.clipboardPaste": "貼上剪貼簿", + "key.opencomputers.materialCosts": "顯示材料成本", + "oc:tooltip.accesspoint": "就像一個切換器,但它會接收無線封包並且轉換無線的封包變成有線的封包.", + "oc:tooltip.abstractbuscard": "允許與 §fStargateTech 2§7 抽象卡傳送與接收 LIP 封包.", + "oc:tooltip.acid": "一種有毒的假液相物質,通常只有某些海盜會使用它們.\n它的腐蝕特性令它非常完美地適用于蝕刻電路板的材料.", + "oc:tooltip.adapter": "用于控制非電腦組件的方塊,\n比如原版或者來自其他模組的方塊.", + "oc:tooltip.alu": "用來做算邏運算以免你親自代勞,\n它更勝任這份差事.", + "oc:tooltip.analyzer": "用于顯示方塊信息,比如它們的§f地址§7和§f組件名稱§7.\n如果計算機未能正常關閉它也會顯示使得計算機崩潰的報錯.", + "oc:tooltip.apu": "這是 CPU 組成 GPU (或 IGP)的一個處理器, 如果你需要額外的卡片插槽,他可以讓你減少一片顯示卡.\n支援的零組件: §f%s§7\n最大分辨率: §f%sx%s§7\n最大顏色深度: §f%s§7\n運作/tick: §f%s§7", + "oc:tooltip.assembler": "機器組裝台可以從不同的電腦零組件來組裝一台機器人。", + "oc:tooltip.cable": "連接方塊的廉價選擇.", + "oc:tooltip.capacitor": "儲能以備他用.\n能夠非常快速地充電放電.", + "oc:tooltip.cardbase": "正如其名,它是用來安裝其他擴展卡的基本卡板.", + "oc:tooltip.case": "計算機電腦機殼是構建計算機的基本方塊,\n也是各種§f介面卡§7,§f記憶體§7以及§f硬碟§7的外殼.\n格子: §f%s§7", + "oc:tooltip.chamelium": "3D列印的原料. 不可吞食: 會導致失明與臨時性身體消失", + "oc:tooltip.chameliumblock": "乾淨清潔. 方便用於3D列印, 只是一個純色的乾淨方塊, 彩色方塊可以讓你發揮創意", + "oc:tooltip.charger": "將電容器的能量傳輸給相鄰機器人.\n傳輸效率取決于輸入的§f紅石信號§7強度,無信號意味著不給機器人充電,而最強信號則意味著全速給機器人充電.", + "oc:tooltip.circuitboard": "現在我們已經取得一些進展.\n可以通過蝕刻來得到印制板.", + "oc:tooltip.controlunit": "用來控制...控制東西...的單元.\n你需要這玩意兒來做CPU.所以反正啦,這個非常重要.", + "oc:tooltip.componentbus": "這個擴充套件能讓伺服器同時與更多組件通訊, 工作原理類似CPU.\n支援的組件: §f%s§7.", + "oc:tooltip.cpu": "所有計算機最核心的組件,它的時鐘頻率有點不可靠,\n但是你考慮到它是靠便攜日晷來運行的你還能指望啥呢?", + "oc:tooltip.cpu.architecture": "架構: §f%s§7", + "oc:tooltip.cuttingwire": "用于將粘土塊切割成電路板的形狀.\n使用一次就會壞掉,這可能使得成為歷來最低效的工具.", + "oc:tooltip.datacard0": "提供了一些先進的演算法,如散列處理以及 deflate/inflate.", + "oc:tooltip.datacard1": "提供了一些先進的演算法,如散列處理, AES 加密 以及 deflate/inflate.", + "oc:tooltip.datacard2": "提供了一些先進的演算法,如散列處理, AES 加密, elliptic curve cryptography and deflate/inflate.", + "oc:tooltip.debugcard": "創造模式物品, 它能讓你更簡易的操控世界來進行測試. 請自己承擔它帶來的危險.", + "oc:tooltip.debugger": "可以使用輸出錯誤訊息到 OC's 網路. 只能使用在開發模式.", + "oc:tooltip.disassembler": "拆解還原成它原來的零組件 §l警告§7: 拆解物品過程中 %s%% 有可能破壞掉這些零組件!", + "oc:tooltip.disk": "用於制造持久存儲設備的原始媒介材料.", + "oc:tooltip.diskdrive.cc": "已經§a支持§7使用另一個電腦模組(ComputerCraft)的軟碟.", + "oc:tooltip.diskdrive": "用來讀寫軟碟.", + "oc:tooltip.diskusage": "磁碟使用量: %s/%s Byte", + "oc:tooltip.diskmodemanaged": "模式: 受管", + "oc:tooltip.diskmodeunmanaged": "模式: 非受管", + "oc:tooltip.drone": "無人機重量輕,速度快的偵察部隊,擁有有限的載貨空間。", + "oc:tooltip.dronecase": "這個外殼是用來建立無人機的組裝外殼。它有足夠的空間用於少量組件,並終界石供電懸浮。", + "oc:tooltip.eeprom": "非常小的程式化儲存裝置,可裝入電腦 BIOS 用來開機.", + "oc:tooltip.fakeendstone": "幾乎一樣好真實的東西,甚至模仿其floatiness!", + "oc:tooltip.geolyzer": "允許掃描周邊地區的方塊硬度,這個訊息可用於產生區域的全息圖,或用來檢測礦石是非常有用的.", + "oc:tooltip.graphicscard": "用來改變顯示器顯示內容.\n最高分辨率: §f%sx%s§7\n最大色深: §f%s§7", + "oc:tooltip.hoverboots": "跳得更高, 跌幅更深, 走得更快. 這是一個全新專利的 懸停靴 (TM).", + "oc:tooltip.inkcartridge": "用於填充墨水的3D打印機。很神奇的原理,不必保留在印表機中。", + "oc:tooltip.inkcartridgeempty": "這個墨盒已經被吸乾。用染料填充它。或者把它扔掉。看看你是否在乎。", + "oc:tooltip.internetcard": "這個介面卡允許你發出 HTTP要求到真實的 TCP sockets.", + "oc:tooltip.interweb": "恭喜,你贏取了 (1) 因特網,你可以使用網路卡連接它,小心,不要餵食巨魔.", + "oc:tooltip.ironnugget": "顆粒狀的鐵,所以叫鐵粒啦,蠢蠢的感覺...", + "oc:tooltip.keyboard": "可以連接顯示器,能夠輸入顯示文本.", + "oc:tooltip.hologram0": "可以透過電腦進行控制以顯示任意像素體積的結構體.\n解析度: §f48x32x48§7\n最大縮放: §f3x§7\n色深: §f單色§7", + "oc:tooltip.hologram1": "可以透過電腦進行控制以顯示任意像素體積的結構體.\n解析度: §f48x32x48§7\n最大縮放: §f4x§7\n色深: §f三色§7", + "oc:tooltip.linkedcard": "這些都是製作成對的,而且只能與它的合作卡通訊,然而,它可以在任何距離,甚至是誇世界溝通,但是發送訊息的消耗的能量非常高.", + "oc:tooltip.linkedcard_channel": "§8頻道: %s§7", + "oc:tooltip.manual": "所有你需要知道開放式電腦模組的訊息都在裡面.而且他的需求非常便宜... §o請按 R 繼續§7.", + "oc:tooltip.materialcosts": "按著 [§f%s§7] 顯示材料成本.", + "oc:tooltip.materials": "需求材料:", + "oc:tooltip.memory": "電腦運行必備.\n記憶體越大,你就可以運行越復雜的程序.", + "oc:tooltip.microchip": "通常也管這種芯片叫集成電路.\n我也不知道為啥能和紅石一起運行,但事實如此啊.", + "oc:tooltip.microcontroller": "微控制器是一部小型電腦. 它的目的是在處理非常具體的任務, 只能執行儲存在 EEPROM 的單一獨立程式.\n§c無法連結外部零組件.§7", + "oc:tooltip.microcontrollercase": "微控制器的基本組件. 將其放入組裝機裡添加其他的零組件", + "oc:tooltip.motionsensor": "可以檢測到附近活物的移動,需要在視線內不能有阻擋物.", + "oc:tooltip.nanomachines": "如果你敢,插入控制單元與一組奈米機器", + "oc:tooltip.networkcard": "允許通過其他方塊(比如線纜)相連的遠程計算機能夠向彼此發送信息進行交互.", + "oc:tooltip.poweracceptor": "能源轉換速度: §f%s/t§7", + "oc:tooltip.powerconverter.BuildCraft": "§f建築模組minecraft焦耳(BC-MJ)§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Factorization": "§f因式分解 (Charge)§7: §a%s:%s§7", + "oc:tooltip.powerconverter.IndustrialCraft2": "§f工業時代 EU(IC²-EU)§7: §a%s:%s§7", + "oc:tooltip.powerconverter.Mekanism": "§f通用機器焦耳(Joules)§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ThermalExpansion": "§f熱能擴展RF(TE-RF)§7: §a%s:%s§7", + "oc:tooltip.powerconverter.ResonantEngine": "§f諧振引擎庫侖§7: §a%s:%s§7", + "oc:tooltip.powerconverter": "將其他模組的能量轉化為本模組的內部能源形式.轉換率:", + "oc:tooltip.powerdistributor": "在不同網絡中分配能量.\n用來通過包含多個理論上獨立的子網絡的轉換器在系統中共享能量時很實用.", + "oc:tooltip.present": "... for your troubles. Open this present for a chance to receive some §kphat lewt§7!\n§8Craft OpenComputers items when the time is right for a chance to receive a present.§7", + "oc:tooltip.print.BeaconBase": "§8一盞燈的基座", + "oc:tooltip.print.LightValue": "§8發出的光: %s.", + "oc:tooltip.print.RedstoneLevel": "§8紅石輸出: %s.", + "oc:tooltip.printedcircuitboard": "基本的構建方塊用來裝入擴充卡與記憶體模組", + "oc:tooltip.printer": "允許使用玩家自定義的墨水匣 Chamelium 與 Ink Cartridges. 必須使用電腦來進行配置. 遠離小朋友", + "oc:tooltip.raid": "使用三個硬碟機然後連接電腦成為一個更大容量的資料儲存系統", + "oc:tooltip.printedcircuitboard": "擴展卡,記憶體等的基板.", + "oc:tooltip.rawcircuitboard": "能夠在熔爐或者類似爐子中加工強化成其他材料.", + "oc:tooltip.redstone": "能夠在方塊周圍接收以及發送紅石信號.\n可以被與之相連的計算機控制.基本上就像一個外置紅石卡.", + "oc:tooltip.redstonecard.ProjectRed": "已經§a支持§fProjectRed§7.", + "oc:tooltip.redstonecard.RedLogic": "已經§a支持§7§f紅石邏輯(RedLogic)§7中的紅石系統.", + "oc:tooltip.redstonecard.RedNet": "已經§a支援§7MFR中的§f紅石網絡(RedNet)§7.", + "oc:tooltip.redstonecard.WirelessCBE": "已經§a支援§f無線紅石 (ChickenBones)§7.", + "oc:tooltip.redstonecard.WirelessSV": "已經§a支援§f無線紅石 (SlimeVoid)§7.", + "oc:tooltip.redstonecard": "能夠在計算機和機器人四周接收以及發送紅石信號.", + "oc:tooltip.robot": "和計算機不同,機器人能夠移動并且像玩家那些與世界之中的東西交互.\n然而,它們§o不能§r§7直接與外設進行交互!", + "oc:tooltip.robot_level": "§f等級§7: §a%s§7.", + "oc:tooltip.robot_storedenergy": "§f儲能§7: §a%s§7.", + "oc:tooltip.screen": "由電腦機殼內的顯示卡控制來顯示文字.\n最高分辨率: §f%sx%s§7\n最大色深: §f%s§7", + "oc:tooltip.server": "這是一台伺服器, 他非常棒, 但是他可以使用元件去升級他功能就像電腦一樣. 他可以插入伺服器機架上執行.", + "oc:tooltip.server.Components": "安裝的元件:", + "oc:tooltip.rack": "提供安裝最多達四個伺服器。為每個伺服器提供了一個內置的虛擬鍵盤和螢幕元件,相當於一個遠程終端。", + "oc:tooltip.switch": "允許設備相互連接不同的網絡.\n僅能傳遞網絡信息,通過路由器方式設備并不互相可見.\n例如可以通過這種方式來建立獨立網絡但仍允許其使用網卡通訊.", + "oc:tooltip.tablet": "一台平板電腦, 可以在旅行中使用LUA. 使用潛行按鍵來啟動或是關閉他", + "oc:tooltip.tabletcase": "平板電腦的機殼,將它放置在機器組裝台內製作一台平板電腦", + "oc:tooltip.terminal": "允許遠程控制伺服器,只要你在它的範圍內。就像一個隨身型螢幕和鍵盤。按住Shift鍵並滑鼠右鍵單擊某個服務器的服務器機架終端綁定它。", + "oc:tooltip.texturepicker": "可以工具允許顯示方塊該面向的紋理描述, 主要用於3D印表機. 完全沒有紋理名字,沒了!", + "oc:tooltip.tier": "§8等級 %s", + "oc:tooltip.netsplitter": "作為一個動態連接器。每邊的連接可以通過用扳手擊中它進行切換。所有邊的連接可以通過應用紅石信號反轉。", + "oc:tooltip.toolong": "按住潛行鍵([§f%s§7])以查看詳細提示信息.", + "oc:tooltip.transistor": "在多數其他計算機的零件中都很基礎的元件.\n引腳有點彎了,但還能用。", + "oc:tooltip.transposer": "允許相鄰的箱子與液體容器之間的物品液體transferral", + "oc:tooltip.upgradeangel": "讓機器人能夠憑空放置方塊在任何地方,即使沒有參考點可以依附放置。", + "oc:tooltip.upgradebattery": "增加機器人可儲存的能量,讓它可以工作更長的時間不需要補充能量.\n容量: §f%s§7", + "oc:tooltip.upgradechunkloader": "如果機器人在森林中移動,周圍沒有其他玩家,它真的會動嗎? 這個升級組件可以確保它持續地在作用中,它使區塊持續在載入狀態,但會不斷消耗能量,維持它。", + "oc:tooltip.upgradecontainercard": "這個集裝箱升級組件允許動態裝配機器人內的某個插卡.\n最大等級: §f%s§7", + "oc:tooltip.upgradecontainerupgrade": "這個集裝箱升級組件允許動態裝配機器人內的某個升級組件\n最大等級: §f%s§7", + "oc:tooltip.upgradecrafting": "使得機器人能夠將其物品欄的左上區域作為合成網格來制做物品.\n物品欄內物品擺放必須與工作臺中一致.", + "oc:tooltip.upgradedatabase": "這個升級允許儲存用於檢索與其他組件的堆疊訊息.\n支持的條目: §f%s§7", + "oc:tooltip.upgradeexperience": "這個升級組件,能夠讓機器人執行各種操作,累積更多的經驗,然後它會獲得更多的能量,更多的儲存空間,速度更快,它收割更有效率,也能使用工具。", + "oc:tooltip.upgradegenerator": "用來不斷地消耗燃料發電,燃燒物品并基于它們的燃燒值隨著時間推移產生電力.\n§f效率§7: §a%s%%§7。", + "oc:tooltip.upgradehover": "可以升級可以讓機器人飛離地面更高,而且不用爬牆.\n最大高度: §f%s§7", + "oc:tooltip.upgradeinventory": "這個升級組件提供機器人一個物品放置的空間,沒有這個升級組件,機器人將不能再內部儲存任何物品。", + "oc:tooltip.upgradeinventoryController": "這個升級組件可以讓機器人更好的控制如何與外部互動物品,並且允許他交換或是裝備物品欄內的工具。", + "oc:tooltip.upgradeleash": "允許一些設備,如無人機, 來綁定 Isaa- excuse me... *chatter* My apologies. 我只是被告知這其實是用來綁住多種動物在木樁上.", + "oc:tooltip.upgradenavigation": "可以用來確定機器人的位置和方向。該位置是相對於被用來製作這個升級地圖的中心。", + "oc:tooltip.upgradepiston": "這個升級十分有用. 它能讓機器人像活塞那樣移動方塊, 但 §l不能§7 移動實體.", + "oc:tooltip.upgradesign": "允許讀取文字與寫入文字到告示牌", + "oc:tooltip.upgradesolarGenerator": "在移動中可以從陽光獲取能量. 機器人上方必須保持無遮蔽狀態才能. 產生能量 %s%% 速度的能量給引擎。", + "oc:tooltip.upgradetank": "為你的機器人裝上一個可存儲流體的水箱. 如果沒有升級此功能, 你的機器人將無法在內部存儲流體.", + "oc:tooltip.upgradetankcontroller": "這個升級能讓你的機器人與外界水箱交互, 向其傳輸流體.", + "oc:tooltip.upgradetractorbeam": "裝備機器人具有非常先進的技術,綽號\"物品磁鐵\"。它能夠拾取三個方塊內的任何地方。", + "oc:tooltip.waypoint": "提供一個參考點到與導航設備的升級。", + "oc:tooltip.wirelessnetworkcard": "允許在發送正常信息外無線發送網絡信息.\n請確保已設置好§f信號強度§7否則不會發出無線數據包。", + "oc:tooltip.worldsensorcard": "允許讀取世界訊息,比如重力,以及是否在大氣下,傳回結果.", + "oc:tooltip.wrench": "螺絲刀和扳手的混合體,這個工具是簡單易學,但很難掌握。", + "achievement.oc.adapter": "插上寶寶", + "achievement.oc.adapter.desc": "Interact with blocks from other mods and even vanilla Minecraft!", + "achievement.oc.assembler": "美妙", + "achievement.oc.assembler.desc": "Time to take over the world!", + "achievement.oc.cable": "不是一個骯髒的電線", + "achievement.oc.cable.desc": "With patented anti-cable-spaghetti technology.", + "achievement.oc.capacitor": "包括電池", + "achievement.oc.capacitor.desc": "You cannot stop it.", + "achievement.oc.card": "我們接受信用卡", + "achievement.oc.card.desc": "For your convenience. No ulterior motive, promise.", + "achievement.oc.case": "出現故障時", + "achievement.oc.case.desc": "Because cuboid towers are the best.", + "achievement.oc.charger": "好吧,讓我們做到這一點", + "achievement.oc.charger.desc": "Chaaaaaaaaaarg- dang, forgot the redstone signal again.", + "achievement.oc.chip": "所有小事", + "achievement.oc.chip.desc": "Because vacuum tubes are so yesteryear.", + "achievement.oc.cpu": "超頻", + "achievement.oc.cpu.desc": "Time to make good use of those computing cycles.", + "achievement.oc.disassembler": "從頭開始", + "achievement.oc.disassembler.desc": "In case one of your brilliant ideas turns out to be not that brilliant after all.", + "achievement.oc.diskDrive": "環狀交叉路", + "achievement.oc.diskDrive.desc": "Inferior capacity but such delicious sound.", + "achievement.oc.drone": "飛走", + "achievement.oc.drone.desc": "Keep calm and nuke it from orbit.", + "achievement.oc.eeprom": "只能有1", + "achievement.oc.eeprom.desc": "Per computer, that is. For deterministic boot order, you know?", + "achievement.oc.floppy": "在一個RI-磁碟", + "achievement.oc.floppy.desc": "Not to be confused with Flappy.", + "achievement.oc.geolyzer": "腳踏實地", + "achievement.oc.geolyzer.desc": "It has extraordinary qualities.", + "achievement.oc.graphicsCard": "LastGen", + "achievement.oc.graphicsCard.desc": "The way it's meant to be... uh... rendered. Yeah. That.", + "achievement.oc.hdd": "熱狗經銷商", + "achievement.oc.hdd.desc": "No wait, that's not what that meant. Hang on, almost got it...", + "achievement.oc.hologram": "下一個維度", + "achievement.oc.hologram.desc": "Because 2D is boring. Or is it?", + "achievement.oc.keyboard": "DirtCatcher3000", + "achievement.oc.keyboard.desc": "It is highly recommended to resist the urge to flip them around and shake them.", + "achievement.oc.microcontroller": "小妹妹", + "achievement.oc.microcontroller.desc": "The small sibling of computers.", + "achievement.oc.motionSensor": "能動", + "achievement.oc.motionSensor.desc": "Like Steve Swagger.", + "achievement.oc.networkCard": "現在我們談論!", + "achievement.oc.networkCard.desc": "Keep in touch with those distant relatives via transitive relations.", + "achievement.oc.openOS": "開機", + "achievement.oc.openOS.desc": "One OS to - wait, I used that one already? Dang.", + "achievement.oc.powerDistributor": "共享是關懷", + "achievement.oc.powerDistributor.desc": "When you need some help balancing all that power.", + "achievement.oc.rack": "Dat Rack", + "achievement.oc.rack.desc": "I don't know what you're thinking of, I clearly meant the server rack.", + "achievement.oc.raid": "LFG", + "achievement.oc.raid.desc": "Heroic plzkthx.", + "achievement.oc.ram": "隨機存取存儲器", + "achievement.oc.ram.desc": "Congratulations, you're Doin' It Right.", + "achievement.oc.redstoneCard": "聯繫", + "achievement.oc.redstoneCard.desc": "Time to go analog.", + "achievement.oc.redstoneIO": "局外人", + "achievement.oc.redstoneIO.desc": "Taking redstone signals where you want them.", + "achievement.oc.robot": "嗶布普", + "achievement.oc.robot.desc": "EXTERMINATE!", + "achievement.oc.screen": "Have you tried turning it off and on again?", + "achievement.oc.screen.desc": "No seriously. A redstone pulse can toggle a screen's power, after all.", + "achievement.oc.server": "專用", + "achievement.oc.server.desc": "雲服務,我們來了。", + "achievement.oc.switch": "複雜的拓撲", + "achievement.oc.switch.desc": "避免易碎物品因遺失資料的可能性。", + "achievement.oc.tablet": "不要嚥下", + "achievement.oc.tablet.desc": "Also keep away from small children to avoid unexpected overdrawing of your credit card.", + "achievement.oc.transistor": "告訴紅石說:“你好。”", + "achievement.oc.transistor.desc": "Create a Transistor to get started. Then listen to the soundtrack. No need to thank me.", + "achievement.oc.wirelessNetworkCard": "信號", + "achievement.oc.wirelessNetworkCard.desc": "Time to go where no packet has gone before.", + "death.attack.oc.nanomachinesOverload.1": "%s 獲得貪婪稱號。", + "death.attack.oc.nanomachinesOverload.2": "%s 神經衰弱。", + "death.attack.oc.nanomachinesOverload.3": "奈米機器 %s 失去控制", + "death.attack.oc.nanomachinesHungry.1": "%s 被奈米機器吃掉", + "death.attack.oc.nanomachinesHungry.2": "%s 沒有定時餵食奈米機器", + "death.attack.oc.nanomachinesHungry.3": "%s 已被消化。", + "nei.options.inventory.oredict": "顯示礦物字典名稱", + "nei.options.inventory.oredict.true": "真", + "nei.options.inventory.oredict.false": "假", + "nei.usage.oc.Manual": "打開手冊", + "option.oc.address": "位址", + "option.oc.componentName": "組件名稱", + "option.oc.energy": "能源" +} diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index 41ab2281a4..2533be739d 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -7,8 +7,6 @@ import net.minecraft.util.text.event.HoverEvent import scala.util.matching.Regex object Localization { - private val nl = Regex.quote("[nl]") - private def resolveKey(key: String) = if (canLocalize(Settings.namespace + key)) Settings.namespace + key else key def canLocalize(key: String): Boolean = LanguageMap.getInstance.has(key) @@ -21,14 +19,14 @@ object Localization { val k = resolveKey(formatKey) var lm = LanguageMap.getInstance if (!lm.has(k)) return k - String.format(lm.getOrDefault(k), values: _*).split(nl).map(_.trim).mkString("\n") + String.format(lm.getOrDefault(k), values: _*).lines.map(_.trim).mkString("\n") } def localizeImmediately(key: String): String = { val k = resolveKey(key) var lm = LanguageMap.getInstance if (!lm.has(k)) return k - lm.getOrDefault(k).split(nl).map(_.trim).mkString("\n") + lm.getOrDefault(k).lines.map(_.trim).mkString("\n") } object Analyzer { diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 6627ceab09..73fd6e596c 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -63,7 +63,7 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str private var unlocalizedName = super.getDescriptionId() @Deprecated - private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name + private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = "tile." + name @Deprecated override def getDescriptionId = unlocalizedName diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 3799c18cb6..0218f43c7e 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -34,7 +34,7 @@ trait SimpleItem extends Item { private var unlocalizedName = super.getDescriptionId() @Deprecated - private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name + private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = "item." + name override def getDescriptionId = unlocalizedName From 2ab07d4f6f7f13fc3870a22dd866d3961fbdf8da Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 30 Jul 2022 00:34:08 +0200 Subject: [PATCH 020/159] Remove item delegate system Due to potential errors with vanilla mechanics like recipes --- src/main/scala/li/cil/oc/CreativeTab.scala | 8 + src/main/scala/li/cil/oc/OpenComputers.scala | 1 - .../scala/li/cil/oc/client/GuiHandler.scala | 17 +- src/main/scala/li/cil/oc/client/Proxy.scala | 3 - .../renderer/block/ModelInitialization.scala | 146 +++------ .../scala/li/cil/oc/common/EventHandler.scala | 11 - .../scala/li/cil/oc/common/GuiHandler.scala | 27 +- src/main/scala/li/cil/oc/common/Proxy.scala | 27 -- .../li/cil/oc/common/block/SimpleBlock.scala | 2 - .../oc/common/component/TerminalServer.scala | 5 +- .../oc/common/event/RobotCommonHandler.scala | 11 +- .../scala/li/cil/oc/common/init/Items.scala | 302 ++++++++---------- .../scala/li/cil/oc/common/item/ALU.scala | 7 +- .../scala/li/cil/oc/common/item/APU.scala | 16 +- .../scala/li/cil/oc/common/item/Acid.scala | 10 +- .../li/cil/oc/common/item/Analyzer.scala | 6 +- .../li/cil/oc/common/item/ArrowKeys.scala | 7 +- .../li/cil/oc/common/item/ButtonGroup.scala | 7 +- .../scala/li/cil/oc/common/item/CPU.scala | 12 +- .../li/cil/oc/common/item/CardBase.scala | 7 +- .../li/cil/oc/common/item/Chamelium.scala | 10 +- .../li/cil/oc/common/item/CircuitBoard.scala | 7 +- .../li/cil/oc/common/item/ComponentBus.scala | 16 +- .../li/cil/oc/common/item/ControlUnit.scala | 7 +- .../li/cil/oc/common/item/CustomModel.scala | 1 + .../li/cil/oc/common/item/CuttingWire.scala | 7 +- .../li/cil/oc/common/item/DataCard.scala | 10 +- .../li/cil/oc/common/item/DebugCard.scala | 6 +- .../li/cil/oc/common/item/Debugger.scala | 6 +- .../li/cil/oc/common/item/Delegator.scala | 262 --------------- .../li/cil/oc/common/item/DiamondChip.scala | 7 +- .../scala/li/cil/oc/common/item/Disk.scala | 7 +- .../oc/common/item/DiskDriveMountable.scala | 6 +- .../scala/li/cil/oc/common/item/Drone.scala | 14 +- .../li/cil/oc/common/item/DroneCase.scala | 11 +- .../scala/li/cil/oc/common/item/EEPROM.scala | 18 +- .../li/cil/oc/common/item/FloppyDisk.scala | 12 +- .../li/cil/oc/common/item/GraphicsCard.scala | 12 +- .../li/cil/oc/common/item/HardDiskDrive.scala | 25 +- .../li/cil/oc/common/item/HoverBoots.scala | 20 +- .../li/cil/oc/common/item/InkCartridge.scala | 9 +- .../oc/common/item/InkCartridgeEmpty.scala | 7 +- .../li/cil/oc/common/item/InternetCard.scala | 7 +- .../li/cil/oc/common/item/Interweb.scala | 7 +- .../li/cil/oc/common/item/LinkedCard.scala | 13 +- .../scala/li/cil/oc/common/item/Manual.scala | 10 +- .../scala/li/cil/oc/common/item/Memory.scala | 12 +- .../li/cil/oc/common/item/Microchip.scala | 14 +- .../oc/common/item/MicrocontrollerCase.scala | 11 +- .../li/cil/oc/common/item/Nanomachines.scala | 19 +- .../li/cil/oc/common/item/NetworkCard.scala | 7 +- .../scala/li/cil/oc/common/item/NumPad.scala | 7 +- .../scala/li/cil/oc/common/item/Present.scala | 12 +- .../oc/common/item/PrintedCircuitBoard.scala | 7 +- .../cil/oc/common/item/RawCircuitBoard.scala | 7 +- .../li/cil/oc/common/item/RedstoneCard.scala | 19 +- .../scala/li/cil/oc/common/item/Server.scala | 14 +- .../scala/li/cil/oc/common/item/Tablet.scala | 35 +- .../li/cil/oc/common/item/TabletCase.scala | 11 +- .../li/cil/oc/common/item/Terminal.scala | 10 +- .../cil/oc/common/item/TerminalServer.scala | 6 +- .../li/cil/oc/common/item/TexturePicker.scala | 6 +- .../li/cil/oc/common/item/Transistor.scala | 7 +- .../li/cil/oc/common/item/UpgradeAngel.scala | 7 +- .../cil/oc/common/item/UpgradeBattery.scala | 13 +- .../oc/common/item/UpgradeChunkloader.scala | 7 +- .../oc/common/item/UpgradeContainerCard.scala | 12 +- .../common/item/UpgradeContainerUpgrade.scala | 12 +- .../cil/oc/common/item/UpgradeCrafting.scala | 7 +- .../cil/oc/common/item/UpgradeDatabase.scala | 14 +- .../oc/common/item/UpgradeExperience.scala | 23 +- .../cil/oc/common/item/UpgradeGenerator.scala | 6 +- .../li/cil/oc/common/item/UpgradeHover.scala | 11 +- .../cil/oc/common/item/UpgradeInventory.scala | 7 +- .../item/UpgradeInventoryController.scala | 7 +- .../li/cil/oc/common/item/UpgradeLeash.scala | 7 +- .../li/cil/oc/common/item/UpgradeMF.scala | 19 +- .../oc/common/item/UpgradeNavigation.scala | 7 +- .../li/cil/oc/common/item/UpgradePiston.scala | 7 +- .../li/cil/oc/common/item/UpgradeSign.scala | 7 +- .../common/item/UpgradeSolarGenerator.scala | 6 +- .../oc/common/item/UpgradeStickyPiston.scala | 9 +- .../li/cil/oc/common/item/UpgradeTank.scala | 12 +- .../common/item/UpgradeTankController.scala | 7 +- .../oc/common/item/UpgradeTractorBeam.scala | 7 +- .../cil/oc/common/item/UpgradeTrading.scala | 9 +- .../oc/common/item/WirelessNetworkCard.scala | 11 +- .../scala/li/cil/oc/common/item/Wrench.scala | 6 +- .../oc/common/item/data/DebugCardData.scala | 2 +- .../cil/oc/common/item/traits/CPULike.scala | 2 +- .../cil/oc/common/item/traits/Delegate.scala | 121 ------- .../common/item/traits/FileSystemLike.scala | 9 +- .../cil/oc/common/item/traits/GPULike.scala | 2 +- .../cil/oc/common/item/traits/ItemTier.scala | 7 +- .../oc/common/item/traits/SimpleItem.scala | 90 +++++- .../li/cil/oc/common/tileentity/Relay.scala | 5 +- .../integration/opencomputers/DriverAPU.scala | 9 +- .../integration/opencomputers/DriverCPU.scala | 5 +- .../opencomputers/DriverComponentBus.scala | 9 +- .../opencomputers/DriverContainerCard.scala | 5 +- .../DriverContainerUpgrade.scala | 5 +- .../opencomputers/DriverDataCard.scala | 5 +- .../opencomputers/DriverFileSystem.scala | 17 +- .../opencomputers/DriverGraphicsCard.scala | 5 +- .../opencomputers/DriverMemory.scala | 9 +- .../opencomputers/DriverRedstoneCard.scala | 5 +- .../opencomputers/DriverUpgradeBattery.scala | 5 +- .../opencomputers/DriverUpgradeDatabase.scala | 5 +- .../opencomputers/DriverUpgradeHover.scala | 5 +- .../DriverWirelessNetworkCard.scala | 5 +- .../opencomputers/ModOpenComputers.scala | 8 +- .../oc/integration/util/ItemBlacklist.scala | 4 +- .../li/cil/oc/server/PacketHandler.scala | 10 +- .../li/cil/oc/server/component/Server.scala | 5 +- .../li/cil/oc/server/fs/FileSystem.scala | 5 +- 115 files changed, 932 insertions(+), 1025 deletions(-) delete mode 100644 src/main/scala/li/cil/oc/common/item/Delegator.scala delete mode 100644 src/main/scala/li/cil/oc/common/item/traits/Delegate.scala diff --git a/src/main/scala/li/cil/oc/CreativeTab.scala b/src/main/scala/li/cil/oc/CreativeTab.scala index 3137a73def..2fe4f1cca8 100644 --- a/src/main/scala/li/cil/oc/CreativeTab.scala +++ b/src/main/scala/li/cil/oc/CreativeTab.scala @@ -1,9 +1,17 @@ package li.cil.oc +import li.cil.oc.common.init.Items import net.minecraft.item.ItemGroup +import net.minecraft.item.ItemStack +import net.minecraft.util.NonNullList object CreativeTab extends ItemGroup(-1, OpenComputers.Name) { private lazy val stack = api.Items.get(Constants.BlockName.CaseTier1).createItemStack(1) override def makeIcon = stack + + override def fillItemList(list: NonNullList[ItemStack]) { + super.fillItemList(list) + Items.decorateCreativeTab(list) + } } diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 2a46722bc4..7996b95fdb 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -92,7 +92,6 @@ class OpenComputers { @SubscribeEvent def registerItems(e: RegistryEvent.Register[Item]) { Items.init() - OpenComputers.proxy.initExtraTags() } @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 5f03fc2eeb..adb775e997 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -9,7 +9,6 @@ import li.cil.oc.common.component import li.cil.oc.common.entity import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventory, ServerInventory} import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity.traits.TileEntity import li.cil.oc.common.{GuiHandler => CommonGuiHandler} @@ -76,22 +75,22 @@ object GuiHandler extends CommonGuiHandler { } case Some(GuiType.Category.Item) => { val itemStackInUse = getItemStackInUse(id, player) - Delegator.subItem(itemStackInUse) match { - case Some(drive: item.traits.FileSystemLike) if id == GuiType.Drive.id => + itemStackInUse.getItem match { + case drive: item.traits.FileSystemLike if id == GuiType.Drive.id => new gui.Drive(player.inventory, () => itemStackInUse) - case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => + case database: item.UpgradeDatabase if id == GuiType.Database.id => new gui.Database(containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) - case Some(server: item.Server) if id == GuiType.Server.id => + case server: item.Server if id == GuiType.Server.id => new gui.Server(containerId, player.inventory, new ServerInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) - case Some(tablet: item.Tablet) if id == GuiType.Tablet.id => + case tablet: item.Tablet if id == GuiType.Tablet.id => val stack = itemStackInUse if (stack.hasTag) { item.Tablet.get(stack, player).components.collect { @@ -102,18 +101,18 @@ object GuiHandler extends CommonGuiHandler { } } else null - case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => + case tablet: item.Tablet if id == GuiType.TabletInner.id => val stack = itemStackInUse if (stack.hasTag) { new gui.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) } else null - case Some(_: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => + case _: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => new gui.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { override def container = itemStackInUse override def stillValid(activePlayer : PlayerEntity): Boolean = activePlayer == player }) - case Some(terminal: item.Terminal) if id == GuiType.Terminal.id => + case terminal: item.Terminal if id == GuiType.Terminal.id => val stack = itemStackInUse if (stack.hasTag) { val address = stack.getTag.getString(Settings.namespace + "server") diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 8c1bba6958..bb30b92f57 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -22,7 +22,6 @@ import li.cil.oc.common.entity.Drone import li.cil.oc.common.entity.EntityTypes import li.cil.oc.common.event.NanomachinesHandler import li.cil.oc.common.event.RackMountableRenderHandler -import li.cil.oc.common.item.traits.Delegate import li.cil.oc.common.tileentity import li.cil.oc.util.Audio import net.minecraft.block.Block @@ -114,8 +113,6 @@ private[oc] class Proxy extends CommonProxy { } } - override def registerModel(instance: Delegate, id: String): Unit = ModelInitialization.registerModel(instance, id) - override def registerModel(instance: Item, id: String): Unit = ModelInitialization.registerModel(instance, id) override def registerModel(instance: Block, id: String): Unit = ModelInitialization.registerModel(instance, id) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 5ee780d3eb..f442d929b5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -6,8 +6,6 @@ import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.common.item.CustomModel -import li.cil.oc.common.item.Delegator -import li.cil.oc.common.item.traits.Delegate import net.minecraft.block.BlockState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.BlockModelShapes @@ -18,11 +16,9 @@ import net.minecraft.client.world.ClientWorld import net.minecraft.entity.LivingEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack -import net.minecraft.util.Direction import net.minecraft.util.IItemProvider -import net.minecraft.util.ResourceLocation +import net.minecraft.util.Direction import net.minecraftforge.client.event.{ModelBakeEvent, ModelRegistryEvent} -import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.MinecraftForge import net.minecraftforge.eventbus.api.SubscribeEvent @@ -43,9 +39,6 @@ object ModelInitialization { final val RackBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Rack, "normal") private val meshableItems = mutable.ArrayBuffer.empty[Item] - private val itemDelegates = mutable.ArrayBuffer.empty[(String, Delegate)] - private val itemDelegatesCustom = mutable.ArrayBuffer.empty[Delegate with CustomModel] - private val delegatorOverrides = mutable.Map.empty[Delegator, ItemStack => ModelResourceLocation] private val modelRemappings = mutable.Map.empty[ModelResourceLocation, ModelResourceLocation] def preInit(): Unit = { @@ -61,19 +54,10 @@ object ModelInitialization { @SubscribeEvent def onModelRegistration(event: ModelRegistryEvent): Unit = { registerItems() - registerSubItems() - registerSubItemsCustom() } // ----------------------------------------------------------------------- // - def registerModel(instance: Delegate, id: String): Unit = { - instance match { - case customModel: CustomModel => itemDelegatesCustom += customModel - case _ => itemDelegates += id -> instance - } - } - def registerModel(instance: IItemProvider, id: String): Unit = { meshableItems += instance.asItem } @@ -95,52 +79,21 @@ object ModelInitialization { private def registerItems(): Unit = { val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper for (item <- meshableItems) { - Option(api.Items.get(new ItemStack(item))) match { - case Some(descriptor) => - val location = Settings.resourceDomain + ":" + descriptor.name() - shaper.register(item, new ModelResourceLocation(location, "inventory")) - case _ => + item match { + case custom: CustomModel => custom.registerModelLocations() + case _ => { + Option(api.Items.get(new ItemStack(item))) match { + case Some(descriptor) => + val location = Settings.resourceDomain + ":" + descriptor.name() + shaper.register(item, new ModelResourceLocation(location, "inventory")) + case _ => + } + } } } meshableItems.clear() } - private def registerSubItems(): Unit = { - for ((id, item) <- itemDelegates) { - val location = Settings.resourceDomain + ":" + id - delegatorOverrides.get(item.parent) match { - case Some(func) => delegatorOverrides.put(item.parent, stack => { - if (stack.getDamageValue != item.itemId) func(stack) - else new ModelResourceLocation(location, "inventory") - }) - case _ => delegatorOverrides.put(item.parent, stack => { - if (stack.getDamageValue != item.itemId) null - else new ModelResourceLocation(location, "inventory") - }) - } - } - } - - private def registerSubItemsCustom(): Unit = { - for (item <- itemDelegatesCustom) { - delegatorOverrides.get(item.parent) match { - case Some(func) => delegatorOverrides.put(item.parent, stack => { - Delegator.subItem(stack) match { - case Some(subItem: CustomModel) => subItem.getModelLocation(stack) - case _ => func(stack) - } - }) - case _ => delegatorOverrides.put(item.parent, stack => { - Delegator.subItem(stack) match { - case Some(subItem: CustomModel) => subItem.getModelLocation(stack) - case _ => null - } - }) - } - item.registerModelLocations() - } - } - // ----------------------------------------------------------------------- // @SubscribeEvent @@ -158,12 +111,42 @@ object ModelInitialization { registry.put(RobotAfterimageBlockLocation, NullModel) registry.put(RobotAfterimageItemLocation, NullModel) - for ((id, item) <- itemDelegates) { - val location = Settings.resourceDomain + ":" + id - ModelLoader.addSpecialModel(new ResourceLocation(location)) - } - for (item <- itemDelegatesCustom) { - item.bakeModels(e) + for (item <- meshableItems) item match { + case custom: CustomModel => { + custom.bakeModels(e) + val originalLocation = new ModelResourceLocation(custom.getRegistryName, "inventory") + registry.get(originalLocation) match { + case original: IBakedModel => { + val overrides = new ItemOverrideList { + override def resolve(base: IBakedModel, stack: ItemStack, world: ClientWorld, holder: LivingEntity) = + Option(custom.getModelLocation(stack)).map(registry).getOrElse(original) + } + val fake = new IBakedModel { + @Deprecated + override def getQuads(state: BlockState, dir: Direction, rand: Random) = original.getQuads(state, dir, rand) + + override def useAmbientOcclusion() = original.useAmbientOcclusion + + override def isGui3d() = original.isGui3d + + override def usesBlockLight() = original.usesBlockLight + + override def isCustomRenderer() = original.isCustomRenderer + + @Deprecated + override def getParticleIcon() = original.getParticleIcon + + @Deprecated + override def getTransforms() = original.getTransforms + + override def getOverrides() = overrides + } + registry.put(originalLocation, fake) + } + case _ => + } + } + case _ => } val modelOverrides = Map[String, IBakedModel => IBakedModel]( @@ -190,40 +173,5 @@ object ModelInitialization { } case _ => } - - // Temporary hack to apply model overrides from registerModel calls. - for ((delegator, handler) <- delegatorOverrides) { - val originalLocation = new ModelResourceLocation(delegator.getRegistryName, "inventory") - registry.get(originalLocation) match { - case original: IBakedModel => { - val overrides = new ItemOverrideList { - override def resolve(base: IBakedModel, stack: ItemStack, world: ClientWorld, holder: LivingEntity) = - Option(handler(stack)).map(registry).getOrElse(original.getOverrides.resolve(base, stack, world, holder)) - } - val fake = new IBakedModel { - @Deprecated - override def getQuads(state: BlockState, dir: Direction, rand: Random) = original.getQuads(state, dir, rand) - - override def useAmbientOcclusion() = original.useAmbientOcclusion - - override def isGui3d() = original.isGui3d - - override def usesBlockLight() = original.usesBlockLight - - override def isCustomRenderer() = original.isCustomRenderer - - @Deprecated - override def getParticleIcon() = original.getParticleIcon - - @Deprecated - override def getTransforms() = original.getTransforms - - override def getOverrides() = overrides - } - registry.put(originalLocation, fake) - } - case _ => - } - } } } diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 9e06e60595..6527092f81 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -150,17 +150,6 @@ object EventHandler { override def run = provider.invalidate }) } - case _: li.cil.oc.api.driver.item.Chargeable => - li.cil.oc.common.item.Delegator.subItem(stack) match { - case Some(subItem: traits.Chargeable) => { - val provider = new traits.Chargeable.Provider(stack, subItem) - event.addCapability(traits.Chargeable.KEY, provider) - event.addListener(new Runnable { - override def run = provider.invalidate - }) - } - case _ => - } case _ => } case _ => diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index bcc793fc45..38e673dd52 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -1,7 +1,6 @@ package li.cil.oc.common import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventory, ServerInventory} -import li.cil.oc.common.item.Delegator import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.server.component.{DiskDriveMountable, Server} @@ -56,26 +55,26 @@ abstract class GuiHandler { } case Some(GuiType.Category.Item) => { val itemStackInUse = getItemStackInUse(id, player) - Delegator.subItem(itemStackInUse) match { - case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => + itemStackInUse.getItem match { + case database: item.UpgradeDatabase if id == GuiType.Database.id => new container.Database(containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) - case Some(server: item.Server) if id == GuiType.Server.id => + case server: item.Server if id == GuiType.Server.id => new container.Server(containerId, player.inventory, new ServerInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) - case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => + case tablet: item.Tablet if id == GuiType.TabletInner.id => val stack = itemStackInUse if (stack.hasTag) new container.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) else null - case Some(drive: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => + case drive: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => new container.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { override def container: ItemStack = itemStackInUse @@ -92,14 +91,14 @@ abstract class GuiHandler { def getItemStackInUse(id: Int, player: PlayerEntity): ItemStack = { val mainItem: ItemStack = player.getItemInHand(Hand.MAIN_HAND) - Delegator.subItem(mainItem) match { - case Some(drive: item.traits.FileSystemLike) if id == GuiType.Drive.id => mainItem - case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => mainItem - case Some(server: item.Server) if id == GuiType.Server.id => mainItem - case Some(tablet: item.Tablet) if id == GuiType.Tablet.id => mainItem - case Some(tablet: item.Tablet) if id == GuiType.TabletInner.id => mainItem - case Some(terminal: item.Terminal) if id == GuiType.Terminal.id => mainItem - case Some(drive: item.DiskDriveMountable) if id == GuiType.DiskDriveMountable.id => mainItem + mainItem.getItem match { + case drive: item.traits.FileSystemLike if id == GuiType.Drive.id => mainItem + case database: item.UpgradeDatabase if id == GuiType.Database.id => mainItem + case server: item.Server if id == GuiType.Server.id => mainItem + case tablet: item.Tablet if id == GuiType.Tablet.id => mainItem + case tablet: item.Tablet if id == GuiType.TabletInner.id => mainItem + case terminal: item.Terminal if id == GuiType.Terminal.id => mainItem + case drive: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => mainItem case _ => player.getItemInHand(Hand.OFF_HAND) } } diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index c56ab41b42..74e06464c4 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -11,8 +11,6 @@ import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.entity.Drone import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items -import li.cil.oc.common.item.Delegator -import li.cil.oc.common.item.traits.Delegate import li.cil.oc.integration.Mods import li.cil.oc.server import li.cil.oc.server._ @@ -48,13 +46,6 @@ import scala.reflect.ClassTag @Deprecated class Proxy { - @Deprecated - def initExtraTags() { - OpenComputers.log.debug("Initializing additional OreDict entries.") - - tryRegisterNugget[item.DiamondChip](Constants.ItemName.DiamondChip, OpenComputers.ID + ":chip_diamond", net.minecraft.item.Items.DIAMOND, "forge:gems/diamond") - } - def preInit() { OpenComputers.log.info("Initializing OpenComputers API.") @@ -132,22 +123,6 @@ class Proxy { driver.Registry.locked = true } - def tryRegisterNugget[TItem <: Delegate : ClassTag](nuggetItemName: String, nuggetOredictName: String, ingotItem: Item, ingotOredictName: String): Unit = { - val nugget = Items.get(nuggetItemName).createItemStack(1) - - Delegator.subItem(nugget) match { - case Some(subItem: TItem) => - if (ItemTags.getAllTags.getTagOrEmpty(new ResourceLocation(nuggetOredictName)).contains(nugget.getItem)) { - Items.registerItem(subItem, nuggetItemName) - Items.registerItem(ingotItem, ingotOredictName) - } - else { - subItem.showInItemList = false - } - case _ => - } - } - def getGuiHandler(): common.GuiHandler = server.GuiHandler @Deprecated @@ -166,8 +141,6 @@ class Proxy { } } - def registerModel(instance: Delegate, id: String): Unit = {} - def registerModel(instance: Item, id: String): Unit = {} def registerModel(instance: Block, id: String): Unit = {} diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 73fd6e596c..68e8c6d69f 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -48,8 +48,6 @@ import scala.collection.convert.ImplicitConversionsToScala._ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).strength(2, 5)) extends ContainerBlock(props.isValidSpawn(new IExtendedPositionPredicate[EntityType[_]] { override def test(state: BlockState, world: IBlockReader, pos: BlockPos, entity: EntityType[_]) = state.getBlock.asInstanceOf[SimpleBlock].isValidSpawn(state, world, pos, entity) })) { - var showInItemList = true - @Deprecated private var creativeTab: ItemGroup = CreativeTab diff --git a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala index 555c45a39b..61d1db1923 100644 --- a/src/main/scala/li/cil/oc/common/component/TerminalServer.scala +++ b/src/main/scala/li/cil/oc/common/component/TerminalServer.scala @@ -23,7 +23,6 @@ import li.cil.oc.api.util.StateAware import li.cil.oc.api.util.StateAware.State import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -55,8 +54,8 @@ class TerminalServer(val rack: api.internal.Rack, val slot: Int) extends Environ keyboard.setUsableOverride(new UsabilityChecker { override def isUsableByPlayer(keyboard: api.internal.Keyboard, player: PlayerEntity) = { val stack = player.getItemInHand(Hand.MAIN_HAND) - Delegator.subItem(stack) match { - case Some(t: item.Terminal) if stack.hasTag => sidedKeys.contains(stack.getTag.getString(Settings.namespace + "key")) + stack.getItem match { + case t: item.Terminal if stack.hasTag => sidedKeys.contains(stack.getTag.getString(Settings.namespace + "key")) case _ => false } } diff --git a/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala b/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala index 16ec084d34..603843da0e 100644 --- a/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/RobotCommonHandler.scala @@ -5,7 +5,6 @@ import li.cil.oc.api.event.RobotMoveEvent import li.cil.oc.api.event.RobotUsedToolEvent import li.cil.oc.api.internal import li.cil.oc.api.internal.Robot -import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.UpgradeHover import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ @@ -37,15 +36,13 @@ object RobotCommonHandler { var maxFlyingHeight = Settings.get.limitFlightHeight (0 until robot.equipmentInventory.getContainerSize). - map(robot.equipmentInventory.getItem). - map(Delegator.subItem). - collect { case Some(item: UpgradeHover) => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } + map(robot.equipmentInventory.getItem(_).getItem). + collect { case item: UpgradeHover => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } (0 until robot.componentCount). map(_ + robot.mainInventory.getContainerSize + robot.equipmentInventory.getContainerSize). - map(robot.getItem(_)). - map(Delegator.subItem(_)). - collect { case Some(item: UpgradeHover) => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } + map(robot.getItem(_).getItem). + collect { case item: UpgradeHover => maxFlyingHeight = math.max(maxFlyingHeight, Settings.get.upgradeFlightHeight(item.tier)) } def isMovingDown = e.direction == Direction.DOWN def hasAdjacentBlock(pos: BlockPosition) = Direction.values.exists(side => { diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index c627455c79..17f2208f65 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -13,13 +13,11 @@ import li.cil.oc.common.Loot import li.cil.oc.common.Tier import li.cil.oc.common.block.SimpleBlock import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.data.DroneData import li.cil.oc.common.item.data.HoverBootsData import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.item.data.TabletData -import li.cil.oc.common.item.traits.Delegate import li.cil.oc.common.item.traits.SimpleItem import li.cil.oc.server.machine.luac.LuaStateFactory import net.minecraft.block.Block @@ -85,30 +83,12 @@ object Items extends ItemAPI { instance } - def registerItem[T <: Delegate](delegate: T, id: String): T = { - if (!descriptors.contains(id)) { - OpenComputers.proxy.registerModel(delegate, id) - descriptors += id -> new ItemInfo { - override def name: String = id - - override def block = null - - override def item: Delegator = delegate.parent - - override def createItemStack(size: Int): ItemStack = delegate.createItemStack(size) - } - names += delegate -> id - } - delegate - } - def registerItem(instance: Item, id: String): Item = { if (!descriptors.contains(id)) { instance match { case simple: SimpleItem => - simple.setUnlocalizedName("oc." + id) GameData.register_impl(simple.setRegistryName(new ResourceLocation(Settings.resourceDomain, id))) - OpenComputers.proxy.registerModel(instance, id) + OpenComputers.proxy.registerModel(simple, id) case _ => } descriptors += id -> new ItemInfo { @@ -148,10 +128,10 @@ object Items extends ItemAPI { private def getBlockOrItem(stack: ItemStack): Any = if (stack.isEmpty) null - else Delegator.subItem(stack).getOrElse(stack.getItem match { + else stack.getItem match { case block: BlockItem => block.getBlock case item => item - }) + } // ----------------------------------------------------------------------- // @@ -338,177 +318,165 @@ object Items extends ItemAPI { // Crafting materials. private def initMaterials(): Unit = { - val materials = newItem(new item.Delegator(), "material") - - registerItem(new item.CuttingWire(materials), Constants.ItemName.CuttingWire) - registerItem(new item.Acid(materials), Constants.ItemName.Acid) - registerItem(new item.RawCircuitBoard(materials), Constants.ItemName.RawCircuitBoard) - registerItem(new item.CircuitBoard(materials), Constants.ItemName.CircuitBoard) - registerItem(new item.PrintedCircuitBoard(materials), Constants.ItemName.PrintedCircuitBoard) - registerItem(new item.CardBase(materials), Constants.ItemName.Card) - registerItem(new item.Transistor(materials), Constants.ItemName.Transistor) - registerItem(new item.Microchip(materials, Tier.One), Constants.ItemName.ChipTier1) - registerItem(new item.Microchip(materials, Tier.Two), Constants.ItemName.ChipTier2) - registerItem(new item.Microchip(materials, Tier.Three), Constants.ItemName.ChipTier3) - registerItem(new item.ALU(materials), Constants.ItemName.Alu) - registerItem(new item.ControlUnit(materials), Constants.ItemName.ControlUnit) - registerItem(new item.Disk(materials), Constants.ItemName.Disk) - registerItem(new item.Interweb(materials), Constants.ItemName.Interweb) - registerItem(new item.ButtonGroup(materials), Constants.ItemName.ButtonGroup) - registerItem(new item.ArrowKeys(materials), Constants.ItemName.ArrowKeys) - registerItem(new item.NumPad(materials), Constants.ItemName.NumPad) - - registerItem(new item.TabletCase(materials, Tier.One), Constants.ItemName.TabletCaseTier1) - registerItem(new item.TabletCase(materials, Tier.Two), Constants.ItemName.TabletCaseTier2) - registerItem(new item.TabletCase(materials, Tier.Four), Constants.ItemName.TabletCaseCreative) - registerItem(new item.MicrocontrollerCase(materials, Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) - registerItem(new item.MicrocontrollerCase(materials, Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) - registerItem(new item.MicrocontrollerCase(materials, Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) - registerItem(new item.DroneCase(materials, Tier.One), Constants.ItemName.DroneCaseTier1) - registerItem(new item.DroneCase(materials, Tier.Two), Constants.ItemName.DroneCaseTier2) - registerItem(new item.DroneCase(materials, Tier.Four), Constants.ItemName.DroneCaseCreative) - - registerItem(new item.InkCartridgeEmpty(materials), Constants.ItemName.InkCartridgeEmpty) - registerItem(new item.InkCartridge(materials), Constants.ItemName.InkCartridge) - registerItem(new item.Chamelium(materials), Constants.ItemName.Chamelium) - - registerItem(new item.DiamondChip(materials), Constants.ItemName.DiamondChip) + registerItem(new item.CuttingWire(), Constants.ItemName.CuttingWire) + registerItem(new item.Acid(), Constants.ItemName.Acid) + registerItem(new item.RawCircuitBoard(), Constants.ItemName.RawCircuitBoard) + registerItem(new item.CircuitBoard(), Constants.ItemName.CircuitBoard) + registerItem(new item.PrintedCircuitBoard(), Constants.ItemName.PrintedCircuitBoard) + registerItem(new item.CardBase(), Constants.ItemName.Card) + registerItem(new item.Transistor(), Constants.ItemName.Transistor) + registerItem(new item.Microchip(Tier.One), Constants.ItemName.ChipTier1) + registerItem(new item.Microchip(Tier.Two), Constants.ItemName.ChipTier2) + registerItem(new item.Microchip(Tier.Three), Constants.ItemName.ChipTier3) + registerItem(new item.ALU(), Constants.ItemName.Alu) + registerItem(new item.ControlUnit(), Constants.ItemName.ControlUnit) + registerItem(new item.Disk(), Constants.ItemName.Disk) + registerItem(new item.Interweb(), Constants.ItemName.Interweb) + registerItem(new item.ButtonGroup(), Constants.ItemName.ButtonGroup) + registerItem(new item.ArrowKeys(), Constants.ItemName.ArrowKeys) + registerItem(new item.NumPad(), Constants.ItemName.NumPad) + + registerItem(new item.TabletCase(Tier.One), Constants.ItemName.TabletCaseTier1) + registerItem(new item.TabletCase(Tier.Two), Constants.ItemName.TabletCaseTier2) + registerItem(new item.TabletCase(Tier.Four), Constants.ItemName.TabletCaseCreative) + registerItem(new item.MicrocontrollerCase(Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) + registerItem(new item.MicrocontrollerCase(Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) + registerItem(new item.MicrocontrollerCase(Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) + registerItem(new item.DroneCase(Tier.One), Constants.ItemName.DroneCaseTier1) + registerItem(new item.DroneCase(Tier.Two), Constants.ItemName.DroneCaseTier2) + registerItem(new item.DroneCase(Tier.Four), Constants.ItemName.DroneCaseCreative) + + registerItem(new item.InkCartridgeEmpty(), Constants.ItemName.InkCartridgeEmpty) + registerItem(new item.InkCartridge(), Constants.ItemName.InkCartridge) + registerItem(new item.Chamelium(), Constants.ItemName.Chamelium) + + registerItem(new item.DiamondChip(), Constants.ItemName.DiamondChip) } // All kinds of tools. private def initTools(): Unit = { - val tools = newItem(new item.Delegator(), "tool") - - registerItem(new item.Analyzer(tools), Constants.ItemName.Analyzer) - registerItem(new item.Debugger(tools), Constants.ItemName.Debugger) - registerItem(new item.Terminal(tools), Constants.ItemName.Terminal) - registerItem(new item.TexturePicker(tools), Constants.ItemName.TexturePicker) - registerItem(new item.Manual(tools), Constants.ItemName.Manual) + registerItem(new item.Analyzer(), Constants.ItemName.Analyzer) + registerItem(new item.Debugger(), Constants.ItemName.Debugger) + registerItem(new item.Terminal(), Constants.ItemName.Terminal) + registerItem(new item.TexturePicker(), Constants.ItemName.TexturePicker) + registerItem(new item.Manual(), Constants.ItemName.Manual) registerItem(new item.Wrench(), Constants.ItemName.Wrench) // 1.5.11 registerItem(new item.HoverBoots(), Constants.ItemName.HoverBoots) // 1.5.18 - registerItem(new item.Nanomachines(tools), Constants.ItemName.Nanomachines) + registerItem(new item.Nanomachines(), Constants.ItemName.Nanomachines) } // General purpose components. private def initComponents(): Unit = { - val components = newItem(new item.Delegator(), "component") - - registerItem(new item.CPU(components, Tier.One), Constants.ItemName.CPUTier1) - registerItem(new item.CPU(components, Tier.Two), Constants.ItemName.CPUTier2) - registerItem(new item.CPU(components, Tier.Three), Constants.ItemName.CPUTier3) - - registerItem(new item.ComponentBus(components, Tier.One), Constants.ItemName.ComponentBusTier1) - registerItem(new item.ComponentBus(components, Tier.Two), Constants.ItemName.ComponentBusTier2) - registerItem(new item.ComponentBus(components, Tier.Three), Constants.ItemName.ComponentBusTier3) - - registerItem(new item.Memory(components, Tier.One), Constants.ItemName.RAMTier1) - registerItem(new item.Memory(components, Tier.Two), Constants.ItemName.RAMTier2) - registerItem(new item.Memory(components, Tier.Three), Constants.ItemName.RAMTier3) - registerItem(new item.Memory(components, Tier.Four), Constants.ItemName.RAMTier4) - registerItem(new item.Memory(components, Tier.Five), Constants.ItemName.RAMTier5) - registerItem(new item.Memory(components, Tier.Six), Constants.ItemName.RAMTier6) - - registerItem(new item.Server(components, Tier.Four), Constants.ItemName.ServerCreative) - registerItem(new item.Server(components, Tier.One), Constants.ItemName.ServerTier1) - registerItem(new item.Server(components, Tier.Two), Constants.ItemName.ServerTier2) - registerItem(new item.Server(components, Tier.Three), Constants.ItemName.ServerTier3) + registerItem(new item.CPU(Tier.One), Constants.ItemName.CPUTier1) + registerItem(new item.CPU(Tier.Two), Constants.ItemName.CPUTier2) + registerItem(new item.CPU(Tier.Three), Constants.ItemName.CPUTier3) + + registerItem(new item.ComponentBus(Tier.One), Constants.ItemName.ComponentBusTier1) + registerItem(new item.ComponentBus(Tier.Two), Constants.ItemName.ComponentBusTier2) + registerItem(new item.ComponentBus(Tier.Three), Constants.ItemName.ComponentBusTier3) + + registerItem(new item.Memory(Tier.One), Constants.ItemName.RAMTier1) + registerItem(new item.Memory(Tier.Two), Constants.ItemName.RAMTier2) + registerItem(new item.Memory(Tier.Three), Constants.ItemName.RAMTier3) + registerItem(new item.Memory(Tier.Four), Constants.ItemName.RAMTier4) + registerItem(new item.Memory(Tier.Five), Constants.ItemName.RAMTier5) + registerItem(new item.Memory(Tier.Six), Constants.ItemName.RAMTier6) + + registerItem(new item.Server(Tier.Four), Constants.ItemName.ServerCreative) + registerItem(new item.Server(Tier.One), Constants.ItemName.ServerTier1) + registerItem(new item.Server(Tier.Two), Constants.ItemName.ServerTier2) + registerItem(new item.Server(Tier.Three), Constants.ItemName.ServerTier3) // 1.5.10 - registerItem(new item.APU(components, Tier.One), Constants.ItemName.APUTier1) - registerItem(new item.APU(components, Tier.Two), Constants.ItemName.APUTier2) + registerItem(new item.APU(Tier.One), Constants.ItemName.APUTier1) + registerItem(new item.APU(Tier.Two), Constants.ItemName.APUTier2) // 1.5.12 - registerItem(new item.APU(components, Tier.Three), Constants.ItemName.APUCreative) + registerItem(new item.APU(Tier.Three), Constants.ItemName.APUCreative) // 1.6 - registerItem(new item.TerminalServer(components), Constants.ItemName.TerminalServer) - registerItem(new item.DiskDriveMountable(components), Constants.ItemName.DiskDriveMountable) + registerItem(new item.TerminalServer(), Constants.ItemName.TerminalServer) + registerItem(new item.DiskDriveMountable(), Constants.ItemName.DiskDriveMountable) } // Card components. private def initCards(): Unit = { - val cards = newItem(new item.Delegator(), "card") - - registerItem(new item.DebugCard(cards), Constants.ItemName.DebugCard) - registerItem(new item.GraphicsCard(cards, Tier.One), Constants.ItemName.GraphicsCardTier1) - registerItem(new item.GraphicsCard(cards, Tier.Two), Constants.ItemName.GraphicsCardTier2) - registerItem(new item.GraphicsCard(cards, Tier.Three), Constants.ItemName.GraphicsCardTier3) - registerItem(new item.RedstoneCard(cards, Tier.One), Constants.ItemName.RedstoneCardTier1) - registerItem(new item.RedstoneCard(cards, Tier.Two), Constants.ItemName.RedstoneCardTier2) - registerItem(new item.NetworkCard(cards), Constants.ItemName.NetworkCard) - registerItem(new item.WirelessNetworkCard(cards, Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) - registerItem(new item.InternetCard(cards), Constants.ItemName.InternetCard) - registerItem(new item.LinkedCard(cards), Constants.ItemName.LinkedCard) + registerItem(new item.DebugCard(), Constants.ItemName.DebugCard) + registerItem(new item.GraphicsCard(Tier.One), Constants.ItemName.GraphicsCardTier1) + registerItem(new item.GraphicsCard(Tier.Two), Constants.ItemName.GraphicsCardTier2) + registerItem(new item.GraphicsCard(Tier.Three), Constants.ItemName.GraphicsCardTier3) + registerItem(new item.RedstoneCard(Tier.One), Constants.ItemName.RedstoneCardTier1) + registerItem(new item.RedstoneCard(Tier.Two), Constants.ItemName.RedstoneCardTier2) + registerItem(new item.NetworkCard(), Constants.ItemName.NetworkCard) + registerItem(new item.WirelessNetworkCard(Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) + registerItem(new item.InternetCard(), Constants.ItemName.InternetCard) + registerItem(new item.LinkedCard(), Constants.ItemName.LinkedCard) // 1.5.13 - registerItem(new item.DataCard(cards, Tier.One), Constants.ItemName.DataCardTier1) + registerItem(new item.DataCard(Tier.One), Constants.ItemName.DataCardTier1) // 1.5.15 - registerItem(new item.DataCard(cards, Tier.Two), Constants.ItemName.DataCardTier2) - registerItem(new item.DataCard(cards, Tier.Three), Constants.ItemName.DataCardTier3) + registerItem(new item.DataCard(Tier.Two), Constants.ItemName.DataCardTier2) + registerItem(new item.DataCard(Tier.Three), Constants.ItemName.DataCardTier3) } // Upgrade components. private def initUpgrades(): Unit = { - val upgrades = newItem(new item.Delegator(), "upgrade") - - registerItem(new item.UpgradeAngel(upgrades), Constants.ItemName.AngelUpgrade) - registerItem(new item.UpgradeBattery(upgrades, Tier.One), Constants.ItemName.BatteryUpgradeTier1) - registerItem(new item.UpgradeBattery(upgrades, Tier.Two), Constants.ItemName.BatteryUpgradeTier2) - registerItem(new item.UpgradeBattery(upgrades, Tier.Three), Constants.ItemName.BatteryUpgradeTier3) - registerItem(new item.UpgradeChunkloader(upgrades), Constants.ItemName.ChunkloaderUpgrade) - registerItem(new item.UpgradeContainerCard(upgrades, Tier.One), Constants.ItemName.CardContainerTier1) - registerItem(new item.UpgradeContainerCard(upgrades, Tier.Two), Constants.ItemName.CardContainerTier2) - registerItem(new item.UpgradeContainerCard(upgrades, Tier.Three), Constants.ItemName.CardContainerTier3) - registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.One), Constants.ItemName.UpgradeContainerTier1) - registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Two), Constants.ItemName.UpgradeContainerTier2) - registerItem(new item.UpgradeContainerUpgrade(upgrades, Tier.Three), Constants.ItemName.UpgradeContainerTier3) - registerItem(new item.UpgradeCrafting(upgrades), Constants.ItemName.CraftingUpgrade) - registerItem(new item.UpgradeDatabase(upgrades, Tier.One), Constants.ItemName.DatabaseUpgradeTier1) - registerItem(new item.UpgradeDatabase(upgrades, Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) - registerItem(new item.UpgradeDatabase(upgrades, Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) - registerItem(new item.UpgradeExperience(upgrades), Constants.ItemName.ExperienceUpgrade) - registerItem(new item.UpgradeGenerator(upgrades), Constants.ItemName.GeneratorUpgrade) - registerItem(new item.UpgradeInventory(upgrades), Constants.ItemName.InventoryUpgrade) - registerItem(new item.UpgradeInventoryController(upgrades), Constants.ItemName.InventoryControllerUpgrade) - registerItem(new item.UpgradeNavigation(upgrades), Constants.ItemName.NavigationUpgrade) - registerItem(new item.UpgradePiston(upgrades), Constants.ItemName.PistonUpgrade) - registerItem(new item.UpgradeSign(upgrades), Constants.ItemName.SignUpgrade) - registerItem(new item.UpgradeSolarGenerator(upgrades), Constants.ItemName.SolarGeneratorUpgrade) - registerItem(new item.UpgradeTank(upgrades), Constants.ItemName.TankUpgrade) - registerItem(new item.UpgradeTankController(upgrades), Constants.ItemName.TankControllerUpgrade) - registerItem(new item.UpgradeTractorBeam(upgrades), Constants.ItemName.TractorBeamUpgrade) - registerItem(new item.UpgradeLeash(upgrades), Constants.ItemName.LeashUpgrade) + registerItem(new item.UpgradeAngel(), Constants.ItemName.AngelUpgrade) + registerItem(new item.UpgradeBattery(Tier.One), Constants.ItemName.BatteryUpgradeTier1) + registerItem(new item.UpgradeBattery(Tier.Two), Constants.ItemName.BatteryUpgradeTier2) + registerItem(new item.UpgradeBattery(Tier.Three), Constants.ItemName.BatteryUpgradeTier3) + registerItem(new item.UpgradeChunkloader(), Constants.ItemName.ChunkloaderUpgrade) + registerItem(new item.UpgradeContainerCard(Tier.One), Constants.ItemName.CardContainerTier1) + registerItem(new item.UpgradeContainerCard(Tier.Two), Constants.ItemName.CardContainerTier2) + registerItem(new item.UpgradeContainerCard(Tier.Three), Constants.ItemName.CardContainerTier3) + registerItem(new item.UpgradeContainerUpgrade(Tier.One), Constants.ItemName.UpgradeContainerTier1) + registerItem(new item.UpgradeContainerUpgrade(Tier.Two), Constants.ItemName.UpgradeContainerTier2) + registerItem(new item.UpgradeContainerUpgrade(Tier.Three), Constants.ItemName.UpgradeContainerTier3) + registerItem(new item.UpgradeCrafting(), Constants.ItemName.CraftingUpgrade) + registerItem(new item.UpgradeDatabase(Tier.One), Constants.ItemName.DatabaseUpgradeTier1) + registerItem(new item.UpgradeDatabase(Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) + registerItem(new item.UpgradeDatabase(Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) + registerItem(new item.UpgradeExperience(), Constants.ItemName.ExperienceUpgrade) + registerItem(new item.UpgradeGenerator(), Constants.ItemName.GeneratorUpgrade) + registerItem(new item.UpgradeInventory(), Constants.ItemName.InventoryUpgrade) + registerItem(new item.UpgradeInventoryController(), Constants.ItemName.InventoryControllerUpgrade) + registerItem(new item.UpgradeNavigation(), Constants.ItemName.NavigationUpgrade) + registerItem(new item.UpgradePiston(), Constants.ItemName.PistonUpgrade) + registerItem(new item.UpgradeSign(), Constants.ItemName.SignUpgrade) + registerItem(new item.UpgradeSolarGenerator(), Constants.ItemName.SolarGeneratorUpgrade) + registerItem(new item.UpgradeTank(), Constants.ItemName.TankUpgrade) + registerItem(new item.UpgradeTankController(), Constants.ItemName.TankControllerUpgrade) + registerItem(new item.UpgradeTractorBeam(), Constants.ItemName.TractorBeamUpgrade) + registerItem(new item.UpgradeLeash(), Constants.ItemName.LeashUpgrade) // 1.5.8 - registerItem(new item.UpgradeHover(upgrades, Tier.One), Constants.ItemName.HoverUpgradeTier1) - registerItem(new item.UpgradeHover(upgrades, Tier.Two), Constants.ItemName.HoverUpgradeTier2) + registerItem(new item.UpgradeHover(Tier.One), Constants.ItemName.HoverUpgradeTier1) + registerItem(new item.UpgradeHover(Tier.Two), Constants.ItemName.HoverUpgradeTier2) // 1.6 - registerItem(new item.UpgradeTrading(upgrades), Constants.ItemName.TradingUpgrade) - registerItem(new item.UpgradeMF(upgrades), Constants.ItemName.MFU) + registerItem(new item.UpgradeTrading(), Constants.ItemName.TradingUpgrade) + registerItem(new item.UpgradeMF(), Constants.ItemName.MFU) // 1.7.2 - registerItem(new item.WirelessNetworkCard(upgrades, Tier.One), Constants.ItemName.WirelessNetworkCardTier1) - registerItem(new item.ComponentBus(upgrades, Tier.Four), Constants.ItemName.ComponentBusCreative) + registerItem(new item.WirelessNetworkCard(Tier.One), Constants.ItemName.WirelessNetworkCardTier1) + registerItem(new item.ComponentBus(Tier.Four), Constants.ItemName.ComponentBusCreative) // 1.8 - registerItem(new item.UpgradeStickyPiston(upgrades), Constants.ItemName.StickyPistonUpgrade) + registerItem(new item.UpgradeStickyPiston(), Constants.ItemName.StickyPistonUpgrade) } // Storage media of all kinds. private def initStorage(): Unit = { - val storage = newItem(new item.Delegator(), "storage") - - registerItem(new item.EEPROM(storage), Constants.ItemName.EEPROM) - registerItem(new item.FloppyDisk(storage), Constants.ItemName.Floppy) - registerItem(new item.HardDiskDrive(storage, Tier.One), Constants.ItemName.HDDTier1) - registerItem(new item.HardDiskDrive(storage, Tier.Two), Constants.ItemName.HDDTier2) - registerItem(new item.HardDiskDrive(storage, Tier.Three), Constants.ItemName.HDDTier3) + registerItem(new item.EEPROM(), Constants.ItemName.EEPROM) + registerItem(new item.FloppyDisk(), Constants.ItemName.Floppy) + registerItem(new item.HardDiskDrive(Tier.One), Constants.ItemName.HDDTier1) + registerItem(new item.HardDiskDrive(Tier.Two), Constants.ItemName.HDDTier2) + registerItem(new item.HardDiskDrive(Tier.Three), Constants.ItemName.HDDTier3) val luaBios = { val code = new Array[Byte](4 * 1024) @@ -521,31 +489,17 @@ object Items extends ItemAPI { // Special purpose items that don't fit into any other category. private def initSpecial(): Unit = { - val misc = newItem(new item.Delegator() { - private def configuredItems = Array( - Items.createConfiguredDrone(), - Items.createConfiguredMicrocontroller(), - Items.createConfiguredRobot(), - Items.createConfiguredTablet(), - Items.createChargedHoverBoots() - ) ++ Loot.disksForClient ++ registeredItems - - override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]): Unit = { - super.fillItemCategory(tab, list) - if(this.allowdedIn(tab)){ - configuredItems.foreach(list.add) - } - } - }, "misc") - - registerItem(new item.Tablet(misc), Constants.ItemName.Tablet) - registerItem(new item.Drone(misc), Constants.ItemName.Drone) - registerItem(new item.Present(misc), Constants.ItemName.Present) + registerItem(new item.Tablet(), Constants.ItemName.Tablet) + registerItem(new item.Drone(), Constants.ItemName.Drone) + registerItem(new item.Present(), Constants.ItemName.Present) } - private def newItem[T <: item.Delegator](delegator: T, name: String): T = { - delegator.setUnlocalizedName("oc." + name) - GameData.register_impl(delegator.setRegistryName(new ResourceLocation(Settings.resourceDomain, name))) - delegator + def decorateCreativeTab(list: NonNullList[ItemStack]) { + list.add(Items.createConfiguredDrone()) + list.add(Items.createConfiguredMicrocontroller()) + list.add(Items.createConfiguredRobot()) + list.add(Items.createConfiguredTablet()) + Loot.disksForClient.foreach(list.add) + registeredItems.foreach(list.add) } } diff --git a/src/main/scala/li/cil/oc/common/item/ALU.scala b/src/main/scala/li/cil/oc/common/item/ALU.scala index 48daf66d1f..ea5cc930bc 100644 --- a/src/main/scala/li/cil/oc/common/item/ALU.scala +++ b/src/main/scala/li/cil/oc/common/item/ALU.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class ALU(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class ALU(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/APU.scala b/src/main/scala/li/cil/oc/common/item/APU.scala index 303c18b69a..f43798ea4d 100644 --- a/src/main/scala/li/cil/oc/common/item/APU.scala +++ b/src/main/scala/li/cil/oc/common/item/APU.scala @@ -1,24 +1,30 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.common.Tier import li.cil.oc.util.Rarity import net.minecraft.item // Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem import scala.language.existentials -class APU(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.CPULike with traits.GPULike { - override val unlocalizedName = super[Delegate].unlocalizedName + tier +class APU(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike with traits.GPULike { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override def rarity(stack: ItemStack): item.Rarity = + @Deprecated + override def getRarity(stack: ItemStack): item.Rarity = if (tier == Tier.Three) Rarity.byTier(Tier.Four) - else super.rarity(stack) + else super.getRarity(stack) override def cpuTier = math.min(Tier.Three, tier + 1) override def gpuTier = tier - override protected def tooltipName = Option(super[Delegate].unlocalizedName) + override protected def tooltipName = Option(super.getDescriptionId) override protected def tooltipData: Seq[Any] = { super[CPULike].tooltipData ++ super[GPULike].tooltipData diff --git a/src/main/scala/li/cil/oc/common/item/Acid.scala b/src/main/scala/li/cil/oc/common/item/Acid.scala index 19d49be75d..2a1550040e 100644 --- a/src/main/scala/li/cil/oc/common/item/Acid.scala +++ b/src/main/scala/li/cil/oc/common/item/Acid.scala @@ -2,11 +2,14 @@ package li.cil.oc.common.item import javax.annotation.Nonnull +import li.cil.oc.CreativeTab import li.cil.oc.api import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.UseAction +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.item.UseAction import net.minecraft.potion.Effect import net.minecraft.potion.Effects import net.minecraft.potion.EffectInstance @@ -14,8 +17,9 @@ import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Hand import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class Acid(val parent: Delegator) extends traits.Delegate { +class Acid(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) @@ -23,7 +27,7 @@ class Acid(val parent: Delegator) extends traits.Delegate { override def getUseAnimation(stack: ItemStack): UseAction = UseAction.DRINK - override def getMaxItemUseDuration(stack: ItemStack): Int = 32 + override def getUseDuration(stack: ItemStack): Int = 32 override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = { entity match { diff --git a/src/main/scala/li/cil/oc/common/item/Analyzer.scala b/src/main/scala/li/cil/oc/common/item/Analyzer.scala index f8da846226..c2cf3837c2 100644 --- a/src/main/scala/li/cil/oc/common/item/Analyzer.scala +++ b/src/main/scala/li/cil/oc/common/item/Analyzer.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.item import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api @@ -13,11 +14,14 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.Direction import net.minecraft.util.Util import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.entity.player.PlayerInteractEvent import net.minecraftforge.eventbus.api.SubscribeEvent @@ -101,7 +105,7 @@ object Analyzer { } } -class Analyzer(val parent: Delegator) extends traits.Delegate { +class Analyzer(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (player.isCrouching && stack.hasTag) { stack.removeTagKey(Settings.namespace + "clipboard") diff --git a/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala b/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala index fef4d0850f..89d151c78f 100644 --- a/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala +++ b/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala @@ -1,5 +1,10 @@ package li.cil.oc.common.item -class ArrowKeys(val parent: Delegator) extends traits.Delegate { +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class ArrowKeys(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipName = None } diff --git a/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala b/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala index c6450d41d3..bad6bc9fe6 100644 --- a/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala +++ b/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala @@ -1,5 +1,10 @@ package li.cil.oc.common.item -class ButtonGroup(val parent: Delegator) extends traits.Delegate { +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class ButtonGroup(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipName = None } diff --git a/src/main/scala/li/cil/oc/common/item/CPU.scala b/src/main/scala/li/cil/oc/common/item/CPU.scala index bc8aacce44..8a2d5643af 100644 --- a/src/main/scala/li/cil/oc/common/item/CPU.scala +++ b/src/main/scala/li/cil/oc/common/item/CPU.scala @@ -1,11 +1,17 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + import scala.language.existentials -class CPU(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.CPULike { - override val unlocalizedName = super.unlocalizedName + tier +class CPU(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier override def cpuTier = tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/CardBase.scala b/src/main/scala/li/cil/oc/common/item/CardBase.scala index dfb3362ae3..6647eae72c 100644 --- a/src/main/scala/li/cil/oc/common/item/CardBase.scala +++ b/src/main/scala/li/cil/oc/common/item/CardBase.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class CardBase(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class CardBase(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/Chamelium.scala b/src/main/scala/li/cil/oc/common/item/Chamelium.scala index 70bce72f40..2e2768bbc7 100644 --- a/src/main/scala/li/cil/oc/common/item/Chamelium.scala +++ b/src/main/scala/li/cil/oc/common/item/Chamelium.scala @@ -1,10 +1,13 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.UseAction +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.item.UseAction import net.minecraft.potion.Effect import net.minecraft.potion.Effects import net.minecraft.potion.EffectInstance @@ -12,8 +15,9 @@ import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Hand import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class Chamelium(val parent: Delegator) extends traits.Delegate { +class Chamelium(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (Settings.get.chameliumEdible) { player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) @@ -23,7 +27,7 @@ class Chamelium(val parent: Delegator) extends traits.Delegate { override def getUseAnimation(stack: ItemStack): UseAction = UseAction.EAT - override def getMaxItemUseDuration(stack: ItemStack): Int = 32 + override def getUseDuration(stack: ItemStack): Int = 32 override def finishUsingItem(stack: ItemStack, world: World, player: LivingEntity): ItemStack = { if (!world.isClientSide) { diff --git a/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala index b24c2a0321..3a69acccd7 100644 --- a/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class CircuitBoard(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class CircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala index 458a30df61..53424ce4a5 100644 --- a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala +++ b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala @@ -1,21 +1,27 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.util.Rarity import net.minecraft.item // Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class ComponentBus(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class ComponentBus(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier // Because the driver considers the creative bus to be tier 3, the superclass // will believe it has T3 rarity. We override that here. - override def rarity(stack: ItemStack): item.Rarity = + @Deprecated + override def getRarity(stack: ItemStack): item.Rarity = if (tier == Tier.Four) Rarity.byTier(Tier.Four) - else super.rarity(stack) + else super.getRarity(stack) - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(Settings.get.cpuComponentSupport(tier)) } diff --git a/src/main/scala/li/cil/oc/common/item/ControlUnit.scala b/src/main/scala/li/cil/oc/common/item/ControlUnit.scala index b9eab26ab0..d2fcb976ec 100644 --- a/src/main/scala/li/cil/oc/common/item/ControlUnit.scala +++ b/src/main/scala/li/cil/oc/common/item/ControlUnit.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class ControlUnit(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class ControlUnit(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/CustomModel.scala b/src/main/scala/li/cil/oc/common/item/CustomModel.scala index e8a114d05c..193de8bcc1 100644 --- a/src/main/scala/li/cil/oc/common/item/CustomModel.scala +++ b/src/main/scala/li/cil/oc/common/item/CustomModel.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.item.ItemStack import net.minecraftforge.client.event.ModelBakeEvent diff --git a/src/main/scala/li/cil/oc/common/item/CuttingWire.scala b/src/main/scala/li/cil/oc/common/item/CuttingWire.scala index 6814a420d9..b0d523d7a1 100644 --- a/src/main/scala/li/cil/oc/common/item/CuttingWire.scala +++ b/src/main/scala/li/cil/oc/common/item/CuttingWire.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class CuttingWire(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class CuttingWire(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/DataCard.scala b/src/main/scala/li/cil/oc/common/item/DataCard.scala index 69479fd05a..d87f9d3244 100644 --- a/src/main/scala/li/cil/oc/common/item/DataCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DataCard.scala @@ -1,5 +1,11 @@ package li.cil.oc.common.item -class DataCard(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class DataCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/DebugCard.scala b/src/main/scala/li/cil/oc/common/item/DebugCard.scala index 2e325b28f1..7e18f60a1b 100644 --- a/src/main/scala/li/cil/oc/common/item/DebugCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DebugCard.scala @@ -2,11 +2,14 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.Settings.DebugCardAccess import li.cil.oc.common.item.data.DebugCardData import li.cil.oc.server.component.{DebugCard => CDebugCard} import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType @@ -15,8 +18,9 @@ import net.minecraft.util.Util import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class DebugCard(val parent: Delegator) extends traits.Delegate { +class DebugCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { super.tooltipExtended(stack, tooltip) val data = new DebugCardData(stack) diff --git a/src/main/scala/li/cil/oc/common/item/Debugger.scala b/src/main/scala/li/cil/oc/common/item/Debugger.scala index c1265b495d..9b7009dce4 100644 --- a/src/main/scala/li/cil/oc/common/item/Debugger.scala +++ b/src/main/scala/li/cil/oc/common/item/Debugger.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.api.network._ @@ -7,11 +8,14 @@ import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraftforge.common.util.FakePlayer +import net.minecraftforge.common.extensions.IForgeItem -class Debugger(val parent: Delegator) extends traits.Delegate { +class Debugger(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get player match { diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala deleted file mode 100644 index 3562836b3f..0000000000 --- a/src/main/scala/li/cil/oc/common/item/Delegator.scala +++ /dev/null @@ -1,262 +0,0 @@ -package li.cil.oc.common.item - -import java.util - -import com.mojang.blaze3d.matrix.MatrixStack -import li.cil.oc.CreativeTab -import li.cil.oc.OpenComputers -import li.cil.oc.api.driver -import li.cil.oc.api.driver.item.Chargeable -import li.cil.oc.api.event.RobotRenderEvent.MountPoint -import li.cil.oc.api.internal.Robot -import li.cil.oc.client.renderer.item.UpgradeRenderer -import li.cil.oc.common.item.traits.Delegate -import li.cil.oc.integration.opencomputers.{Item => OpenComputersItem} -import li.cil.oc.util.BlockPosition -import net.minecraft.client.util.ITooltipFlag -import net.minecraft.item.ItemGroup -import net.minecraft.entity.Entity -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.BlockItemUseContext -import net.minecraft.item.Item -import net.minecraft.item.Item.Properties -import net.minecraft.item.ItemStack -import net.minecraft.item.ItemUseContext -import net.minecraft.item.Rarity -import net.minecraft.item.UseAction -import net.minecraft.util.ActionResult -import net.minecraft.util.ActionResultType -import net.minecraft.util.Direction -import net.minecraft.util.Hand -import net.minecraft.util.NonNullList -import net.minecraft.util.math.BlockPos -import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent -import net.minecraft.world.IWorldReader -import net.minecraft.world.World -import net.minecraftforge.api.distmarker.Dist -import net.minecraftforge.api.distmarker.OnlyIn - -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer - -object Delegator { - def subItem(stack: ItemStack): Option[Delegate] = - if (!stack.isEmpty) stack.getItem match { - case delegator: Delegator => delegator.subItem(stack.getDamageValue) - case _ => None - } - else None -} - -class Delegator extends Item(new Properties().tab(CreativeTab)) with driver.item.UpgradeRenderer with Chargeable { - - // ----------------------------------------------------------------------- // - // SubItem - // ----------------------------------------------------------------------- // - - @Deprecated - override def getItemStackLimit(stack: ItemStack): Int = - Delegator.subItem(stack) match { - case Some(subItem) => OpenComputersItem.address(stack) match { - case Some(address) => 1 - case _ => subItem.maxStackSize - } - case _ => super.getItemStackLimit(stack) - } - - val subItems: ArrayBuffer[Delegate] = mutable.ArrayBuffer.empty[traits.Delegate] - - def add(subItem: traits.Delegate): Int = { - val itemId = subItems.length - subItems += subItem - itemId - } - - def subItem(damage: Int): Option[Delegate] = - damage match { - case itemId if itemId >= 0 && itemId < subItems.length => Some(subItems(itemId)) - case _ => None - } - - override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) { - // Workaround for MC's untyped lists... - if(allowdedIn(tab)){ - subItems.indices.filter(subItems(_).showInItemList). - map(subItems(_).createItemStack()). - sortBy(_.getDescriptionId). - foreach(list.add) - } - } - - // ----------------------------------------------------------------------- // - // Item - // ----------------------------------------------------------------------- // - - @Deprecated - private var unlocalizedName = super.getDescriptionId() - - @Deprecated - private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = name - - @Deprecated - override def getDescriptionId(stack: ItemStack): String = - Delegator.subItem(stack) match { - case Some(subItem) => "item.oc." + subItem.unlocalizedName - case _ => unlocalizedName - } - - override def getRarity(stack: ItemStack): Rarity = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.rarity(stack) - case _ => Rarity.COMMON - } - -// override def getColorFromItemStack(stack: ItemStack, pass: Int) = -// Delegator.subItem(stack) match { -// case Some(subItem) => subItem.color(stack, pass) -// case _ => super.getColorFromItemStack(stack, pass) -// } - - // ----------------------------------------------------------------------- // - - override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.doesSneakBypassUse(world, pos, player) - case _ => super.doesSneakBypassUse(stack, world, pos, player) - } - - @Deprecated - override def onItemUseFirst(stack: ItemStack, ctx: ItemUseContext): ActionResultType = - if (stack != null) { - Delegator.subItem(stack) match { - case Some(subItem) => { - val pos = BlockPosition(ctx.getClickedPos, ctx.getLevel) - val hitPos = ctx.getClickLocation - subItem.onItemUseFirst(stack, ctx.getPlayer, pos, ctx.getClickedFace, - (hitPos.x - pos.x).toFloat, (hitPos.y - pos.y).toFloat, (hitPos.z - pos.z).toFloat) - } - case _ => super.onItemUseFirst(stack, ctx) - } - } - else super.onItemUseFirst(stack, ctx) - - @Deprecated - def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand) = ActionResultType.PASS - - override def useOn(ctx: ItemUseContext): ActionResultType = - ctx.getItemInHand match { - case stack: ItemStack => Delegator.subItem(stack) match { - case Some(subItem) => { - val world = ctx.getLevel - val pos = BlockPosition(ctx.getClickedPos, world) - val hitPos = ctx.getClickLocation - val success = subItem.onItemUse(ctx.getItemInHand, ctx.getPlayer, pos, ctx.getClickedFace, - (hitPos.x - pos.x).toFloat, (hitPos.y - pos.y).toFloat, (hitPos.z - pos.z).toFloat) - if (success) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS - } - case _ => super.useOn(ctx) - } - case _ => super.useOn(ctx) - } - - override def use(world: World, player: PlayerEntity, hand: Hand): ActionResult[ItemStack] = - player.getItemInHand(hand) match { - case stack: ItemStack => Delegator.subItem(stack) match { - case Some(subItem) => subItem.use(stack, world, player) - case _ => super.use(world, player, hand) - } - case _ => super.use(world, player, hand) - } - - // ----------------------------------------------------------------------- // - - override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.finishUsingItem(stack, world, entity) - case _ => super.finishUsingItem(stack, world, entity) - } - - override def getUseAnimation(stack: ItemStack): UseAction = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.getUseAnimation(stack) - case _ => super.getUseAnimation(stack) - } - - override def getUseDuration(stack: ItemStack): Int = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.getMaxItemUseDuration(stack) - case _ => super.getUseDuration(stack) - } - - override def releaseUsing(stack: ItemStack, world: World, entity: LivingEntity, timeLeft: Int): Unit = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.onPlayerStoppedUsing(stack, entity, timeLeft) - case _ => super.releaseUsing(stack, world, entity, timeLeft) - } - - @Deprecated - def internalGetItemStackDisplayName(stack: ItemStack): String = super.getName(stack).getString - - @Deprecated - override def getName(stack: ItemStack): ITextComponent = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.displayName(stack) match { - case Some(name) => new StringTextComponent(name) - case _ => super.getName(stack) - } - case _ => super.getName(stack) - } - - @OnlyIn(Dist.CLIENT) - override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - super.appendHoverText(stack, world, tooltip, flag) - Delegator.subItem(stack) match { - case Some(subItem) => try subItem.tooltipLines(stack, world, tooltip, flag) catch { - case t: Throwable => OpenComputers.log.warn("Error in item tooltip.", t) - } - case _ => // Nothing to add. - } - } - - override def getDurabilityForDisplay(stack: ItemStack): Double = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.durability(stack) - case _ => super.getDurabilityForDisplay(stack) - } - - override def showDurabilityBar(stack: ItemStack): Boolean = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.showDurabilityBar(stack) - case _ => super.showDurabilityBar(stack) - } - - override def inventoryTick(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean): Unit = - Delegator.subItem(stack) match { - case Some(subItem) => subItem.update(stack, world, player, slot, selected) - case _ => super.inventoryTick(stack, world, player, slot, selected) - } - - override def toString: String = getDescriptionId - - // ----------------------------------------------------------------------- // - - def canCharge(stack: ItemStack): Boolean = - Delegator.subItem(stack) match { - case Some(subItem: Chargeable) => true - case _ => false - } - - def charge(stack: ItemStack, amount: Double, simulate: Boolean): Double = - Delegator.subItem(stack) match { - case Some(subItem: Chargeable) => subItem.charge(stack, amount, simulate) - case _ => amount - } - - // ----------------------------------------------------------------------- // - - override def computePreferredMountPoint(stack: ItemStack, robot: Robot, availableMountPoints: util.Set[String]): String = UpgradeRenderer.preferredMountPoint(stack, availableMountPoints) - - override def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(matrix, stack, mountPoint) -} diff --git a/src/main/scala/li/cil/oc/common/item/DiamondChip.scala b/src/main/scala/li/cil/oc/common/item/DiamondChip.scala index 813d7cae4d..d96aaf9ca0 100644 --- a/src/main/scala/li/cil/oc/common/item/DiamondChip.scala +++ b/src/main/scala/li/cil/oc/common/item/DiamondChip.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class DiamondChip(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class DiamondChip(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/Disk.scala b/src/main/scala/li/cil/oc/common/item/Disk.scala index 38ba6f950e..a9c9266334 100644 --- a/src/main/scala/li/cil/oc/common/item/Disk.scala +++ b/src/main/scala/li/cil/oc/common/item/Disk.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class Disk(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class Disk(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala index 2157349be3..83e51d3ad7 100644 --- a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala @@ -1,13 +1,17 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.common.GuiType import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.{ActionResult, ActionResultType, Hand} import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class DiskDriveMountable(val parent: Delegator) extends traits.Delegate { +class DiskDriveMountable(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 override def use(stack: ItemStack, world: World, player: PlayerEntity) = { diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 614bc32155..43f8ae8d09 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -3,30 +3,33 @@ package li.cil.oc.common.item import java.util import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.client.KeyBindings import li.cil.oc.client.renderer.block.DroneModel -import li.cil.oc.common.entity +import li.cil.oc.common.init.Items import li.cil.oc.common.item.data.DroneData +import li.cil.oc.common.entity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.agent import li.cil.oc.util.BlockPosition import li.cil.oc.util.Rarity import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraftforge.client.event.ModelBakeEvent +import net.minecraftforge.common.extensions.IForgeItem import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Drone(val parent: Delegator) extends traits.Delegate with CustomModel { +class Drone(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { ItemBlacklist.hide(this) - showInItemList = false - @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack) = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Drone, "inventory") @@ -44,7 +47,8 @@ class Drone(val parent: Delegator) extends traits.Delegate with CustomModel { } } - override def rarity(stack: ItemStack) = { + @Deprecated + override def getRarity(stack: ItemStack) = { val data = new DroneData(stack) Rarity.byTier(data.tier) } diff --git a/src/main/scala/li/cil/oc/common/item/DroneCase.scala b/src/main/scala/li/cil/oc/common/item/DroneCase.scala index 8564082602..6b0d306c68 100644 --- a/src/main/scala/li/cil/oc/common/item/DroneCase.scala +++ b/src/main/scala/li/cil/oc/common/item/DroneCase.scala @@ -1,11 +1,16 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class DroneCase(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class DroneCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier override protected def tierFromDriver(stack: ItemStack) = tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/EEPROM.scala b/src/main/scala/li/cil/oc/common/item/EEPROM.scala index bc48baaaa7..c4c5385d3f 100644 --- a/src/main/scala/li/cil/oc/common/item/EEPROM.scala +++ b/src/main/scala/li/cil/oc/common/item/EEPROM.scala @@ -1,25 +1,31 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.BlockPosition import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockReader +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.IWorldReader +import net.minecraftforge.common.extensions.IForgeItem -class EEPROM(val parent: Delegator) extends traits.Delegate { - override def displayName(stack: ItemStack): Option[String] = { +class EEPROM(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { + override def getName(stack: ItemStack): ITextComponent = { if (stack.hasTag) { val tag = stack.getTag if (tag.contains(Settings.namespace + "data")) { val data = tag.getCompound(Settings.namespace + "data") if (data.contains(Settings.namespace + "label")) { - return Some(data.getString(Settings.namespace + "label")) + return new StringTextComponent(data.getString(Settings.namespace + "label")) } } } - super.displayName(stack) + super.getName(stack) } - override def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity): Boolean = true + override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index 56144d8628..31508d8ee3 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -1,22 +1,26 @@ package li.cil.oc.common.item import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.Color import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockReader +import net.minecraft.world.IWorldReader import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.ModelLoader +import net.minecraftforge.common.extensions.IForgeItem -class FloppyDisk(val parent: Delegator) extends traits.Delegate with CustomModel with traits.FileSystemLike { +class FloppyDisk(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.FileSystemLike { // Necessary for anonymous subclasses used for loot disks. - override def unlocalizedName = "floppydisk" + unlocalizedName = "floppydisk" val kiloBytes = Settings.get.floppySize @@ -43,5 +47,5 @@ class FloppyDisk(val parent: Delegator) extends traits.Delegate with CustomModel } } - override def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity): Boolean = true + override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala b/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala index 0fb7074615..28736f19af 100644 --- a/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala @@ -1,9 +1,15 @@ package li.cil.oc.common.item -class GraphicsCard(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.GPULike { - override val unlocalizedName = super.unlocalizedName + tier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class GraphicsCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.GPULike { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier override def gpuTier = tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala index 2f6ba39c42..f9502f07e5 100644 --- a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala @@ -1,20 +1,29 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent +import net.minecraftforge.common.extensions.IForgeItem + +class HardDiskDrive(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.FileSystemLike { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier -class HardDiskDrive(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.FileSystemLike { - override val unlocalizedName: String = super.unlocalizedName + tier val kiloBytes: Int = Settings.get.hddSizes(tier) val platterCount: Int = Settings.get.hddPlatterCounts(tier) - override def displayName(stack: ItemStack): Some[String] = { - val localizedName = parent.internalGetItemStackDisplayName(stack) - Some(if (kiloBytes >= 1024) { - localizedName + s" (${kiloBytes / 1024}MB)" + override def getName(stack: ItemStack): ITextComponent = { + val localizedName = super.getName(stack).copy() + if (kiloBytes >= 1024) { + localizedName.append(s" (${kiloBytes / 1024}MB)") } else { - localizedName + s" (${kiloBytes}KB)" - }) + localizedName.append(s" (${kiloBytes}KB)") + } + localizedName } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index 1dc48b3518..86e1d891b9 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -3,24 +3,28 @@ package li.cil.oc.common.item import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.client.renderer.item.HoverBootRenderer +import li.cil.oc.common.init.Items import li.cil.oc.common.item.data.HoverBootsData import li.cil.oc.util.ItemColorizer +import net.minecraft.block.Blocks import net.minecraft.block.CauldronBlock import net.minecraft.client.renderer.entity.model.BipedModel -import net.minecraft.entity.Entity -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.item.ItemEntity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.block.Blocks import net.minecraft.inventory.EquipmentSlotType import net.minecraft.item.ArmorItem import net.minecraft.item.ArmorMaterial +import net.minecraft.item.Item import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.item.Rarity +import net.minecraft.entity.Entity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.item.ItemEntity +import net.minecraft.entity.player.PlayerEntity import net.minecraft.potion.Effect import net.minecraft.potion.Effects import net.minecraft.potion.EffectInstance +import net.minecraft.util.NonNullList import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn @@ -29,6 +33,7 @@ import net.minecraftforge.common.extensions.IForgeItem class HoverBoots(props: Properties = new Properties().tab(CreativeTab).setNoRepair()) extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with IForgeItem with traits.SimpleItem with traits.Chargeable { + @Deprecated override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON override def maxCharge(stack: ItemStack): Double = Settings.get.bufferHoverBoots @@ -52,6 +57,11 @@ class HoverBoots(props: Properties = new Properties().tab(CreativeTab).setNoRepa }) } + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]): Unit = { + super.fillItemCategory(tab, list) + if (allowdedIn(tab)) list.add(Items.createChargedHoverBoots()) + } + @OnlyIn(Dist.CLIENT) override def getArmorModel[A <: BipedModel[_]](entityLiving: LivingEntity, itemStack: ItemStack, armorSlot: EquipmentSlotType, _default: A): A = { if (armorSlot == slot) { diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala index 9a231ae89f..6b75e2c336 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala @@ -1,14 +1,13 @@ package li.cil.oc.common.item import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.api import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class InkCartridge(val parent: Delegator) extends traits.Delegate { +class InkCartridge(props: Properties = new Properties().tab(CreativeTab).craftRemainder(api.Items.get(Constants.ItemName.InkCartridgeEmpty).item)) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 - - override def getCraftingRemainingItem(): Item = api.Items.get(Constants.ItemName.InkCartridgeEmpty).item - - override def hasCraftingRemainingItem(): Boolean = true } diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala index f7a879ca21..882fb05f4e 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala @@ -1,5 +1,10 @@ package li.cil.oc.common.item -class InkCartridgeEmpty(val parent: Delegator) extends traits.Delegate { +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class InkCartridgeEmpty(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 } diff --git a/src/main/scala/li/cil/oc/common/item/InternetCard.scala b/src/main/scala/li/cil/oc/common/item/InternetCard.scala index 5c43ebc508..4b693c00a0 100644 --- a/src/main/scala/li/cil/oc/common/item/InternetCard.scala +++ b/src/main/scala/li/cil/oc/common/item/InternetCard.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class InternetCard(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class InternetCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/Interweb.scala b/src/main/scala/li/cil/oc/common/item/Interweb.scala index 7e1fdbc78c..8fc2556e25 100644 --- a/src/main/scala/li/cil/oc/common/item/Interweb.scala +++ b/src/main/scala/li/cil/oc/common/item/Interweb.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class Interweb(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class Interweb(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala index 5758705552..fa70c99128 100644 --- a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala @@ -2,19 +2,27 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.extensions.IForgeItem import scala.collection.convert.ImplicitConversionsToScala._ -class LinkedCard(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { +class LinkedCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { val data = stack.getTag.getCompound(Settings.namespace + "data") if (data.contains(Settings.namespace + "tunnel")) { @@ -31,6 +39,5 @@ class LinkedCard(val parent: Delegator) extends traits.Delegate with traits.Item } } } - super.tooltipLines(stack, world, tooltip, flag) } } diff --git a/src/main/scala/li/cil/oc/common/item/Manual.scala b/src/main/scala/li/cil/oc/common/item/Manual.scala index ab57178a4d..4aa0f1d1fd 100644 --- a/src/main/scala/li/cil/oc/common/item/Manual.scala +++ b/src/main/scala/li/cil/oc/common/item/Manual.scala @@ -2,11 +2,14 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.BlockPosition import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType @@ -17,12 +20,13 @@ import net.minecraft.util.text.TextFormatting import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.extensions.IForgeItem -class Manual(val parent: Delegator) extends traits.Delegate { +class Manual(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { @OnlyIn(Dist.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) tooltip.add(new StringTextComponent(TextFormatting.DARK_GRAY.toString + "v" + OpenComputers.get.Version)) - super.tooltipLines(stack, world, tooltip, flag) } override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { diff --git a/src/main/scala/li/cil/oc/common/item/Memory.scala b/src/main/scala/li/cil/oc/common/item/Memory.scala index 9eb4652b15..34b3dc909b 100644 --- a/src/main/scala/li/cil/oc/common/item/Memory.scala +++ b/src/main/scala/li/cil/oc/common/item/Memory.scala @@ -1,7 +1,13 @@ package li.cil.oc.common.item -class Memory(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem - override protected def tooltipName = Option(super.unlocalizedName) +class Memory(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier + + override protected def tooltipName = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/Microchip.scala b/src/main/scala/li/cil/oc/common/item/Microchip.scala index 42fbc20ecf..13a8292192 100644 --- a/src/main/scala/li/cil/oc/common/item/Microchip.scala +++ b/src/main/scala/li/cil/oc/common/item/Microchip.scala @@ -1,12 +1,18 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.util.Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class Microchip(val parent: Delegator, val tier: Int) extends traits.Delegate { - override val unlocalizedName = super.unlocalizedName + tier +class Microchip(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) - override def rarity(stack: ItemStack) = Rarity.byTier(tier) + @Deprecated + override def getRarity(stack: ItemStack) = Rarity.byTier(tier) } diff --git a/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala index 70d092b68d..77ebab2543 100644 --- a/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala +++ b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala @@ -1,11 +1,16 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class MicrocontrollerCase(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class MicrocontrollerCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier override protected def tierFromDriver(stack: ItemStack) = tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala index 0da52e65b7..5efa18ae55 100644 --- a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala @@ -3,15 +3,18 @@ package li.cil.oc.common.item import java.util import com.google.common.base.Strings +import li.cil.oc.CreativeTab import li.cil.oc.api import li.cil.oc.common.item.data.NanomachineData import li.cil.oc.common.nanomachines.ControllerImpl import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.UseAction -import net.minecraft.item.Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraft.item.Rarity +import net.minecraft.item.UseAction import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Hand @@ -20,13 +23,15 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.extensions.IForgeItem -class Nanomachines(val parent: Delegator) extends traits.Delegate { - override def rarity(stack: ItemStack): Rarity = Rarity.UNCOMMON +class Nanomachines(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { + @Deprecated + override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON @OnlyIn(Dist.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { - super.tooltipLines(stack, world, tooltip, flag) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (stack.hasTag) { val data = new NanomachineData(stack) if (!Strings.isNullOrEmpty(data.uuid)) { @@ -42,7 +47,7 @@ class Nanomachines(val parent: Delegator) extends traits.Delegate { override def getUseAnimation(stack: ItemStack): UseAction = UseAction.EAT - override def getMaxItemUseDuration(stack: ItemStack): Int = 32 + override def getUseDuration(stack: ItemStack): Int = 32 override def finishUsingItem(stack: ItemStack, world: World, entity: LivingEntity): ItemStack = { entity match { diff --git a/src/main/scala/li/cil/oc/common/item/NetworkCard.scala b/src/main/scala/li/cil/oc/common/item/NetworkCard.scala index 89b64cf928..3c2cb1c784 100644 --- a/src/main/scala/li/cil/oc/common/item/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/common/item/NetworkCard.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class NetworkCard(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class NetworkCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/NumPad.scala b/src/main/scala/li/cil/oc/common/item/NumPad.scala index 57d50028e1..5085a84d22 100644 --- a/src/main/scala/li/cil/oc/common/item/NumPad.scala +++ b/src/main/scala/li/cil/oc/common/item/NumPad.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class NumPad(val parent: Delegator) extends traits.Delegate \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class NumPad(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/Present.scala b/src/main/scala/li/cil/oc/common/item/Present.scala index 35025bf2db..5169b725ba 100644 --- a/src/main/scala/li/cil/oc/common/item/Present.scala +++ b/src/main/scala/li/cil/oc/common/item/Present.scala @@ -3,23 +3,29 @@ package li.cil.oc.common.item import java.util.Random import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ItemUtils import net.minecraft.entity.player.PlayerEntity -import net.minecraft.util.SoundEvents +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.item.crafting.RecipeManager import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType +import net.minecraft.util.NonNullList import net.minecraft.util.SoundCategory +import net.minecraft.util.SoundEvents import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem import scala.collection.mutable -class Present(val parent: Delegator) extends traits.Delegate { - showInItemList = false +class Present(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) {} override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (stack.getCount > 0) { diff --git a/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala index 37a0097b3c..f0d4efd9fe 100644 --- a/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class PrintedCircuitBoard(val parent: Delegator) extends traits.Delegate +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class PrintedCircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala index 5e52e7416e..6f0d70ae5d 100644 --- a/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class RawCircuitBoard(val parent: Delegator) extends traits.Delegate \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class RawCircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala index a53a6fbf3e..406cd0b4ec 100644 --- a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala +++ b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala @@ -2,14 +2,23 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.common.Tier +import li.cil.oc.integration.opencomputers.ModOpenComputers +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack +import net.minecraft.util.NonNullList +import net.minecraftforge.common.extensions.IForgeItem -class RedstoneCard(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class RedstoneCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) - // Note: T2 is enabled in mod integration, if it makes sense. - showInItemList = tier == Tier.One + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) { + if (tier == Tier.One || ModOpenComputers.hasRedstoneCardT2) super.fillItemCategory(tab, list) + } } diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index 824500c729..ff10deab75 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.client.KeyBindings import li.cil.oc.common.GuiType @@ -10,6 +11,8 @@ import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.entity.player.PlayerEntity import net.minecraft.item // Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType @@ -17,16 +20,19 @@ import net.minecraft.util.Hand import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem import scala.collection.mutable import scala.collection.convert.ImplicitConversionsToScala._ -class Server(val parent: Delegator, val tier: Int) extends traits.Delegate { - override val unlocalizedName: String = super.unlocalizedName + tier +class Server(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) - override def rarity(stack: ItemStack): item.Rarity = Rarity.byTier(tier) + @Deprecated + override def getRarity(stack: ItemStack): item.Rarity = Rarity.byTier(tier) override def maxStackSize = 1 diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index d153e05da8..392f423a76 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotification} import com.google.common.collect.ImmutableMap import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -43,6 +44,9 @@ import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.{PlayerEntity, ServerPlayerEntity} import net.minecraft.item // Rarity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.server.integrated.IntegratedServer @@ -50,14 +54,17 @@ import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand +import net.minecraft.util.NonNullList import net.minecraft.util.ResourceLocation import net.minecraft.util.Util +import net.minecraft.util.math.BlockPos import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.ModelLoader +import net.minecraftforge.common.extensions.IForgeItem import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.event.TickEvent.ClientTickEvent import net.minecraftforge.event.TickEvent.ServerTickEvent @@ -68,12 +75,9 @@ import scala.collection.JavaConverters.asJavaIterable import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ -class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel with traits.Chargeable { +class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.Chargeable { final val TimeToAnalyze = 10 - // Must be assembled to be usable so we hide it in the item list. - showInItemList = false - override def maxStackSize = 1 // ----------------------------------------------------------------------- // @@ -94,14 +98,15 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit } } - override def rarity(stack: ItemStack): item.Rarity = { + @Deprecated + override def getRarity(stack: ItemStack): item.Rarity = { val data = new TabletData(stack) Rarity.byTier(data.tier) } override def showDurabilityBar(stack: ItemStack) = true - override def durability(stack: ItemStack): Double = { + override def getDurabilityForDisplay(stack: ItemStack): Double = { if (stack.hasTag) { val data = Tablet.Client.getWeak(stack) match { case Some(wrapper) => wrapper.data @@ -152,7 +157,10 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit // ----------------------------------------------------------------------- // - override def update(stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean): Unit = + // Must be assembled to be usable so we hide it in the item list. + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) {} + + override def inventoryTick(stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean): Unit = entity match { case player: PlayerEntity => // Play an audio cue to let players know when they finished analyzing a block. @@ -163,9 +171,9 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit case _ => } - override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = { - Tablet.currentlyAnalyzing = Some((position, side, hitX, hitY, hitZ)) - super.onItemUseFirst(stack, player, position, side, hitX, hitY, hitZ) + override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { + Tablet.currentlyAnalyzing = Some((BlockPosition(pos, world), side, hitX, hitY, hitZ)) + super.onItemUseFirst(stack, player, world, pos, side, hitX, hitY, hitZ, hand) } override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { @@ -178,13 +186,12 @@ class Tablet(val parent: Delegator) extends traits.Delegate with CustomModel wit new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } - override def getMaxItemUseDuration(stack: ItemStack): Int = 72000 + override def getUseDuration(stack: ItemStack): Int = 72000 - override def onPlayerStoppedUsing(stack: ItemStack, entity: LivingEntity, duration: Int): Unit = { + override def releaseUsing(stack: ItemStack, world: World, entity: LivingEntity, duration: Int): Unit = { entity match { case player: PlayerEntity => - val world = player.level - val didAnalyze = getMaxItemUseDuration(stack) - duration >= TimeToAnalyze + val didAnalyze = getUseDuration(stack) - duration >= TimeToAnalyze if (didAnalyze) { if (!world.isClientSide) { Tablet.currentlyAnalyzing match { diff --git a/src/main/scala/li/cil/oc/common/item/TabletCase.scala b/src/main/scala/li/cil/oc/common/item/TabletCase.scala index 7ac5dcd584..fe3717818b 100644 --- a/src/main/scala/li/cil/oc/common/item/TabletCase.scala +++ b/src/main/scala/li/cil/oc/common/item/TabletCase.scala @@ -1,11 +1,16 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class TabletCase(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class TabletCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier override protected def tierFromDriver(stack: ItemStack) = tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 9905c3ff3d..88321f8e25 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.item import java.util import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType @@ -10,6 +11,8 @@ import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.Hand @@ -20,15 +23,16 @@ import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.ModelLoader +import net.minecraftforge.common.extensions.IForgeItem -class Terminal(val parent: Delegator) extends traits.Delegate with CustomModel { +class Terminal(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { override def maxStackSize = 1 def hasServer(stack: ItemStack) = stack.hasTag && stack.getTag.contains(Settings.namespace + "server") @OnlyIn(Dist.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - super.tooltipLines(stack, world, tooltip, flag) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (hasServer(stack)) { val server = stack.getTag.getString(Settings.namespace + "server") tooltip.add(new StringTextComponent("§8" + server.substring(0, 13) + "...§7")) diff --git a/src/main/scala/li/cil/oc/common/item/TerminalServer.scala b/src/main/scala/li/cil/oc/common/item/TerminalServer.scala index 7fd58da8bd..0f8b2edfd9 100644 --- a/src/main/scala/li/cil/oc/common/item/TerminalServer.scala +++ b/src/main/scala/li/cil/oc/common/item/TerminalServer.scala @@ -1,7 +1,11 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem -class TerminalServer(val parent: Delegator) extends traits.Delegate { +class TerminalServer(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipData = Seq(Settings.get.terminalsPerServer) } diff --git a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala index b955020898..ebfab17f51 100644 --- a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala +++ b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala @@ -1,17 +1,21 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block import net.minecraft.client.Minecraft import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.Util import net.minecraftforge.client.model.ModelDataManager +import net.minecraftforge.common.extensions.IForgeItem -class TexturePicker(val parent: Delegator) extends traits.Delegate { +class TexturePicker(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { player.level.getBlock(position) match { case block: Block => diff --git a/src/main/scala/li/cil/oc/common/item/Transistor.scala b/src/main/scala/li/cil/oc/common/item/Transistor.scala index eb84cd8a60..6e0d23f9aa 100644 --- a/src/main/scala/li/cil/oc/common/item/Transistor.scala +++ b/src/main/scala/li/cil/oc/common/item/Transistor.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class Transistor(val parent: Delegator) extends traits.Delegate \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class Transistor(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala b/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala index ffb4e6da3a..3355c6b843 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeAngel(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeAngel(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala index db3364aa5f..2e9a9809e3 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala @@ -1,20 +1,25 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.api.driver.item.Chargeable import li.cil.oc.common.item.data.NodeData +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeBattery(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.Chargeable { - override val unlocalizedName: String = super.unlocalizedName + tier +class UpgradeBattery(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.Chargeable { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(Settings.get.bufferCapacitorUpgrades(tier).toInt) override def showDurabilityBar(stack: ItemStack) = true - override def durability(stack: ItemStack): Double = { + override def getDurabilityForDisplay(stack: ItemStack): Double = { val data = new NodeData(stack) 1 - data.buffer.getOrElse(0.0) / Settings.get.bufferCapacitorUpgrades(tier) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala b/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala index da2da9081c..e17d93145a 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeChunkloader(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeChunkloader(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala b/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala index d2edec129a..3da6fdb36f 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala @@ -1,9 +1,15 @@ package li.cil.oc.common.item -class UpgradeContainerCard(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem - override protected def tooltipName = Option(super.unlocalizedName) +class UpgradeContainerCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier + + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(tier + 1) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala b/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala index b118d5158b..e9f0a89910 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala @@ -1,9 +1,15 @@ package li.cil.oc.common.item -class UpgradeContainerUpgrade(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem - override protected def tooltipName = Option(super.unlocalizedName) +class UpgradeContainerUpgrade(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier + + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(tier + 1) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala index e822f1e34c..3c5d293091 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeCrafting(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeCrafting(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala index ec33304365..19934f1abe 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala @@ -1,24 +1,30 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType import li.cil.oc.util.Rarity import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Hand import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeDatabase(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class UpgradeDatabase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(Settings.get.databaseEntriesPerTier(tier)) - override def rarity(stack: ItemStack) = Rarity.byTier(tier) + @Deprecated + override def getRarity(stack: ItemStack) = Rarity.byTier(tier) override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching) { diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala index 6fa1162035..557a4fad18 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala @@ -2,26 +2,29 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.util.UpgradeExperience +import li.cil.oc.CreativeTab +import li.cil.oc.Localization +import li.cil.oc.util.{UpgradeExperience => ExperienceUtil} import net.minecraft.client.util.ITooltipFlag +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.api.distmarker.{Dist, OnlyIn} -import li.cil.oc.Localization; +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeExperience(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - - @OnlyIn(Dist.CLIENT) override - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { +class UpgradeExperience(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (stack.hasTag) { val nbt = li.cil.oc.integration.opencomputers.Item.dataTag(stack) - val experience = UpgradeExperience.getExperience(nbt) - val level = UpgradeExperience.calculateLevelFromExperience(experience) - val reportedLevel = UpgradeExperience.calculateExperienceLevel(level, experience) + val experience = ExperienceUtil.getExperience(nbt) + val level = ExperienceUtil.calculateLevelFromExperience(experience) + val reportedLevel = ExperienceUtil.calculateExperienceLevel(level, experience) tooltip.add(new StringTextComponent(Localization.Tooltip.ExperienceLevel(reportedLevel))) } - super.tooltipLines(stack, world, tooltip, flag) } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala index 8dd2f3be4e..51e29f9d1a 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala @@ -1,7 +1,11 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeGenerator(val parent: Delegator) extends traits.Delegate with traits.ItemTier { +class UpgradeGenerator(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipData = Seq((Settings.get.generatorEfficiency * 100).toInt) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala b/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala index 3afd012221..4b0cb7b7d6 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala @@ -1,11 +1,16 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeHover(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class UpgradeHover(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(Settings.get.upgradeFlightHeight(tier)) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala b/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala index 5ac0ef7e8b..c00da29fd1 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeInventory(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeInventory(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala b/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala index 773458084f..d1e831842e 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeInventoryController(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeInventoryController(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala index 42f0633778..ce586bd5d5 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeLeash(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeLeash(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala index 46b75ae135..9b7daf8f94 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala @@ -2,26 +2,33 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.util.BlockPosition +import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.Settings +import li.cil.oc.util.BlockPosition import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.util.ActionResultType import net.minecraft.util.Direction +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent +import net.minecraft.world.World +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeMF(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = { +class UpgradeMF(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { if (!player.level.isClientSide && player.isCrouching) { val data = stack.getOrCreateTag - data.putString(Settings.namespace + "dimension", player.level.dimension.location.toString) - data.putIntArray(Settings.namespace + "coord", Array(position.x, position.y, position.z, side.ordinal())) + data.putString(Settings.namespace + "dimension", world.dimension.location.toString) + data.putIntArray(Settings.namespace + "coord", Array(pos.getX, pos.getY, pos.getZ, side.ordinal())) return ActionResultType.sidedSuccess(player.level.isClientSide) } - super.onItemUseFirst(stack, player, position, side, hitX, hitY, hitZ) + super.onItemUseFirst(stack, player, world, pos, side, hitX, hitY, hitZ, hand) } override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala index 0c8ac4870c..b092e29013 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeNavigation(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeNavigation(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala b/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala index 83f6f7cf3d..fbcbd269c0 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradePiston(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradePiston(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala b/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala index 56fa772c56..73f0c23256 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeSign(val parent: Delegator) extends traits.Delegate with traits.ItemTier +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeSign(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala index f0487450f0..5f98298d59 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala @@ -1,7 +1,11 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.Settings +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeSolarGenerator(val parent: Delegator) extends traits.Delegate with traits.ItemTier { +class UpgradeSolarGenerator(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipData = Seq((Settings.get.solarGeneratorEfficiency * 100).toInt) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala b/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala index befd47bc16..75a687f891 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala @@ -1,6 +1,11 @@ package li.cil.oc.common.item -class UpgradeStickyPiston(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override protected def tooltipName: Option[String] = Option(super.unlocalizedName) +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeStickyPiston(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + override protected def tooltipName: Option[String] = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala index 6d98ff6019..487ee0a1cd 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala @@ -2,9 +2,12 @@ package li.cil.oc.common.item import java.util +import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent @@ -12,10 +15,12 @@ import net.minecraft.world.World import net.minecraftforge.fluids.FluidStack import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.common.extensions.IForgeItem -class UpgradeTank(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - @OnlyIn(Dist.CLIENT) override - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { +class UpgradeTank(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (stack.hasTag) { FluidStack.loadFluidStackFromNBT(stack.getTag.getCompound(Settings.namespace + "data")) match { case stack: FluidStack => @@ -23,6 +28,5 @@ class UpgradeTank(val parent: Delegator) extends traits.Delegate with traits.Ite case _ => } } - super.tooltipLines(stack, world, tooltip, flag) } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala index ff0d347341..01f3deba4e 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeTankController(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeTankController(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala index e9dba02c85..00ed41f24b 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala @@ -1,3 +1,8 @@ package li.cil.oc.common.item -class UpgradeTractorBeam(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeTractorBeam(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala index 30806741d2..e54be35a16 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala @@ -1,5 +1,10 @@ package li.cil.oc.common.item -class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier { - override protected def tooltipName: Option[String] = Option(super.unlocalizedName) +import li.cil.oc.CreativeTab +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem + +class UpgradeTrading(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + override protected def tooltipName: Option[String] = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala index 6356d87711..48de2d44b4 100644 --- a/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala @@ -1,9 +1,14 @@ package li.cil.oc.common.item +import li.cil.oc.CreativeTab import li.cil.oc.common.Tier +import net.minecraft.item.Item +import net.minecraft.item.Item.Properties +import net.minecraftforge.common.extensions.IForgeItem -class WirelessNetworkCard(val parent: Delegator, var tier: Int) extends traits.Delegate with traits.ItemTier { - override val unlocalizedName = super.unlocalizedName + tier +class WirelessNetworkCard(var tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { + @Deprecated + override def getDescriptionId = super.getDescriptionId + tier - override protected def tooltipName = Option(super.unlocalizedName) + override protected def tooltipName = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index d165d508e0..9fff0177a8 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -27,7 +27,7 @@ object Wrench { class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench.WrenchType, 1).tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with api.internal.Wrench { override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true - override def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { + override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { if (world.isLoaded(pos) && world.mayInteract(player, pos)) { val state = world.getBlockState(pos) state.getBlock match { @@ -42,10 +42,10 @@ class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench player.swing(hand) if (!world.isClientSide) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS } - else super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) + else super.onItemUseFirst(stack, player, world, pos, side, hitX, hitY, hitZ, hand) } } - else super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand) + else super.onItemUseFirst(stack, player, world, pos, side, hitX, hitY, hitZ, hand) } def useWrenchOnBlock(player: PlayerEntity, world: World, pos: BlockPos, simulate: Boolean): Boolean = { diff --git a/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala index 848345eb04..6673fe12b6 100644 --- a/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/DebugCardData.scala @@ -1,8 +1,8 @@ package li.cil.oc.common.item.data -import li.cil.oc.server.component.DebugCard.AccessContext import li.cil.oc.Constants import li.cil.oc.Settings +import li.cil.oc.server.component.DebugCard.AccessContext import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT diff --git a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala index cc5dd30c33..cc8ab37f35 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala @@ -21,7 +21,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ import scala.language.existentials -trait CPULike extends Delegate { +trait CPULike extends SimpleItem { def cpuTier: Int override protected def tooltipData: Seq[Any] = Seq(Settings.get.cpuComponentSupport(cpuTier)) diff --git a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala b/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala deleted file mode 100644 index d00458736d..0000000000 --- a/src/main/scala/li/cil/oc/common/item/traits/Delegate.scala +++ /dev/null @@ -1,121 +0,0 @@ -package li.cil.oc.common.item.traits - -import java.util - -import li.cil.oc.Settings -import li.cil.oc.api -import li.cil.oc.api.driver.DriverItem -import li.cil.oc.common.item.Delegator -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.Rarity -import li.cil.oc.util.Tooltip -import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.Entity -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item // Rarity -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.UseAction -import net.minecraft.util.ActionResult -import net.minecraft.util.ActionResultType -import net.minecraft.util.Direction -import net.minecraft.util.math.BlockPos -import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent -import net.minecraft.world.IBlockReader -import net.minecraft.world.World -import net.minecraftforge.api.distmarker.Dist -import net.minecraftforge.api.distmarker.OnlyIn - -import scala.collection.convert.ImplicitConversionsToScala._ - -trait Delegate { - def parent: Delegator - - def unlocalizedName: String = getClass.getSimpleName.toLowerCase - - protected def tooltipName = Option(unlocalizedName) - - protected def tooltipData = Seq.empty[Any] - - var showInItemList = true - - val itemId: Int = parent.add(this) - - def maxStackSize = 64 - - def createItemStack(amount: Int = 1) = { - val stack = new ItemStack(parent, amount) - stack.setDamageValue(itemId) - stack - } - - // ----------------------------------------------------------------------- // - - def doesSneakBypassUse(world: IBlockReader, pos: BlockPos, player: PlayerEntity) = false - - def onItemUseFirst(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): ActionResultType = ActionResultType.PASS - - @Deprecated - def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = false - - def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = new ActionResult(ActionResultType.PASS, stack) - - def getUseAnimation(stack: ItemStack): UseAction = UseAction.NONE - - def getMaxItemUseDuration(stack: ItemStack) = 0 - - def finishUsingItem(stack: ItemStack, world: World, player: LivingEntity): ItemStack = stack - - def onPlayerStoppedUsing(stack: ItemStack, player: LivingEntity, duration: Int) {} - - def update(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean) {} - - // ----------------------------------------------------------------------- // - - def rarity(stack: ItemStack): item.Rarity = Rarity.byTier(tierFromDriver(stack)) - - protected def tierFromDriver(stack: ItemStack): Int = - api.Driver.driverFor(stack) match { - case driver: DriverItem => driver.tier(stack) - case _ => 0 - } - - def color(stack: ItemStack, pass: Int) = 0xFFFFFF - - @Deprecated - def getCraftingRemainingItem(): Item = null - - @Deprecated - def hasCraftingRemainingItem(): Boolean = false - - def displayName(stack: ItemStack): Option[String] = None - - @OnlyIn(Dist.CLIENT) - def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - if (tooltipName.isDefined) { - for (curr <- Tooltip.get(tooltipName.get, tooltipData: _*)) { - tooltip.add(new StringTextComponent(curr)) - } - tooltipExtended(stack, tooltip) - } - tooltipCosts(stack, tooltip) - } - - // For stuff that goes to the normal 'extended' tooltip, before the costs. - protected def tooltipExtended(stack: ItemStack, tooltip: java.util.List[ITextComponent]) {} - - protected def tooltipCosts(stack: ItemStack, tooltip: java.util.List[ITextComponent]) { - if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { - val data = stack.getTag.getCompound(Settings.namespace + "data") - if (data.contains("node") && data.getCompound("node").contains("address")) { - tooltip.add(new StringTextComponent("§8" + data.getCompound("node").getString("address").substring(0, 13) + "...§7")) - } - } - } - - def showDurabilityBar(stack: ItemStack) = false - - def durability(stack: ItemStack) = 0.0 -} diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala index 3bc2721afd..c7f4abea03 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -16,13 +16,17 @@ import net.minecraft.util.Hand import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn -trait FileSystemLike extends Delegate { +trait FileSystemLike extends SimpleItem { override protected def tooltipName = None def kiloBytes: Int - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag): Unit = { + @OnlyIn(Dist.CLIENT) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (stack.hasTag) { val nbt = stack.getTag if (nbt.contains(Settings.namespace + "data")) { @@ -42,7 +46,6 @@ trait FileSystemLike extends Delegate { tooltip.add(new StringTextComponent(Localization.Tooltip.DiskMode(data.isUnmanaged))) tooltip.add(new StringTextComponent(Localization.Tooltip.DiskLock(data.lockInfo))) } - super.tooltipLines(stack, world, tooltip, flag) } override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { diff --git a/src/main/scala/li/cil/oc/common/item/traits/GPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/GPULike.scala index ffd255ef6d..73f2ec3c6c 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/GPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/GPULike.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.item.traits import li.cil.oc.Settings import li.cil.oc.util.PackedColor -trait GPULike extends Delegate { +trait GPULike extends SimpleItem { def gpuTier: Int override protected def tooltipData: Seq[Any] = { diff --git a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala index 3dc68f749a..1c2ce0a5c1 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala @@ -12,11 +12,10 @@ import net.minecraft.world.World import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -trait ItemTier extends Delegate { - self: Delegate => +trait ItemTier extends SimpleItem { @OnlyIn(Dist.CLIENT) - override def tooltipLines(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - super.tooltipLines(stack, world, tooltip, flag) + override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { + super.appendHoverText(stack, world, tooltip, flag) if (flag.isAdvanced) { tooltip.add(new StringTextComponent(Localization.Tooltip.Tier(tierFromDriver(stack) + 1))) } diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 0218f43c7e..50c79d8214 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -2,10 +2,16 @@ package li.cil.oc.common.item.traits import java.util +import com.mojang.blaze3d.matrix.MatrixStack import li.cil.oc.CreativeTab import li.cil.oc.Settings -import li.cil.oc.common.item.Delegator +import li.cil.oc.api +import li.cil.oc.api.event.RobotRenderEvent.MountPoint +import li.cil.oc.api.internal.Robot +import li.cil.oc.client.renderer.item.UpgradeRenderer import li.cil.oc.common.tileentity +import li.cil.oc.integration.opencomputers.{Item => OpenComputersItem} +import li.cil.oc.util.BlockPosition import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity @@ -14,6 +20,7 @@ import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.item.ItemUseContext +import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -27,16 +34,24 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToScala._ -trait SimpleItem extends Item { +trait SimpleItem extends Item with api.driver.item.UpgradeRenderer { def createItemStack(amount: Int = 1) = new ItemStack(this, amount) @Deprecated - private var unlocalizedName = super.getDescriptionId() + protected var unlocalizedName = getClass.getSimpleName.toLowerCase @Deprecated - private[oc] def setUnlocalizedName(name: String): Unit = unlocalizedName = "item." + name + override def getDescriptionId = "item.oc." + unlocalizedName - override def getDescriptionId = unlocalizedName + @Deprecated + def maxStackSize = 64 + + @Deprecated + override def getItemStackLimit(stack: ItemStack): Int = + OpenComputersItem.address(stack) match { + case Some(address) => 1 + case _ => maxStackSize + } override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = { world.getBlockEntity(pos) match { @@ -49,19 +64,70 @@ trait SimpleItem extends Item { override def onItemUseFirst(stack: ItemStack, ctx: ItemUseContext): ActionResultType = { val pos = ctx.getClickedPos val hitPos = ctx.getClickLocation - onItemUseFirst(ctx.getPlayer, ctx.getPlayer.level, pos, ctx.getClickedFace, + onItemUseFirst(stack, ctx.getPlayer, ctx.getPlayer.level, pos, ctx.getClickedFace, (hitPos.x - pos.getX).toFloat, (hitPos.y - pos.getY).toFloat, (hitPos.z - pos.getZ).toFloat, ctx.getHand) } @Deprecated - def onItemUseFirst(player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand) = ActionResultType.PASS + def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = ActionResultType.PASS + + @Deprecated + override def useOn(ctx: ItemUseContext): ActionResultType = + ctx.getItemInHand match { + case stack: ItemStack => { + val world = ctx.getLevel + val pos = BlockPosition(ctx.getClickedPos, world) + val hitPos = ctx.getClickLocation + val success = onItemUse(stack, ctx.getPlayer, pos, ctx.getClickedFace, + (hitPos.x - pos.x).toFloat, (hitPos.y - pos.y).toFloat, (hitPos.z - pos.z).toFloat) + if (success) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS + } + case _ => super.useOn(ctx) + } + + @Deprecated + def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = false + + @Deprecated + override def use(world: World, player: PlayerEntity, hand: Hand): ActionResult[ItemStack] = + player.getItemInHand(hand) match { + case stack: ItemStack => use(stack, world, player) + case _ => super.use(world, player, hand) + } + + @Deprecated + def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = new ActionResult(ActionResultType.PASS, stack) + + protected def tierFromDriver(stack: ItemStack): Int = + api.Driver.driverFor(stack) match { + case driver: api.driver.DriverItem => driver.tier(stack) + case _ => 0 + } + + protected def tooltipName = Option(unlocalizedName) + + protected def tooltipData = Seq.empty[Any] @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { - tooltip.add(new StringTextComponent(curr)) + if (tooltipName.isDefined) { + for (curr <- Tooltip.get(tooltipName.get, tooltipData: _*)) { + tooltip.add(new StringTextComponent(curr)) + } + tooltipExtended(stack, tooltip) } + else { + for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { + tooltip.add(new StringTextComponent(curr)) + } + } + tooltipCosts(stack, tooltip) + } + + // For stuff that goes to the normal 'extended' tooltip, before the costs. + protected def tooltipExtended(stack: ItemStack, tooltip: java.util.List[ITextComponent]) {} + protected def tooltipCosts(stack: ItemStack, tooltip: java.util.List[ITextComponent]) { if (stack.hasTag && stack.getTag.contains(Settings.namespace + "data")) { val data = stack.getTag.getCompound(Settings.namespace + "data") if (data.contains("node") && data.getCompound("node").contains("address")) { @@ -69,4 +135,10 @@ trait SimpleItem extends Item { } } } + + // ----------------------------------------------------------------------- // + + override def computePreferredMountPoint(stack: ItemStack, robot: Robot, availableMountPoints: util.Set[String]): String = UpgradeRenderer.preferredMountPoint(stack, availableMountPoints) + + override def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(matrix, stack, mountPoint) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index a0778964d3..de5479f9b8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -25,7 +25,6 @@ import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.integration.Mods import li.cil.oc.integration.opencomputers.DriverLinkedCard import li.cil.oc.server.network.QuantumNetwork @@ -231,8 +230,8 @@ class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) w case Some(driver) if driver.slot(stack) == Slot.CPU => relayDelay = math.max(1, relayBaseDelay - ((driver.tier(stack) + 1) * relayDelayPerUpgrade).toInt) case Some(driver) if driver.slot(stack) == Slot.Memory => - relayAmount = math.max(1, relayBaseAmount + (Delegator.subItem(stack) match { - case Some(ram: item.Memory) => (ram.tier + 1) * relayAmountPerUpgrade + relayAmount = math.max(1, relayBaseAmount + (stack.getItem match { + case ram: item.Memory => (ram.tier + 1) * relayAmountPerUpgrade case _ => (driver.tier(stack) + 1) * (relayAmountPerUpgrade * 2) })) case Some(driver) if driver.slot(stack) == Slot.HDD => diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala index 752ffed160..2f1e145e2d 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverAPU.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.driver.item.HostAware import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.Tier -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -27,14 +26,14 @@ object DriverAPU extends DriverCPU with HostAware { } override def cpuTier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(apu: common.item.APU) => apu.cpuTier + stack.getItem match { + case apu: common.item.APU => apu.cpuTier case _ => Tier.One } def gpuTier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(apu: common.item.APU) => apu.gpuTier + stack.getItem match { + case apu: common.item.APU => apu.gpuTier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala index 1b3a6b9ea8..c421e4aff7 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala @@ -7,7 +7,6 @@ import li.cil.oc.api import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import li.cil.oc.server.machine.luac.NativeLuaArchitecture import net.minecraft.item.ItemStack @@ -31,8 +30,8 @@ abstract class DriverCPU extends Item with api.driver.item.MutableProcessor with override def tier(stack: ItemStack) = cpuTier(stack) def cpuTier(stack: ItemStack): Int = - Delegator.subItem(stack) match { - case Some(cpu: item.CPU) => cpu.cpuTier + stack.getItem match { + case cpu: item.CPU => cpu.cpuTier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala index eae7874692..e372a91173 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverComponentBus.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.driver.item.Processor import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import net.minecraft.item.ItemStack object DriverComponentBus extends Item with Processor { @@ -24,14 +23,14 @@ object DriverComponentBus extends Item with Processor { // Clamp item tier because the creative bus needs to fit into tier 3 slots. override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(bus: item.ComponentBus) => bus.tier min Tier.Three + stack.getItem match { + case bus: item.ComponentBus => bus.tier min Tier.Three case _ => Tier.One } override def supportedComponents(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(bus: item.ComponentBus) => Settings.get.cpuComponentSupport(bus.tier) + stack.getItem match { + case bus: item.ComponentBus => Settings.get.cpuComponentSupport(bus.tier) case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala index 2e3531ae60..5a174f9710 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerCard.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.driver.item.Container import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import net.minecraft.item.ItemStack object DriverContainerCard extends Item with Container { @@ -25,8 +24,8 @@ object DriverContainerCard extends Item with Container { override def providedTier(stack: ItemStack) = tier(stack) override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(container: item.UpgradeContainerCard) => container.tier + stack.getItem match { + case container: item.UpgradeContainerCard => container.tier case _ => Tier.One } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala index fc99bb9d3c..1a274a99d1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverContainerUpgrade.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.driver.item.Container import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import net.minecraft.item.ItemStack object DriverContainerUpgrade extends Item with Container { @@ -25,8 +24,8 @@ object DriverContainerUpgrade extends Item with Container { override def providedTier(stack: ItemStack) = tier(stack) override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(container: item.UpgradeContainerUpgrade) => container.tier + stack.getItem match { + case container: item.UpgradeContainerUpgrade => container.tier case _ => Tier.One } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala index 6b467a7f3c..0f57efe557 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.Slot import li.cil.oc.common.Tier -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -29,8 +28,8 @@ object DriverDataCard extends Item { override def slot(stack: ItemStack) = Slot.Card override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(data: common.item.DataCard) => data.tier + stack.getItem match { + case data: common.item.DataCard => data.tier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala index d8b1625843..866ded6c4d 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -8,7 +8,6 @@ import li.cil.oc.api import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Loot import li.cil.oc.common.Slot -import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.FloppyDisk import li.cil.oc.common.item.HardDiskDrive import li.cil.oc.common.item.data.DriveData @@ -30,22 +29,22 @@ object DriverFileSystem extends Item { override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = if (host.world != null && host.world.isClientSide) null - else Delegator.subItem(stack) match { - case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, hdd.platterCount, host, hdd.tier + 2) - case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, 1, host, 1) + else stack.getItem match { + case hdd: HardDiskDrive => createEnvironment(stack, hdd.kiloBytes * 1024, hdd.platterCount, host, hdd.tier + 2) + case disk: FloppyDisk => createEnvironment(stack, Settings.get.floppySize * 1024, 1, host, 1) case _ => null } override def slot(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(hdd: HardDiskDrive) => Slot.HDD - case Some(disk: FloppyDisk) => Slot.Floppy + stack.getItem match { + case hdd: HardDiskDrive => Slot.HDD + case disk: FloppyDisk => Slot.Floppy case _ => throw new IllegalArgumentException() } override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(hdd: HardDiskDrive) => hdd.tier + stack.getItem match { + case hdd: HardDiskDrive => hdd.tier case _ => 0 } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala index a98b61a2c8..7803d283a6 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverGraphicsCard.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.Slot import li.cil.oc.common.Tier -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -30,8 +29,8 @@ object DriverGraphicsCard extends Item with HostAware { override def slot(stack: ItemStack) = Slot.Card override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(gpu: common.item.GraphicsCard) => gpu.gpuTier + stack.getItem match { + case gpu: common.item.GraphicsCard => gpu.gpuTier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverMemory.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverMemory.scala index 2d3a4f0993..440efc7a3b 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverMemory.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverMemory.scala @@ -6,13 +6,12 @@ import li.cil.oc.api import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack object DriverMemory extends Item with api.driver.item.Memory with api.driver.item.CallBudget { - override def amount(stack: ItemStack) = Delegator.subItem(stack) match { - case Some(memory: item.Memory) => + override def amount(stack: ItemStack) = stack.getItem match { + case memory: item.Memory => val sizes = Settings.get.ramSizes Settings.get.ramSizes(memory.tier max 0 min (sizes.length - 1)) case _ => 0.0 @@ -31,8 +30,8 @@ object DriverMemory extends Item with api.driver.item.Memory with api.driver.ite override def slot(stack: ItemStack) = Slot.Memory override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(memory: item.Memory) => memory.tier / 2 + stack.getItem match { + case memory: item.Memory => memory.tier / 2 case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala index 4e902344bb..558c4ded29 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverRedstoneCard.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.common.tileentity.traits.BundledRedstoneAware import li.cil.oc.common.tileentity.traits.RedstoneAware import li.cil.oc.integration.util.BundledRedstone @@ -43,8 +42,8 @@ object DriverRedstoneCard extends Item with HostAware { override def slot(stack: ItemStack) = Slot.Card override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(card: item.RedstoneCard) => card.tier + stack.getItem match { + case card: item.RedstoneCard => card.tier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala index 956648ea26..3727d7ebf1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeBattery.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -24,8 +23,8 @@ object DriverUpgradeBattery extends Item with HostAware { override def slot(stack: ItemStack) = Slot.Upgrade override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(battery: item.UpgradeBattery) => battery.tier + stack.getItem match { + case battery: item.UpgradeBattery => battery.tier case _ => Tier.One } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala index 53dfbe20cd..188f4f7e34 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeDatabase.scala @@ -7,7 +7,6 @@ import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.inventory.DatabaseInventory import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -29,8 +28,8 @@ object DriverUpgradeDatabase extends Item with api.driver.item.HostAware { override def slot(stack: ItemStack) = Slot.Upgrade override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(database: item.UpgradeDatabase) => database.tier + stack.getItem match { + case database: item.UpgradeDatabase => database.tier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeHover.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeHover.scala index 7efd881e65..15e344b77c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeHover.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeHover.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.driver.item.HostAware import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import net.minecraft.item.ItemStack object DriverUpgradeHover extends Item with HostAware { @@ -20,8 +19,8 @@ object DriverUpgradeHover extends Item with HostAware { override def slot(stack: ItemStack) = Slot.Upgrade override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(upgrade: item.UpgradeHover) => upgrade.tier + stack.getItem match { + case upgrade: item.UpgradeHover => upgrade.tier case _ => Tier.One } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala index b00f775a47..c7ae3313cd 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverWirelessNetworkCard.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.Slot import li.cil.oc.common.Tier -import li.cil.oc.common.item.Delegator import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -27,8 +26,8 @@ object DriverWirelessNetworkCard extends Item { override def slot(stack: ItemStack) = Slot.Card override def tier(stack: ItemStack) = - Delegator.subItem(stack) match { - case Some(card: common.item.WirelessNetworkCard) => card.tier + stack.getItem match { + case card: common.item.WirelessNetworkCard => card.tier case _ => Tier.One } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index fef966011c..5fea8fa64c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -24,7 +24,6 @@ import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.block.SimpleBlock import li.cil.oc.common.event._ import li.cil.oc.common.item.Analyzer -import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.RedstoneCard import li.cil.oc.common.item.Tablet import li.cil.oc.common.nanomachines.provider.DisintegrationProvider @@ -313,10 +312,7 @@ object ModOpenComputers extends ModProxy { // redstone card availability here, after all other mods were inited. if (BundledRedstone.isAvailable) { OpenComputers.log.info("Found extended redstone mods, enabling tier two redstone card.") - Delegator.subItem(api.Items.get(Constants.ItemName.RedstoneCardTier2).createItemStack(1)) match { - case Some(redstone: RedstoneCard) => redstone.showInItemList = true - case _ => - } + ModOpenComputers.hasRedstoneCardT2 = true } api.Manual.addProvider(DefinitionPathProvider) @@ -337,6 +333,8 @@ object ModOpenComputers extends ModProxy { api.Nanomachines.addProvider(MagnetProvider) } + protected[oc] var hasRedstoneCardT2 = false + def useWrench(player: PlayerEntity, pos: BlockPos, changeDurability: Boolean): Boolean = { player.getItemInHand(Hand.MAIN_HAND).getItem match { case wrench: Wrench => wrench.useWrenchOnBlock(player, player.level, pos, !changeDurability) diff --git a/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala b/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala index 6c42f04a4b..db1653bc24 100644 --- a/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala +++ b/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala @@ -1,6 +1,6 @@ package li.cil.oc.integration.util -import li.cil.oc.common.item.traits.Delegate +import li.cil.oc.common.item.traits.SimpleItem import net.minecraft.block.Block import net.minecraft.item.ItemStack @@ -15,7 +15,7 @@ object ItemBlacklist { def hide(block: Block): Unit = hiddenItems += (() => new ItemStack(block)) - def hide(item: Delegate): Unit = hiddenItems += (() => item.createItemStack()) + def hide(item: SimpleItem): Unit = hiddenItems += (() => item.createItemStack()) def apply(): Unit = { for (consumer <- consumers) { diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index ad8ae222fa..49e1fb0f49 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -11,7 +11,7 @@ import li.cil.oc.common.Achievement import li.cil.oc.common.PacketType import li.cil.oc.common.component.TextBuffer import li.cil.oc.common.entity.Drone -import li.cil.oc.common.item.{Delegator, Tablet, TabletWrapper} +import li.cil.oc.common.item.{Tablet, TabletWrapper} import li.cil.oc.common.item.data.DriveData import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.common.tileentity._ @@ -102,8 +102,8 @@ object PacketHandler extends CommonPacketHandler { def onDriveLock(p: PacketParser): Unit = p.player match { case player: ServerPlayerEntity => { val heldItem = player.getItemInHand(Hand.MAIN_HAND) - Delegator.subItem(heldItem) match { - case Some(drive: FileSystemLike) => DriveData.lock(heldItem, player) + heldItem.getItem match { + case drive: FileSystemLike => DriveData.lock(heldItem, player) case _ => // Invalid packet } } @@ -115,8 +115,8 @@ object PacketHandler extends CommonPacketHandler { p.player match { case player: ServerPlayerEntity => val heldItem = player.getItemInHand(Hand.MAIN_HAND) - Delegator.subItem(heldItem) match { - case Some(drive: FileSystemLike) => DriveData.setUnmanaged(heldItem, unmanaged) + heldItem.getItem match { + case drive: FileSystemLike => DriveData.setUnmanaged(heldItem, unmanaged) case _ => // Invalid packet. } case _ => // Invalid packet. diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index dc918e6951..4fc4b9fe35 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -26,7 +26,6 @@ import li.cil.oc.common.Tier import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.common.item -import li.cil.oc.common.item.Delegator import li.cil.oc.server.network.Connector import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ @@ -125,8 +124,8 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit // ----------------------------------------------------------------------- // // ServerInventory - override def tier: Int = Delegator.subItem(container) match { - case Some(server: item.Server) => server.tier + override def tier: Int = container.getItem match { + case server: item.Server => server.tier case _ => 0 } diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index 977e799837..358ca8e13e 100755 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -11,7 +11,6 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.fs.Label import li.cil.oc.api.network.EnvironmentHost -import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -115,8 +114,8 @@ object FileSystem extends api.detail.FileSystemAPI { } def removeAddress(fsStack: ItemStack): Boolean = { - Delegator.subItem(fsStack) match { - case Some(drive: FileSystemLike) => { + fsStack.getItem match { + case drive: FileSystemLike => { val data = li.cil.oc.integration.opencomputers.Item.dataTag(fsStack) if (data.contains("node")) { val nodeData = data.getCompound("node") From ac3d9b2ac6229baa6a351e8777a3158c44e3a105 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 31 Jul 2022 22:43:48 +0200 Subject: [PATCH 021/159] Upgrade block models and items --- .../blockstates/accesspoint.json | 2 +- .../opencomputers/blockstates/adapter.json | 2 +- .../opencomputers/blockstates/assembler.json | 2 +- .../opencomputers/blockstates/cable.json | 5 +++ .../opencomputers/blockstates/capacitor.json | 2 +- .../blockstates/carpetedcapacitor.json | 2 +- .../opencomputers/blockstates/case1.json | 16 +++++----- .../opencomputers/blockstates/case2.json | 16 +++++----- .../opencomputers/blockstates/case3.json | 16 +++++----- .../blockstates/casecreative.json | 16 +++++----- .../blockstates/chameliumblock.json | 32 +++++++++---------- .../opencomputers/blockstates/charger.json | 8 ++--- .../blockstates/disassembler.json | 2 +- .../opencomputers/blockstates/diskdrive.json | 8 ++--- .../opencomputers/blockstates/endstone.json | 2 +- .../opencomputers/blockstates/geolyzer.json | 2 +- .../opencomputers/blockstates/hologram1.json | 2 +- .../opencomputers/blockstates/hologram2.json | 2 +- .../opencomputers/blockstates/keyboard.json | 24 +++++++------- .../blockstates/microcontroller.json | 8 ++--- .../blockstates/motionsensor.json | 2 +- .../blockstates/netsplitter.json | 5 +++ .../blockstates/powerconverter.json | 2 +- .../blockstates/powerdistributor.json | 2 +- .../opencomputers/blockstates/print.json | 5 +++ .../opencomputers/blockstates/printer.json | 2 +- .../opencomputers/blockstates/rack.json | 8 ++--- .../opencomputers/blockstates/raid.json | 8 ++--- .../opencomputers/blockstates/redstone.json | 2 +- .../opencomputers/blockstates/relay.json | 2 +- .../opencomputers/blockstates/robot.json | 5 +++ .../blockstates/robotafterimage.json | 5 +++ .../opencomputers/blockstates/screen1.json | 24 +++++++------- .../opencomputers/blockstates/screen2.json | 24 +++++++------- .../opencomputers/blockstates/screen3.json | 24 +++++++------- .../opencomputers/blockstates/switch.json | 2 +- .../opencomputers/blockstates/transposer.json | 2 +- .../opencomputers/blockstates/waypoint.json | 24 +++++++------- .../opencomputers/models/item/cable.json | 3 ++ .../models/item/netsplitter.json | 3 ++ .../opencomputers/models/item/print.json | 3 ++ .../opencomputers/models/item/robot.json | 3 ++ .../models/item/robotafterimage.json | 3 ++ .../scala/li/cil/oc/common/init/Items.scala | 1 - 44 files changed, 186 insertions(+), 147 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/blockstates/cable.json create mode 100644 src/main/resources/assets/opencomputers/blockstates/netsplitter.json create mode 100644 src/main/resources/assets/opencomputers/blockstates/print.json create mode 100644 src/main/resources/assets/opencomputers/blockstates/robot.json create mode 100644 src/main/resources/assets/opencomputers/blockstates/robotafterimage.json create mode 100644 src/main/resources/assets/opencomputers/models/item/cable.json create mode 100644 src/main/resources/assets/opencomputers/models/item/netsplitter.json create mode 100644 src/main/resources/assets/opencomputers/models/item/print.json create mode 100644 src/main/resources/assets/opencomputers/models/item/robot.json create mode 100644 src/main/resources/assets/opencomputers/models/item/robotafterimage.json diff --git a/src/main/resources/assets/opencomputers/blockstates/accesspoint.json b/src/main/resources/assets/opencomputers/blockstates/accesspoint.json index 617e1a249b..90e6d47100 100644 --- a/src/main/resources/assets/opencomputers/blockstates/accesspoint.json +++ b/src/main/resources/assets/opencomputers/blockstates/accesspoint.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:accesspoint" } + "": { "model": "opencomputers:block/accesspoint" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/adapter.json b/src/main/resources/assets/opencomputers/blockstates/adapter.json index 55444855fe..1893a7394b 100644 --- a/src/main/resources/assets/opencomputers/blockstates/adapter.json +++ b/src/main/resources/assets/opencomputers/blockstates/adapter.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:adapter" } + "": { "model": "opencomputers:block/adapter" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/assembler.json b/src/main/resources/assets/opencomputers/blockstates/assembler.json index b89ed2b1fd..f9cf866fce 100644 --- a/src/main/resources/assets/opencomputers/blockstates/assembler.json +++ b/src/main/resources/assets/opencomputers/blockstates/assembler.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:assembler" } + "": { "model": "opencomputers:block/assembler" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/cable.json b/src/main/resources/assets/opencomputers/blockstates/cable.json new file mode 100644 index 0000000000..dbeef9edc2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/cable.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "minecraft:block/air" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/capacitor.json b/src/main/resources/assets/opencomputers/blockstates/capacitor.json index 7887a194f2..ab9a30aebd 100644 --- a/src/main/resources/assets/opencomputers/blockstates/capacitor.json +++ b/src/main/resources/assets/opencomputers/blockstates/capacitor.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:capacitor" } + "": { "model": "opencomputers:block/capacitor" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/carpetedcapacitor.json b/src/main/resources/assets/opencomputers/blockstates/carpetedcapacitor.json index f6c92cb33b..43bb89c09f 100644 --- a/src/main/resources/assets/opencomputers/blockstates/carpetedcapacitor.json +++ b/src/main/resources/assets/opencomputers/blockstates/carpetedcapacitor.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:carpetedcapacitor" } + "": { "model": "opencomputers:block/carpetedcapacitor" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/case1.json b/src/main/resources/assets/opencomputers/blockstates/case1.json index 2ed08635bd..22429bafcf 100644 --- a/src/main/resources/assets/opencomputers/blockstates/case1.json +++ b/src/main/resources/assets/opencomputers/blockstates/case1.json @@ -1,12 +1,12 @@ { "variants": { - "facing=north,running=false": { "model": "opencomputers:case" }, - "facing=south,running=false": { "model": "opencomputers:case", "y": 180 }, - "facing=west,running=false": { "model": "opencomputers:case", "y": 270 }, - "facing=east,running=false": { "model": "opencomputers:case", "y": 90 }, - "facing=north,running=true": { "model": "opencomputers:case_running" }, - "facing=south,running=true": { "model": "opencomputers:case_running", "y": 180 }, - "facing=west,running=true": { "model": "opencomputers:case_running", "y": 270 }, - "facing=east,running=true": { "model": "opencomputers:case_running", "y": 90 } + "facing=north,running=false": { "model": "opencomputers:block/case" }, + "facing=south,running=false": { "model": "opencomputers:block/case", "y": 180 }, + "facing=west,running=false": { "model": "opencomputers:block/case", "y": 270 }, + "facing=east,running=false": { "model": "opencomputers:block/case", "y": 90 }, + "facing=north,running=true": { "model": "opencomputers:block/case_running" }, + "facing=south,running=true": { "model": "opencomputers:block/case_running", "y": 180 }, + "facing=west,running=true": { "model": "opencomputers:block/case_running", "y": 270 }, + "facing=east,running=true": { "model": "opencomputers:block/case_running", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/case2.json b/src/main/resources/assets/opencomputers/blockstates/case2.json index 2ed08635bd..22429bafcf 100644 --- a/src/main/resources/assets/opencomputers/blockstates/case2.json +++ b/src/main/resources/assets/opencomputers/blockstates/case2.json @@ -1,12 +1,12 @@ { "variants": { - "facing=north,running=false": { "model": "opencomputers:case" }, - "facing=south,running=false": { "model": "opencomputers:case", "y": 180 }, - "facing=west,running=false": { "model": "opencomputers:case", "y": 270 }, - "facing=east,running=false": { "model": "opencomputers:case", "y": 90 }, - "facing=north,running=true": { "model": "opencomputers:case_running" }, - "facing=south,running=true": { "model": "opencomputers:case_running", "y": 180 }, - "facing=west,running=true": { "model": "opencomputers:case_running", "y": 270 }, - "facing=east,running=true": { "model": "opencomputers:case_running", "y": 90 } + "facing=north,running=false": { "model": "opencomputers:block/case" }, + "facing=south,running=false": { "model": "opencomputers:block/case", "y": 180 }, + "facing=west,running=false": { "model": "opencomputers:block/case", "y": 270 }, + "facing=east,running=false": { "model": "opencomputers:block/case", "y": 90 }, + "facing=north,running=true": { "model": "opencomputers:block/case_running" }, + "facing=south,running=true": { "model": "opencomputers:block/case_running", "y": 180 }, + "facing=west,running=true": { "model": "opencomputers:block/case_running", "y": 270 }, + "facing=east,running=true": { "model": "opencomputers:block/case_running", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/case3.json b/src/main/resources/assets/opencomputers/blockstates/case3.json index 2ed08635bd..22429bafcf 100644 --- a/src/main/resources/assets/opencomputers/blockstates/case3.json +++ b/src/main/resources/assets/opencomputers/blockstates/case3.json @@ -1,12 +1,12 @@ { "variants": { - "facing=north,running=false": { "model": "opencomputers:case" }, - "facing=south,running=false": { "model": "opencomputers:case", "y": 180 }, - "facing=west,running=false": { "model": "opencomputers:case", "y": 270 }, - "facing=east,running=false": { "model": "opencomputers:case", "y": 90 }, - "facing=north,running=true": { "model": "opencomputers:case_running" }, - "facing=south,running=true": { "model": "opencomputers:case_running", "y": 180 }, - "facing=west,running=true": { "model": "opencomputers:case_running", "y": 270 }, - "facing=east,running=true": { "model": "opencomputers:case_running", "y": 90 } + "facing=north,running=false": { "model": "opencomputers:block/case" }, + "facing=south,running=false": { "model": "opencomputers:block/case", "y": 180 }, + "facing=west,running=false": { "model": "opencomputers:block/case", "y": 270 }, + "facing=east,running=false": { "model": "opencomputers:block/case", "y": 90 }, + "facing=north,running=true": { "model": "opencomputers:block/case_running" }, + "facing=south,running=true": { "model": "opencomputers:block/case_running", "y": 180 }, + "facing=west,running=true": { "model": "opencomputers:block/case_running", "y": 270 }, + "facing=east,running=true": { "model": "opencomputers:block/case_running", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/casecreative.json b/src/main/resources/assets/opencomputers/blockstates/casecreative.json index 2ed08635bd..22429bafcf 100644 --- a/src/main/resources/assets/opencomputers/blockstates/casecreative.json +++ b/src/main/resources/assets/opencomputers/blockstates/casecreative.json @@ -1,12 +1,12 @@ { "variants": { - "facing=north,running=false": { "model": "opencomputers:case" }, - "facing=south,running=false": { "model": "opencomputers:case", "y": 180 }, - "facing=west,running=false": { "model": "opencomputers:case", "y": 270 }, - "facing=east,running=false": { "model": "opencomputers:case", "y": 90 }, - "facing=north,running=true": { "model": "opencomputers:case_running" }, - "facing=south,running=true": { "model": "opencomputers:case_running", "y": 180 }, - "facing=west,running=true": { "model": "opencomputers:case_running", "y": 270 }, - "facing=east,running=true": { "model": "opencomputers:case_running", "y": 90 } + "facing=north,running=false": { "model": "opencomputers:block/case" }, + "facing=south,running=false": { "model": "opencomputers:block/case", "y": 180 }, + "facing=west,running=false": { "model": "opencomputers:block/case", "y": 270 }, + "facing=east,running=false": { "model": "opencomputers:block/case", "y": 90 }, + "facing=north,running=true": { "model": "opencomputers:block/case_running" }, + "facing=south,running=true": { "model": "opencomputers:block/case_running", "y": 180 }, + "facing=west,running=true": { "model": "opencomputers:block/case_running", "y": 270 }, + "facing=east,running=true": { "model": "opencomputers:block/case_running", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/chameliumblock.json b/src/main/resources/assets/opencomputers/blockstates/chameliumblock.json index b1affded73..4f4f19a937 100644 --- a/src/main/resources/assets/opencomputers/blockstates/chameliumblock.json +++ b/src/main/resources/assets/opencomputers/blockstates/chameliumblock.json @@ -1,20 +1,20 @@ { "variants": { - "color=silver": { "model": "opencomputers:chameliumblock" }, - "color=magenta": { "model": "opencomputers:chameliumblock" }, - "color=light_blue": { "model": "opencomputers:chameliumblock" }, - "color=red": { "model": "opencomputers:chameliumblock" }, - "color=yellow": { "model": "opencomputers:chameliumblock" }, - "color=gray": { "model": "opencomputers:chameliumblock" }, - "color=brown": { "model": "opencomputers:chameliumblock" }, - "color=cyan": { "model": "opencomputers:chameliumblock" }, - "color=green": { "model": "opencomputers:chameliumblock" }, - "color=blue": { "model": "opencomputers:chameliumblock" }, - "color=purple": { "model": "opencomputers:chameliumblock" }, - "color=orange": { "model": "opencomputers:chameliumblock" }, - "color=pink": { "model": "opencomputers:chameliumblock" }, - "color=black": { "model": "opencomputers:chameliumblock" }, - "color=white": { "model": "opencomputers:chameliumblock" }, - "color=lime": { "model": "opencomputers:chameliumblock" } + "color=light_gray": { "model": "opencomputers:block/chameliumblock" }, + "color=magenta": { "model": "opencomputers:block/chameliumblock" }, + "color=light_blue": { "model": "opencomputers:block/chameliumblock" }, + "color=red": { "model": "opencomputers:block/chameliumblock" }, + "color=yellow": { "model": "opencomputers:block/chameliumblock" }, + "color=gray": { "model": "opencomputers:block/chameliumblock" }, + "color=brown": { "model": "opencomputers:block/chameliumblock" }, + "color=cyan": { "model": "opencomputers:block/chameliumblock" }, + "color=green": { "model": "opencomputers:block/chameliumblock" }, + "color=blue": { "model": "opencomputers:block/chameliumblock" }, + "color=purple": { "model": "opencomputers:block/chameliumblock" }, + "color=orange": { "model": "opencomputers:block/chameliumblock" }, + "color=pink": { "model": "opencomputers:block/chameliumblock" }, + "color=black": { "model": "opencomputers:block/chameliumblock" }, + "color=white": { "model": "opencomputers:block/chameliumblock" }, + "color=lime": { "model": "opencomputers:block/chameliumblock" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/charger.json b/src/main/resources/assets/opencomputers/blockstates/charger.json index 54cebc64e8..6d0afd9e90 100644 --- a/src/main/resources/assets/opencomputers/blockstates/charger.json +++ b/src/main/resources/assets/opencomputers/blockstates/charger.json @@ -1,8 +1,8 @@ { "variants": { - "facing=north": { "model": "opencomputers:charger" }, - "facing=south": { "model": "opencomputers:charger", "y": 180 }, - "facing=west": { "model": "opencomputers:charger", "y": 270 }, - "facing=east": { "model": "opencomputers:charger", "y": 90 } + "facing=north": { "model": "opencomputers:block/charger" }, + "facing=south": { "model": "opencomputers:block/charger", "y": 180 }, + "facing=west": { "model": "opencomputers:block/charger", "y": 270 }, + "facing=east": { "model": "opencomputers:block/charger", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/disassembler.json b/src/main/resources/assets/opencomputers/blockstates/disassembler.json index 8b7823ccfa..2ce043f06c 100644 --- a/src/main/resources/assets/opencomputers/blockstates/disassembler.json +++ b/src/main/resources/assets/opencomputers/blockstates/disassembler.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:disassembler" } + "": { "model": "opencomputers:block/disassembler" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/diskdrive.json b/src/main/resources/assets/opencomputers/blockstates/diskdrive.json index c157589956..68bc7d87df 100644 --- a/src/main/resources/assets/opencomputers/blockstates/diskdrive.json +++ b/src/main/resources/assets/opencomputers/blockstates/diskdrive.json @@ -1,8 +1,8 @@ { "variants": { - "facing=north": { "model": "opencomputers:diskdrive" }, - "facing=south": { "model": "opencomputers:diskdrive", "y": 180 }, - "facing=west": { "model": "opencomputers:diskdrive", "y": 270 }, - "facing=east": { "model": "opencomputers:diskdrive", "y": 90 } + "facing=north": { "model": "opencomputers:block/diskdrive" }, + "facing=south": { "model": "opencomputers:block/diskdrive", "y": 180 }, + "facing=west": { "model": "opencomputers:block/diskdrive", "y": 270 }, + "facing=east": { "model": "opencomputers:block/diskdrive", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/endstone.json b/src/main/resources/assets/opencomputers/blockstates/endstone.json index 128583c89f..f14a18da61 100644 --- a/src/main/resources/assets/opencomputers/blockstates/endstone.json +++ b/src/main/resources/assets/opencomputers/blockstates/endstone.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "minecraft:end_stone" } + "": { "model": "minecraft:block/end_stone" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/geolyzer.json b/src/main/resources/assets/opencomputers/blockstates/geolyzer.json index 006954e9d3..7ee1d77440 100644 --- a/src/main/resources/assets/opencomputers/blockstates/geolyzer.json +++ b/src/main/resources/assets/opencomputers/blockstates/geolyzer.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:geolyzer" } + "": { "model": "opencomputers:block/geolyzer" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/hologram1.json b/src/main/resources/assets/opencomputers/blockstates/hologram1.json index e35d1d4984..0d567612b4 100644 --- a/src/main/resources/assets/opencomputers/blockstates/hologram1.json +++ b/src/main/resources/assets/opencomputers/blockstates/hologram1.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:hologram1" } + "": { "model": "opencomputers:block/hologram1" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/hologram2.json b/src/main/resources/assets/opencomputers/blockstates/hologram2.json index f0f48e0c77..21638bd491 100644 --- a/src/main/resources/assets/opencomputers/blockstates/hologram2.json +++ b/src/main/resources/assets/opencomputers/blockstates/hologram2.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:hologram2" } + "": { "model": "opencomputers:block/hologram2" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/keyboard.json b/src/main/resources/assets/opencomputers/blockstates/keyboard.json index 242fa77a4b..1490131bb4 100644 --- a/src/main/resources/assets/opencomputers/blockstates/keyboard.json +++ b/src/main/resources/assets/opencomputers/blockstates/keyboard.json @@ -1,16 +1,16 @@ { "variants": { - "pitch=north,yaw=north": { "model": "opencomputers:keyboard" }, - "pitch=north,yaw=east": { "model": "opencomputers:keyboard", "y": 90 }, - "pitch=north,yaw=south": { "model": "opencomputers:keyboard", "y": 180 }, - "pitch=north,yaw=west": { "model": "opencomputers:keyboard", "y": 270 }, - "pitch=up,yaw=north": { "model": "opencomputers:keyboard", "x": -90, "y": 180 }, - "pitch=up,yaw=east": { "model": "opencomputers:keyboard", "x": -90, "y": 270 }, - "pitch=up,yaw=south": { "model": "opencomputers:keyboard", "x": -90 }, - "pitch=up,yaw=west": { "model": "opencomputers:keyboard", "x": -90, "y": 90 }, - "pitch=down,yaw=north": { "model": "opencomputers:keyboard", "x": 90, "y": 180 }, - "pitch=down,yaw=east": { "model": "opencomputers:keyboard", "x": 90, "y": 270 }, - "pitch=down,yaw=south": { "model": "opencomputers:keyboard", "x": 90 }, - "pitch=down,yaw=west": { "model": "opencomputers:keyboard", "x": 90, "y": 90 } + "pitch=north,yaw=north": { "model": "opencomputers:block/keyboard" }, + "pitch=north,yaw=east": { "model": "opencomputers:block/keyboard", "y": 90 }, + "pitch=north,yaw=south": { "model": "opencomputers:block/keyboard", "y": 180 }, + "pitch=north,yaw=west": { "model": "opencomputers:block/keyboard", "y": 270 }, + "pitch=up,yaw=north": { "model": "opencomputers:block/keyboard", "x": -90, "y": 180 }, + "pitch=up,yaw=east": { "model": "opencomputers:block/keyboard", "x": -90, "y": 270 }, + "pitch=up,yaw=south": { "model": "opencomputers:block/keyboard", "x": -90 }, + "pitch=up,yaw=west": { "model": "opencomputers:block/keyboard", "x": -90, "y": 90 }, + "pitch=down,yaw=north": { "model": "opencomputers:block/keyboard", "x": 90, "y": 180 }, + "pitch=down,yaw=east": { "model": "opencomputers:block/keyboard", "x": 90, "y": 270 }, + "pitch=down,yaw=south": { "model": "opencomputers:block/keyboard", "x": 90 }, + "pitch=down,yaw=west": { "model": "opencomputers:block/keyboard", "x": 90, "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/microcontroller.json b/src/main/resources/assets/opencomputers/blockstates/microcontroller.json index ae4a9aa8ea..c329e47fa9 100644 --- a/src/main/resources/assets/opencomputers/blockstates/microcontroller.json +++ b/src/main/resources/assets/opencomputers/blockstates/microcontroller.json @@ -1,8 +1,8 @@ { "variants": { - "facing=north": { "model": "opencomputers:microcontroller" }, - "facing=south": { "model": "opencomputers:microcontroller", "y": 180 }, - "facing=west": { "model": "opencomputers:microcontroller", "y": 270 }, - "facing=east": { "model": "opencomputers:microcontroller", "y": 90 } + "facing=north": { "model": "opencomputers:block/microcontroller" }, + "facing=south": { "model": "opencomputers:block/microcontroller", "y": 180 }, + "facing=west": { "model": "opencomputers:block/microcontroller", "y": 270 }, + "facing=east": { "model": "opencomputers:block/microcontroller", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/motionsensor.json b/src/main/resources/assets/opencomputers/blockstates/motionsensor.json index 6be6d0c07a..f1181d7ab7 100644 --- a/src/main/resources/assets/opencomputers/blockstates/motionsensor.json +++ b/src/main/resources/assets/opencomputers/blockstates/motionsensor.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:motionsensor" } + "": { "model": "opencomputers:block/motionsensor" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/netsplitter.json b/src/main/resources/assets/opencomputers/blockstates/netsplitter.json new file mode 100644 index 0000000000..dbeef9edc2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/netsplitter.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "minecraft:block/air" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/powerconverter.json b/src/main/resources/assets/opencomputers/blockstates/powerconverter.json index 3fc72045cf..47e646e637 100644 --- a/src/main/resources/assets/opencomputers/blockstates/powerconverter.json +++ b/src/main/resources/assets/opencomputers/blockstates/powerconverter.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:powerconverter" } + "": { "model": "opencomputers:block/powerconverter" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/powerdistributor.json b/src/main/resources/assets/opencomputers/blockstates/powerdistributor.json index 3ab0491654..2194435bd2 100644 --- a/src/main/resources/assets/opencomputers/blockstates/powerdistributor.json +++ b/src/main/resources/assets/opencomputers/blockstates/powerdistributor.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:powerdistributor" } + "": { "model": "opencomputers:block/powerdistributor" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/print.json b/src/main/resources/assets/opencomputers/blockstates/print.json new file mode 100644 index 0000000000..dbeef9edc2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/print.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "minecraft:block/air" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/printer.json b/src/main/resources/assets/opencomputers/blockstates/printer.json index 18e8d7c5b4..e87119e7bd 100644 --- a/src/main/resources/assets/opencomputers/blockstates/printer.json +++ b/src/main/resources/assets/opencomputers/blockstates/printer.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:printer" } + "": { "model": "opencomputers:block/printer" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/rack.json b/src/main/resources/assets/opencomputers/blockstates/rack.json index 98bad44b77..27c0034d94 100644 --- a/src/main/resources/assets/opencomputers/blockstates/rack.json +++ b/src/main/resources/assets/opencomputers/blockstates/rack.json @@ -1,8 +1,8 @@ { "variants": { - "facing=north": { "model": "opencomputers:rack" }, - "facing=south": { "model": "opencomputers:rack", "y": 180 }, - "facing=west": { "model": "opencomputers:rack", "y": 270 }, - "facing=east": { "model": "opencomputers:rack", "y": 90 } + "facing=north": { "model": "opencomputers:block/rack" }, + "facing=south": { "model": "opencomputers:block/rack", "y": 180 }, + "facing=west": { "model": "opencomputers:block/rack", "y": 270 }, + "facing=east": { "model": "opencomputers:block/rack", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/raid.json b/src/main/resources/assets/opencomputers/blockstates/raid.json index 84de291f81..d9c02591a8 100644 --- a/src/main/resources/assets/opencomputers/blockstates/raid.json +++ b/src/main/resources/assets/opencomputers/blockstates/raid.json @@ -1,8 +1,8 @@ { "variants": { - "facing=north": { "model": "opencomputers:raid" }, - "facing=south": { "model": "opencomputers:raid", "y": 180 }, - "facing=west": { "model": "opencomputers:raid", "y": 270 }, - "facing=east": { "model": "opencomputers:raid", "y": 90 } + "facing=north": { "model": "opencomputers:block/raid" }, + "facing=south": { "model": "opencomputers:block/raid", "y": 180 }, + "facing=west": { "model": "opencomputers:block/raid", "y": 270 }, + "facing=east": { "model": "opencomputers:block/raid", "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/redstone.json b/src/main/resources/assets/opencomputers/blockstates/redstone.json index bd41205b87..db1ee2bed2 100644 --- a/src/main/resources/assets/opencomputers/blockstates/redstone.json +++ b/src/main/resources/assets/opencomputers/blockstates/redstone.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:redstone" } + "": { "model": "opencomputers:block/redstone" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/relay.json b/src/main/resources/assets/opencomputers/blockstates/relay.json index bb04449098..2ec907449b 100644 --- a/src/main/resources/assets/opencomputers/blockstates/relay.json +++ b/src/main/resources/assets/opencomputers/blockstates/relay.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:relay" } + "": { "model": "opencomputers:block/relay" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/robot.json b/src/main/resources/assets/opencomputers/blockstates/robot.json new file mode 100644 index 0000000000..dbeef9edc2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/robot.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "minecraft:block/air" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/robotafterimage.json b/src/main/resources/assets/opencomputers/blockstates/robotafterimage.json new file mode 100644 index 0000000000..dbeef9edc2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/robotafterimage.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "minecraft:block/air" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/screen1.json b/src/main/resources/assets/opencomputers/blockstates/screen1.json index b54966d253..63967b93f2 100644 --- a/src/main/resources/assets/opencomputers/blockstates/screen1.json +++ b/src/main/resources/assets/opencomputers/blockstates/screen1.json @@ -1,16 +1,16 @@ { "variants": { - "pitch=north,yaw=north": { "model": "opencomputers:screen" }, - "pitch=north,yaw=south": { "model": "opencomputers:screen", "y": 180 }, - "pitch=north,yaw=east": { "model": "opencomputers:screen", "y": 270 }, - "pitch=north,yaw=west": { "model": "opencomputers:screen", "y": 90 }, - "pitch=up,yaw=north": { "model": "opencomputers:screen", "x": 90 }, - "pitch=up,yaw=south": { "model": "opencomputers:screen", "x": 90, "y": 180 }, - "pitch=up,yaw=east": { "model": "opencomputers:screen", "x": 90, "y": 270 }, - "pitch=up,yaw=west": { "model": "opencomputers:screen", "x": 90, "y": 90 }, - "pitch=down,yaw=north": { "model": "opencomputers:screen", "x": -90 }, - "pitch=down,yaw=south": { "model": "opencomputers:screen", "x": -90, "y": 180 }, - "pitch=down,yaw=east": { "model": "opencomputers:screen", "x": -90, "y": 270 }, - "pitch=down,yaw=west": { "model": "opencomputers:screen", "x": -90, "y": 90 } + "pitch=north,yaw=north": { "model": "opencomputers:block/screen" }, + "pitch=north,yaw=south": { "model": "opencomputers:block/screen", "y": 180 }, + "pitch=north,yaw=east": { "model": "opencomputers:block/screen", "y": 270 }, + "pitch=north,yaw=west": { "model": "opencomputers:block/screen", "y": 90 }, + "pitch=up,yaw=north": { "model": "opencomputers:block/screen", "x": 90 }, + "pitch=up,yaw=south": { "model": "opencomputers:block/screen", "x": 90, "y": 180 }, + "pitch=up,yaw=east": { "model": "opencomputers:block/screen", "x": 90, "y": 270 }, + "pitch=up,yaw=west": { "model": "opencomputers:block/screen", "x": 90, "y": 90 }, + "pitch=down,yaw=north": { "model": "opencomputers:block/screen", "x": -90 }, + "pitch=down,yaw=south": { "model": "opencomputers:block/screen", "x": -90, "y": 180 }, + "pitch=down,yaw=east": { "model": "opencomputers:block/screen", "x": -90, "y": 270 }, + "pitch=down,yaw=west": { "model": "opencomputers:block/screen", "x": -90, "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/screen2.json b/src/main/resources/assets/opencomputers/blockstates/screen2.json index b54966d253..63967b93f2 100644 --- a/src/main/resources/assets/opencomputers/blockstates/screen2.json +++ b/src/main/resources/assets/opencomputers/blockstates/screen2.json @@ -1,16 +1,16 @@ { "variants": { - "pitch=north,yaw=north": { "model": "opencomputers:screen" }, - "pitch=north,yaw=south": { "model": "opencomputers:screen", "y": 180 }, - "pitch=north,yaw=east": { "model": "opencomputers:screen", "y": 270 }, - "pitch=north,yaw=west": { "model": "opencomputers:screen", "y": 90 }, - "pitch=up,yaw=north": { "model": "opencomputers:screen", "x": 90 }, - "pitch=up,yaw=south": { "model": "opencomputers:screen", "x": 90, "y": 180 }, - "pitch=up,yaw=east": { "model": "opencomputers:screen", "x": 90, "y": 270 }, - "pitch=up,yaw=west": { "model": "opencomputers:screen", "x": 90, "y": 90 }, - "pitch=down,yaw=north": { "model": "opencomputers:screen", "x": -90 }, - "pitch=down,yaw=south": { "model": "opencomputers:screen", "x": -90, "y": 180 }, - "pitch=down,yaw=east": { "model": "opencomputers:screen", "x": -90, "y": 270 }, - "pitch=down,yaw=west": { "model": "opencomputers:screen", "x": -90, "y": 90 } + "pitch=north,yaw=north": { "model": "opencomputers:block/screen" }, + "pitch=north,yaw=south": { "model": "opencomputers:block/screen", "y": 180 }, + "pitch=north,yaw=east": { "model": "opencomputers:block/screen", "y": 270 }, + "pitch=north,yaw=west": { "model": "opencomputers:block/screen", "y": 90 }, + "pitch=up,yaw=north": { "model": "opencomputers:block/screen", "x": 90 }, + "pitch=up,yaw=south": { "model": "opencomputers:block/screen", "x": 90, "y": 180 }, + "pitch=up,yaw=east": { "model": "opencomputers:block/screen", "x": 90, "y": 270 }, + "pitch=up,yaw=west": { "model": "opencomputers:block/screen", "x": 90, "y": 90 }, + "pitch=down,yaw=north": { "model": "opencomputers:block/screen", "x": -90 }, + "pitch=down,yaw=south": { "model": "opencomputers:block/screen", "x": -90, "y": 180 }, + "pitch=down,yaw=east": { "model": "opencomputers:block/screen", "x": -90, "y": 270 }, + "pitch=down,yaw=west": { "model": "opencomputers:block/screen", "x": -90, "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/screen3.json b/src/main/resources/assets/opencomputers/blockstates/screen3.json index b54966d253..63967b93f2 100644 --- a/src/main/resources/assets/opencomputers/blockstates/screen3.json +++ b/src/main/resources/assets/opencomputers/blockstates/screen3.json @@ -1,16 +1,16 @@ { "variants": { - "pitch=north,yaw=north": { "model": "opencomputers:screen" }, - "pitch=north,yaw=south": { "model": "opencomputers:screen", "y": 180 }, - "pitch=north,yaw=east": { "model": "opencomputers:screen", "y": 270 }, - "pitch=north,yaw=west": { "model": "opencomputers:screen", "y": 90 }, - "pitch=up,yaw=north": { "model": "opencomputers:screen", "x": 90 }, - "pitch=up,yaw=south": { "model": "opencomputers:screen", "x": 90, "y": 180 }, - "pitch=up,yaw=east": { "model": "opencomputers:screen", "x": 90, "y": 270 }, - "pitch=up,yaw=west": { "model": "opencomputers:screen", "x": 90, "y": 90 }, - "pitch=down,yaw=north": { "model": "opencomputers:screen", "x": -90 }, - "pitch=down,yaw=south": { "model": "opencomputers:screen", "x": -90, "y": 180 }, - "pitch=down,yaw=east": { "model": "opencomputers:screen", "x": -90, "y": 270 }, - "pitch=down,yaw=west": { "model": "opencomputers:screen", "x": -90, "y": 90 } + "pitch=north,yaw=north": { "model": "opencomputers:block/screen" }, + "pitch=north,yaw=south": { "model": "opencomputers:block/screen", "y": 180 }, + "pitch=north,yaw=east": { "model": "opencomputers:block/screen", "y": 270 }, + "pitch=north,yaw=west": { "model": "opencomputers:block/screen", "y": 90 }, + "pitch=up,yaw=north": { "model": "opencomputers:block/screen", "x": 90 }, + "pitch=up,yaw=south": { "model": "opencomputers:block/screen", "x": 90, "y": 180 }, + "pitch=up,yaw=east": { "model": "opencomputers:block/screen", "x": 90, "y": 270 }, + "pitch=up,yaw=west": { "model": "opencomputers:block/screen", "x": 90, "y": 90 }, + "pitch=down,yaw=north": { "model": "opencomputers:block/screen", "x": -90 }, + "pitch=down,yaw=south": { "model": "opencomputers:block/screen", "x": -90, "y": 180 }, + "pitch=down,yaw=east": { "model": "opencomputers:block/screen", "x": -90, "y": 270 }, + "pitch=down,yaw=west": { "model": "opencomputers:block/screen", "x": -90, "y": 90 } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/switch.json b/src/main/resources/assets/opencomputers/blockstates/switch.json index 18c289b041..24f1db9d96 100644 --- a/src/main/resources/assets/opencomputers/blockstates/switch.json +++ b/src/main/resources/assets/opencomputers/blockstates/switch.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:switch" } + "": { "model": "opencomputers:block/switch" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/transposer.json b/src/main/resources/assets/opencomputers/blockstates/transposer.json index 32bd3dad6d..eb059ab372 100644 --- a/src/main/resources/assets/opencomputers/blockstates/transposer.json +++ b/src/main/resources/assets/opencomputers/blockstates/transposer.json @@ -1,5 +1,5 @@ { "variants": { - "normal": { "model": "opencomputers:transposer" } + "": { "model": "opencomputers:block/transposer" } } } diff --git a/src/main/resources/assets/opencomputers/blockstates/waypoint.json b/src/main/resources/assets/opencomputers/blockstates/waypoint.json index 2b4a7f93db..1c856cd5ad 100644 --- a/src/main/resources/assets/opencomputers/blockstates/waypoint.json +++ b/src/main/resources/assets/opencomputers/blockstates/waypoint.json @@ -1,16 +1,16 @@ { "variants": { - "pitch=north,yaw=north": { "model": "opencomputers:waypoint" }, - "pitch=north,yaw=east": { "model": "opencomputers:waypoint", "y": 90 }, - "pitch=north,yaw=south": { "model": "opencomputers:waypoint", "y": 180 }, - "pitch=north,yaw=west": { "model": "opencomputers:waypoint", "y": 270 }, - "pitch=up,yaw=north": { "model": "opencomputers:waypoint", "x": -90 }, - "pitch=up,yaw=east": { "model": "opencomputers:waypoint", "x": -90, "y": 90 }, - "pitch=up,yaw=south": { "model": "opencomputers:waypoint", "x": -90, "y": 180 }, - "pitch=up,yaw=west": { "model": "opencomputers:waypoint", "x": -90, "y": 270 }, - "pitch=down,yaw=north": { "model": "opencomputers:waypoint", "x": 90 }, - "pitch=down,yaw=east": { "model": "opencomputers:waypoint", "x": 90, "y": 90 }, - "pitch=down,yaw=south": { "model": "opencomputers:waypoint", "x": 90, "y": 180 }, - "pitch=down,yaw=west": { "model": "opencomputers:waypoint", "x": 90, "y": 270 } + "pitch=north,yaw=north": { "model": "opencomputers:block/waypoint" }, + "pitch=north,yaw=east": { "model": "opencomputers:block/waypoint", "y": 90 }, + "pitch=north,yaw=south": { "model": "opencomputers:block/waypoint", "y": 180 }, + "pitch=north,yaw=west": { "model": "opencomputers:block/waypoint", "y": 270 }, + "pitch=up,yaw=north": { "model": "opencomputers:block/waypoint", "x": -90 }, + "pitch=up,yaw=east": { "model": "opencomputers:block/waypoint", "x": -90, "y": 90 }, + "pitch=up,yaw=south": { "model": "opencomputers:block/waypoint", "x": -90, "y": 180 }, + "pitch=up,yaw=west": { "model": "opencomputers:block/waypoint", "x": -90, "y": 270 }, + "pitch=down,yaw=north": { "model": "opencomputers:block/waypoint", "x": 90 }, + "pitch=down,yaw=east": { "model": "opencomputers:block/waypoint", "x": 90, "y": 90 }, + "pitch=down,yaw=south": { "model": "opencomputers:block/waypoint", "x": 90, "y": 180 }, + "pitch=down,yaw=west": { "model": "opencomputers:block/waypoint", "x": 90, "y": 270 } } } diff --git a/src/main/resources/assets/opencomputers/models/item/cable.json b/src/main/resources/assets/opencomputers/models/item/cable.json new file mode 100644 index 0000000000..5485f33463 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/cable.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:block/air" +} diff --git a/src/main/resources/assets/opencomputers/models/item/netsplitter.json b/src/main/resources/assets/opencomputers/models/item/netsplitter.json new file mode 100644 index 0000000000..5485f33463 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/netsplitter.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:block/air" +} diff --git a/src/main/resources/assets/opencomputers/models/item/print.json b/src/main/resources/assets/opencomputers/models/item/print.json new file mode 100644 index 0000000000..5485f33463 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/print.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:block/air" +} diff --git a/src/main/resources/assets/opencomputers/models/item/robot.json b/src/main/resources/assets/opencomputers/models/item/robot.json new file mode 100644 index 0000000000..5485f33463 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/robot.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:block/air" +} diff --git a/src/main/resources/assets/opencomputers/models/item/robotafterimage.json b/src/main/resources/assets/opencomputers/models/item/robotafterimage.json new file mode 100644 index 0000000000..5485f33463 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/robotafterimage.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:block/air" +} diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 17f2208f65..8f2b68037b 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -58,7 +58,6 @@ object Items extends ItemAPI { simple.setUnlocalizedName("oc." + id) simple.setRegistryName(OpenComputers.ID, id) GameData.register_impl[Block](simple) - OpenComputers.proxy.registerModel(simple, id) val item : Item = new common.block.Item(simple) item.setRegistryName(OpenComputers.ID, id) From 999b2b8475a4126840dde4538d4f5e3399579242 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 1 Aug 2022 01:22:22 +0200 Subject: [PATCH 022/159] Fix CustomModel rendering for items --- .../renderer/block/ModelInitialization.scala | 42 +++++++++---------- .../scala/li/cil/oc/common/EventHandler.scala | 13 ++---- .../li/cil/oc/common/item/FloppyDisk.scala | 2 +- .../scala/li/cil/oc/common/item/Tablet.scala | 3 +- .../li/cil/oc/common/item/Terminal.scala | 3 +- 5 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index f442d929b5..a92fbbab52 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -3,6 +3,7 @@ package li.cil.oc.client.renderer.block import java.util.Random import li.cil.oc.Constants +import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.common.item.CustomModel @@ -18,13 +19,16 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.IItemProvider import net.minecraft.util.Direction +import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.{ModelBakeEvent, ModelRegistryEvent} -import net.minecraftforge.common.MinecraftForge import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable +@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object ModelInitialization { final val CableBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "normal") final val CableItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "inventory") @@ -42,8 +46,6 @@ object ModelInitialization { private val modelRemappings = mutable.Map.empty[ModelResourceLocation, ModelResourceLocation] def preInit(): Unit = { - MinecraftForge.EVENT_BUS.register(this) - registerModel(Constants.BlockName.Cable, CableBlockLocation, CableItemLocation) registerModel(Constants.BlockName.NetSplitter, NetSplitterBlockLocation, NetSplitterItemLocation) registerModel(Constants.BlockName.Print, PrintBlockLocation, PrintItemLocation) @@ -53,7 +55,20 @@ object ModelInitialization { @SubscribeEvent def onModelRegistration(event: ModelRegistryEvent): Unit = { - registerItems() + val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper + for (item <- meshableItems) { + item match { + case custom: CustomModel => custom.registerModelLocations() + case _ => { + Option(api.Items.get(new ItemStack(item))) match { + case Some(descriptor) => + val location = Settings.resourceDomain + ":" + descriptor.name() + shaper.register(item, new ModelResourceLocation(location, "inventory")) + case _ => + } + } + } + } } // ----------------------------------------------------------------------- // @@ -76,24 +91,6 @@ object ModelInitialization { } } - private def registerItems(): Unit = { - val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper - for (item <- meshableItems) { - item match { - case custom: CustomModel => custom.registerModelLocations() - case _ => { - Option(api.Items.get(new ItemStack(item))) match { - case Some(descriptor) => - val location = Settings.resourceDomain + ":" + descriptor.name() - shaper.register(item, new ModelResourceLocation(location, "inventory")) - case _ => - } - } - } - } - meshableItems.clear() - } - // ----------------------------------------------------------------------- // @SubscribeEvent @@ -148,6 +145,7 @@ object ModelInitialization { } case _ => } + meshableItems.clear() val modelOverrides = Map[String, IBakedModel => IBakedModel]( Constants.BlockName.ScreenTier1 -> (_ => ScreenModel), diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 6527092f81..db79fc442c 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -60,6 +60,7 @@ import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.common.ObfuscationReflectionHelper import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.ImplicitConversionsToScala._ @@ -438,18 +439,10 @@ object EventHandler { else false } - private lazy val getChunks = try { - val m = classOf[ChunkManager].getDeclaredMethod("getChunks") // getChunks - m.setAccessible(true) - m - } - catch { - case e: Throwable => - throw new Error("Could not access server chunk list", e) - } + private val getChunks = ObfuscationReflectionHelper.findMethod(classOf[ChunkManager], "getChunks") private def getChunks(world: ServerWorld): Iterable[ChunkHolder] = try { - getChunks.invoke(world.getChunkSource.chunkMap).asInstanceOf[Iterable[ChunkHolder]] + getChunks.invoke(world.getChunkSource.chunkMap).asInstanceOf[java.lang.Iterable[ChunkHolder]] } catch { case e: Throwable => diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index 31508d8ee3..6bcb939267 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -26,7 +26,7 @@ class FloppyDisk(props: Properties = new Properties().tab(CreativeTab)) extends @OnlyIn(Dist.CLIENT) private def modelLocationFromDyeName(name: String) = { - new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Floppy + "_" + name, "inventory") + new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Floppy + "_" + name.toLowerCase, "inventory") } @OnlyIn(Dist.CLIENT) diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index 392f423a76..fa708f64de 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -139,8 +139,7 @@ class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { for (state <- Seq(None, Some(true), Some(false))) { - val location = modelLocationFromState(state) - ModelLoader.addSpecialModel(new ResourceLocation(location.getNamespace + ":" + location.getPath)) + ModelLoader.addSpecialModel(modelLocationFromState(state)) } } diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 88321f8e25..1559fe6500 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -52,8 +52,7 @@ class Terminal(props: Properties = new Properties().tab(CreativeTab)) extends It @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { for (state <- Seq(true, false)) { - val location = modelLocationFromState(state) - ModelLoader.addSpecialModel(new ResourceLocation(location.getNamespace + ":" + location.getPath)) + ModelLoader.addSpecialModel(modelLocationFromState(state)) } } From 05ddd393bdc59ff2cfdfaeab1c54a415afc424e2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:37:32 +0200 Subject: [PATCH 023/159] Fix SideTracker's integrated server detection --- src/main/java/li/cil/oc/util/SideTracker.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/li/cil/oc/util/SideTracker.java b/src/main/java/li/cil/oc/util/SideTracker.java index 43bf69c0ab..238826eb2e 100644 --- a/src/main/java/li/cil/oc/util/SideTracker.java +++ b/src/main/java/li/cil/oc/util/SideTracker.java @@ -1,19 +1,14 @@ package li.cil.oc.util; import net.minecraftforge.forgespi.Environment; +import net.minecraftforge.fml.common.thread.EffectiveSide; import java.util.Collections; import java.util.Set; public final class SideTracker { - private static final Set serverThreads = Collections.newSetFromMap(new java.util.WeakHashMap()); - - public static void addServerThread() { - serverThreads.add(Thread.currentThread()); - } - public static boolean isServer() { - return Environment.get().getDist().isDedicatedServer() || serverThreads.contains(Thread.currentThread()); + return Environment.get().getDist().isDedicatedServer() || EffectiveSide.get().isServer(); } public static boolean isClient() { From bf1e601d5838d462d8569f7c9ac167e09a10b285 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 2 Aug 2022 22:03:40 +0200 Subject: [PATCH 024/159] Use SRG names for ObfuscationReflectionHelper --- .../WirelessNetworkDebugRenderer.scala | 1 - .../scala/li/cil/oc/common/EventHandler.scala | 2 +- .../li/cil/oc/common/PacketBuilder.scala | 2 +- .../scala/li/cil/oc/server/agent/Player.scala | 19 +++++++++++++------ .../PlayerInteractionManagerHelper.scala | 3 ++- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala index 4b411b080e..21a653c1d6 100644 --- a/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/WirelessNetworkDebugRenderer.scala @@ -10,7 +10,6 @@ import net.minecraft.util.math.vector.Matrix4f import net.minecraft.world.World import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.common.ObfuscationReflectionHelper import org.lwjgl.opengl.GL11 object WirelessNetworkDebugRenderer { diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index db79fc442c..6d53c855ce 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -439,7 +439,7 @@ object EventHandler { else false } - private val getChunks = ObfuscationReflectionHelper.findMethod(classOf[ChunkManager], "getChunks") + private val getChunks = ObfuscationReflectionHelper.findMethod(classOf[ChunkManager], "func_223491_f") private def getChunks(world: ServerWorld): Iterable[ChunkHolder] = try { getChunks.invoke(world.getChunkSource.chunkMap).asInstanceOf[java.lang.Iterable[ChunkHolder]] diff --git a/src/main/scala/li/cil/oc/common/PacketBuilder.scala b/src/main/scala/li/cil/oc/common/PacketBuilder.scala index d8fd02ccd8..ac524d9183 100644 --- a/src/main/scala/li/cil/oc/common/PacketBuilder.scala +++ b/src/main/scala/li/cil/oc/common/PacketBuilder.scala @@ -77,7 +77,7 @@ abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stre val server = ServerLifecycleHooks.getCurrentServer val manager = server.getPlayerList for (player <- manager.getPlayers if player.level == world) { - val playerRenderDistance = 16 // ObfuscationReflectionHelper.getPrivateValue(classOf[ServerPlayerEntity], player, "renderDistance").asInstanceOf[Integer] + val playerRenderDistance = 16 val playerSpecificRange = range.getOrElse((manager.getViewDistance min playerRenderDistance) * 16.0) if (player.distanceToSqr(x, y, z) < playerSpecificRange * playerSpecificRange) { sendToPlayer(player) diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index 51ef3c029d..3b86cc823e 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -62,6 +62,13 @@ import net.minecraftforge.items.wrapper._ import scala.collection.convert.ImplicitConversionsToScala._ object Player { + // These use unobfuscated names because they're added by forge (LazyOptional / capabilities). + private val playerMainHandler = ObfuscationReflectionHelper.findField(classOf[PlayerEntity], "playerMainHandler") + + private val playerEquipmentHandler = ObfuscationReflectionHelper.findField(classOf[PlayerEntity], "playerEquipmentHandler") + + private val playerJoinedHandler = ObfuscationReflectionHelper.findField(classOf[PlayerEntity], "playerJoinedHandler") + def profileFor(agent: internal.Agent): GameProfile = { val uuid = agent.ownerUUID val randomId = (agent.world.random.nextInt(0xFFFFFF) + 1).toString @@ -163,15 +170,15 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc this.containerMenu = this.inventoryMenu try { - ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + Player.playerMainHandler.set(this, LazyOptional.of(new NonNullSupplier[IItemHandler] { override def get = new PlayerMainInvWrapper(inventory) - }), "playerMainHandler") - ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + })) + Player.playerEquipmentHandler.set(this, LazyOptional.of(new NonNullSupplier[IItemHandler] { override def get = new CombinedInvWrapper(new PlayerArmorInvWrapper(inventory), new PlayerOffhandInvWrapper(inventory)) - }), "playerEquipmentHandler") - ObfuscationReflectionHelper.setPrivateValue(classOf[PlayerEntity], this, LazyOptional.of(new NonNullSupplier[IItemHandler] { + })) + Player.playerJoinedHandler.set(this, LazyOptional.of(new NonNullSupplier[IItemHandler] { override def get = new PlayerInvWrapper(inventory) - }), "playerJoinedHandler") + })) } catch { case _: Exception => } diff --git a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala index 4e096d5ddf..0137b32864 100644 --- a/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala +++ b/src/main/scala/li/cil/oc/server/agent/PlayerInteractionManagerHelper.scala @@ -15,10 +15,11 @@ import net.minecraftforge.eventbus.api.{EventPriority, SubscribeEvent} import scala.collection.convert.ImplicitConversionsToScala._ object PlayerInteractionManagerHelper { + private val isDestroyingBlock = ObfuscationReflectionHelper.findField(classOf[PlayerInteractionManager], "field_73088_d") private def isDestroyingBlock(player: Player): Boolean = { try { - ObfuscationReflectionHelper.getPrivateValue(classOf[PlayerInteractionManager], player.gameMode, "isDestroyingBlock").asInstanceOf[Boolean] + isDestroyingBlock.getBoolean(player.gameMode) } catch { case _: Exception => true } From 946df3a51d14e79e0dec40fa02ef2b910afee4b7 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 2 Aug 2022 22:10:59 +0200 Subject: [PATCH 025/159] Remove duplicate drone from creative menu --- src/main/scala/li/cil/oc/common/item/Drone.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 43f8ae8d09..ad1d782d87 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -18,8 +18,10 @@ import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.util.Direction +import net.minecraft.util.NonNullList import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraftforge.client.event.ModelBakeEvent @@ -53,6 +55,9 @@ class Drone(props: Properties = new Properties().tab(CreativeTab)) extends Item( Rarity.byTier(data.tier) } + // Must be assembled to be usable so we hide it in the item list. + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) {} + override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get if (!world.isClientSide) { From b5e889e9c43bb4d3eadaf49fd65b2d2e6b010e9c Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 4 Aug 2022 21:08:41 +0200 Subject: [PATCH 026/159] Move event listeners to mod bus where required --- src/main/scala/li/cil/oc/OpenComputers.scala | 11 +-- src/main/scala/li/cil/oc/client/Proxy.scala | 4 -- .../scala/li/cil/oc/client/Textures.scala | 26 ++++--- .../renderer/block/NetSplitterModel.scala | 68 +++++++++++-------- src/main/scala/li/cil/oc/common/Proxy.scala | 5 +- .../opencomputers/ModOpenComputers.scala | 4 -- src/main/scala/li/cil/oc/util/Audio.scala | 3 - 7 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 7996b95fdb..09d2da702c 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -22,7 +22,7 @@ import net.minecraftforge.fml.ModContainer import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus -import net.minecraftforge.fml.event.lifecycle._ +import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent import net.minecraftforge.fml.event.server._ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext import net.minecraftforge.fml.loading.FMLPaths @@ -81,6 +81,7 @@ class OpenComputers { OpenComputers.instance = Some(this) MinecraftForge.EVENT_BUS.register(OpenComputers.proxy) + FMLJavaModLoadingContext.get.getModEventBus.register(OpenComputers.proxy) Settings.load(FMLPaths.CONFIGDIR.get().resolve(Paths.get("opencomputers", "settings.conf")).toFile()) OpenComputers.proxy.preInit() @@ -94,14 +95,6 @@ class OpenComputers { Items.init() } - @SubscribeEvent - def commonInit(e: FMLCommonSetupEvent): Unit = { - OpenComputers.proxy.init(e) - } - @SubscribeEvent def imc(e: InterModProcessEvent): Unit = InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) - - @SubscribeEvent - def loadComplete(e: FMLLoadCompleteEvent): Unit = OpenComputers.proxy.postInit(e) } diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index bb30b92f57..6990765939 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -11,7 +11,6 @@ import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer import li.cil.oc.client.renderer.block.ModelInitialization -import li.cil.oc.client.renderer.block.NetSplitterModel import li.cil.oc.client.renderer.entity.DroneRenderer import li.cil.oc.client.renderer.tileentity._ import li.cil.oc.common @@ -43,9 +42,6 @@ private[oc] class Proxy extends CommonProxy { super.preInit() api.API.manual = client.Manual - - MinecraftForge.EVENT_BUS.register(Textures) - MinecraftForge.EVENT_BUS.register(NetSplitterModel) } override def init(e: FMLCommonSetupEvent) { diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index c84c971214..1d15e1d1db 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -10,11 +10,15 @@ import net.minecraft.inventory.container.PlayerContainer import net.minecraft.client.renderer.texture.SimpleTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.util.ResourceLocation +import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.mutable +@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object Textures { object Font extends TextureBundle { @@ -24,7 +28,7 @@ object Textures { override protected def basePath = "font/%s" override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = - Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(new ResourceLocation(loc.getNamespace, s"textures/${loc.getPath}.png"))) } object GUI extends TextureBundle { @@ -71,7 +75,7 @@ object Textures { override protected def basePath = "gui/%s" override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = - Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(new ResourceLocation(loc.getNamespace, s"textures/${loc.getPath}.png"))) } object Icons extends TextureBundle { @@ -85,7 +89,7 @@ object Textures { override protected def basePath = "icons/%s" override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = - Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(new ResourceLocation(loc.getNamespace, s"textures/${loc.getPath}.png"))) } object Model extends TextureBundle { @@ -99,7 +103,7 @@ object Textures { override protected def basePath = "model/%s" override protected def loader(e: TextureStitchEvent.Pre, loc: ResourceLocation) = - Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(loc)) + Minecraft.getInstance.textureManager.register(loc, new SimpleTexture(new ResourceLocation(loc.getNamespace, s"textures/${loc.getPath}.png"))) } object Item extends TextureBundle { @@ -124,14 +128,14 @@ object Textures { val ChargerSideOn = L("overlay/charger_side_on") val DisassemblerSideOn = L("overlay/disassembler_side_on") val DisassemblerTopOn = L("overlay/disassembler_top_on") - val DiskDriveFrontActivity = L("overlay/diskDrive_front_activity") + val DiskDriveFrontActivity = L("overlay/diskdrive_front_activity") val GeolyzerTopOn = L("overlay/geolyzer_top_on") val MicrocontrollerFrontLight = L("overlay/microcontroller_front_light") val MicrocontrollerFrontOn = L("overlay/microcontroller_front_on") val MicrocontrollerFrontError = L("overlay/microcontroller_front_error") - val NetSplitterOn = L("overlay/netSplitter_on") - val PowerDistributorSideOn = L("overlay/powerDistributor_side_on") - val PowerDistributorTopOn = L("overlay/powerDistributor_top_on") + val NetSplitterOn = L("overlay/netsplitter_on") + val PowerDistributorSideOn = L("overlay/powerdistributor_side_on") + val PowerDistributorTopOn = L("overlay/powerdistributor_top_on") val RackDiskDrive = L("rack_disk_drive") val RackDiskDriveActivity = L("overlay/rack_disk_drive_activity") val RackServer = L("rack_server") @@ -149,10 +153,10 @@ object Textures { val TransposerOn = L("overlay/transposer_on") val Cable = L("cable") - val CableCap = L("cableCap") + val CableCap = L("cablecap") val GenericTop = L("generic_top", load = false) - val NetSplitterSide = L("netSplitter_side") - val NetSplitterTop = L("netSplitter_top") + val NetSplitterSide = L("netsplitter_side") + val NetSplitterTop = L("netsplitter_top") val RackFront = L("rack_front", load = false) val RackSide = L("rack_side", load = false) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala index ca560f1ada..2eaef6559c 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala @@ -3,6 +3,7 @@ package li.cil.oc.client.renderer.block import java.util import java.util.Collections +import li.cil.oc.OpenComputers import li.cil.oc.client.Textures import li.cil.oc.common.block import li.cil.oc.common.item.data.PrintData @@ -12,17 +13,25 @@ import net.minecraft.client.world.ClientWorld import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.model.IBakedModel import net.minecraft.client.renderer.model.ItemOverrideList +import net.minecraft.client.renderer.texture.AtlasTexture +import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack +import net.minecraft.inventory.container.PlayerContainer import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Vector3d +import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable +@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object NetSplitterModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride @@ -38,33 +47,38 @@ object NetSplitterModel extends SmartBlockModelBase { case _ => super.getQuads(state, side, rand) } - protected def splitterTexture = Array( - Textures.getSprite(Textures.Block.NetSplitterTop), - Textures.getSprite(Textures.Block.NetSplitterTop), - Textures.getSprite(Textures.Block.NetSplitterSide), - Textures.getSprite(Textures.Block.NetSplitterSide), - Textures.getSprite(Textures.Block.NetSplitterSide), - Textures.getSprite(Textures.Block.NetSplitterSide) + private def getSprite(location: ResourceLocation, atlas: Option[AtlasTexture]): TextureAtlasSprite = atlas match { + case Some(atls) => atls.getSprite(location) + case None => Textures.getSprite(location) + } + + protected def splitterTexture(atlas: Option[AtlasTexture]) = Array( + getSprite(Textures.Block.NetSplitterTop, atlas), + getSprite(Textures.Block.NetSplitterTop, atlas), + getSprite(Textures.Block.NetSplitterSide, atlas), + getSprite(Textures.Block.NetSplitterSide, atlas), + getSprite(Textures.Block.NetSplitterSide, atlas), + getSprite(Textures.Block.NetSplitterSide, atlas) ) - protected def GenerateBaseModel() = { + protected def GenerateBaseModel(atlas: AtlasTexture) = { val faces = mutable.ArrayBuffer.empty[BakedQuad] // Bottom. - faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 5 / 16f), new Vector3d(5 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 5 / 16f), new Vector3d(16 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 0 / 16f), new Vector3d(11 / 16f, 5 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 11 / 16f), new Vector3d(11 / 16f, 5 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 5 / 16f), new Vector3d(5 / 16f, 5 / 16f, 11 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 5 / 16f), new Vector3d(16 / 16f, 5 / 16f, 11 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 0 / 16f), new Vector3d(11 / 16f, 5 / 16f, 5 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 0 / 16f, 11 / 16f), new Vector3d(11 / 16f, 5 / 16f, 16 / 16f)), splitterTexture(Some(atlas)), None) // Corners. - faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 0 / 16f), new Vector3d(5 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 0 / 16f), new Vector3d(16 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 11 / 16f), new Vector3d(5 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 11 / 16f), new Vector3d(16 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 0 / 16f), new Vector3d(5 / 16f, 16 / 16f, 5 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 0 / 16f), new Vector3d(16 / 16f, 16 / 16f, 5 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 0 / 16f, 11 / 16f), new Vector3d(5 / 16f, 16 / 16f, 16 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 0 / 16f, 11 / 16f), new Vector3d(16 / 16f, 16 / 16f, 16 / 16f)), splitterTexture(Some(atlas)), None) // Top. - faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 11 / 16f, 5 / 16f), new Vector3d(5 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 11 / 16f, 5 / 16f), new Vector3d(16 / 16f, 16 / 16f, 11 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 0 / 16f), new Vector3d(11 / 16f, 16 / 16f, 5 / 16f)), splitterTexture, None) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 11 / 16f), new Vector3d(11 / 16f, 16 / 16f, 16 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(0 / 16f, 11 / 16f, 5 / 16f), new Vector3d(5 / 16f, 16 / 16f, 11 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 11 / 16f, 5 / 16f), new Vector3d(16 / 16f, 16 / 16f, 11 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 0 / 16f), new Vector3d(11 / 16f, 16 / 16f, 5 / 16f)), splitterTexture(Some(atlas)), None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 11 / 16f), new Vector3d(11 / 16f, 16 / 16f, 16 / 16f)), splitterTexture(Some(atlas)), None) faces.toArray } @@ -73,27 +87,27 @@ object NetSplitterModel extends SmartBlockModelBase { @SubscribeEvent def onTextureStitch(e: TextureStitchEvent.Post): Unit = { - BaseModel = GenerateBaseModel() + if (e.getMap.location.equals(PlayerContainer.BLOCK_ATLAS)) BaseModel = GenerateBaseModel(e.getMap) } protected def addSideQuads(faces: mutable.ArrayBuffer[BakedQuad], openSides: Array[Boolean]): Unit = { val down = openSides(Direction.DOWN.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, if (down) 0 / 16f else 2 / 16f, 5 / 16f), new Vector3d(11 / 16f, 5 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, if (down) 0 / 16f else 2 / 16f, 5 / 16f), new Vector3d(11 / 16f, 5 / 16f, 11 / 16f)), splitterTexture(None), None) val up = openSides(Direction.UP.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 5 / 16f), new Vector3d(11 / 16f, if (up) 16 / 16f else 14f / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 11 / 16f, 5 / 16f), new Vector3d(11 / 16f, if (up) 16 / 16f else 14f / 16f, 11 / 16f)), splitterTexture(None), None) val north = openSides(Direction.NORTH.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, if (north) 0 / 16f else 2 / 16f), new Vector3d(11 / 16f, 11 / 16f, 5 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, if (north) 0 / 16f else 2 / 16f), new Vector3d(11 / 16f, 11 / 16f, 5 / 16f)), splitterTexture(None), None) val south = openSides(Direction.SOUTH.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, 11 / 16f), new Vector3d(11 / 16f, 11 / 16f, if (south) 16 / 16f else 14 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(5 / 16f, 5 / 16f, 11 / 16f), new Vector3d(11 / 16f, 11 / 16f, if (south) 16 / 16f else 14 / 16f)), splitterTexture(None), None) val west = openSides(Direction.WEST.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(if (west) 0 / 16f else 2 / 16f, 5 / 16f, 5 / 16f), new Vector3d(5 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(if (west) 0 / 16f else 2 / 16f, 5 / 16f, 5 / 16f), new Vector3d(5 / 16f, 11 / 16f, 11 / 16f)), splitterTexture(None), None) val east = openSides(Direction.EAST.ordinal()) - faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 5 / 16f, 5 / 16f), new Vector3d(if (east) 16 / 16f else 14 / 16f, 11 / 16f, 11 / 16f)), splitterTexture, None) + faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 5 / 16f, 5 / 16f), new Vector3d(if (east) 16 / 16f else 14 / 16f, 11 / 16f, 11 / 16f)), splitterTexture(None), None) } class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 74e06464c4..44b0841bea 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -36,7 +36,8 @@ import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.event.RegistryEvent.MissingMappings import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.event.lifecycle._ +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent +import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent import net.minecraftforge.fml.network.NetworkEvent import net.minecraftforge.fml.network.NetworkRegistry import net.minecraftforge.registries.ForgeRegistries @@ -83,6 +84,7 @@ class Proxy { else api.Machine.architectures.head } + @SubscribeEvent def init(e: FMLCommonSetupEvent) { OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), new Supplier[String] { override def get = "" @@ -118,6 +120,7 @@ class Proxy { api.API.isPowerEnabled = !Settings.get.ignorePower } + @SubscribeEvent def postInit(e: FMLLoadCompleteEvent) { // Don't allow driver registration after this point, to avoid issues. driver.Registry.locked = true diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index 5fea8fa64c..935b237525 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -97,20 +97,16 @@ object ModOpenComputers extends ModProxy { MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Common) MinecraftForge.EVENT_BUS.register(SimpleComponentTickHandler.Instance) MinecraftForge.EVENT_BUS.register(Tablet) - MinecraftForge.EVENT_BUS.register(Analyzer) MinecraftForge.EVENT_BUS.register(AngelUpgradeHandler) MinecraftForge.EVENT_BUS.register(ChunkloaderUpgradeHandler) - MinecraftForge.EVENT_BUS.register(EventHandler) MinecraftForge.EVENT_BUS.register(ExperienceUpgradeHandler) MinecraftForge.EVENT_BUS.register(FileSystemAccessHandler) MinecraftForge.EVENT_BUS.register(HoverBootsHandler) MinecraftForge.EVENT_BUS.register(Loot) - MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Common) MinecraftForge.EVENT_BUS.register(NetworkActivityHandler) MinecraftForge.EVENT_BUS.register(RobotCommonHandler) MinecraftForge.EVENT_BUS.register(SaveHandler) - MinecraftForge.EVENT_BUS.register(Tablet) MinecraftForge.EVENT_BUS.register(Waypoints) MinecraftForge.EVENT_BUS.register(WirelessNetwork) MinecraftForge.EVENT_BUS.register(WirelessNetworkCardHandler) diff --git a/src/main/scala/li/cil/oc/util/Audio.scala b/src/main/scala/li/cil/oc/util/Audio.scala index c6cc5fc585..1281a520a2 100644 --- a/src/main/scala/li/cil/oc/util/Audio.scala +++ b/src/main/scala/li/cil/oc/util/Audio.scala @@ -12,7 +12,6 @@ import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvent import net.minecraft.util.math.BlockPos import net.minecraft.util.math.vector.Vector3d -import net.minecraftforge.common.MinecraftForge import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.event.TickEvent.ClientTickEvent import org.lwjgl.BufferUtils @@ -183,8 +182,6 @@ object Audio { } } - MinecraftForge.EVENT_BUS.register(this) - @SubscribeEvent def onTick(e: ClientTickEvent) { update() From e317f28f49a845ee5d02ad39aab6bedac905d11b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 4 Aug 2022 21:40:18 +0200 Subject: [PATCH 027/159] Fix thread pools not getting created --- src/main/scala/li/cil/oc/OpenComputers.scala | 15 --------------- .../scala/li/cil/oc/util/ThreadPoolFactory.scala | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 09d2da702c..1c6f77a55c 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -6,7 +6,6 @@ import li.cil.oc.common.IMC import li.cil.oc.common.Proxy import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items -import li.cil.oc.util.ThreadPoolFactory import net.minecraft.block.Block import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item @@ -14,16 +13,13 @@ import net.minecraft.world.World import net.minecraftforge.event.RegistryEvent import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.event.RegisterCommandsEvent import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.forgespi.Environment import net.minecraftforge.fml.InterModComms import net.minecraftforge.fml.ModContainer import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent -import net.minecraftforge.fml.event.server._ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext import net.minecraftforge.fml.loading.FMLPaths import net.minecraftforge.fml.network.simple.SimpleChannel @@ -32,7 +28,6 @@ import org.apache.logging.log4j.Logger import scala.collection.convert.ImplicitConversionsToScala._ -@Mod.EventBusSubscriber(modid = OpenComputers.ID, bus = Bus.FORGE) object OpenComputers { final val ID = "opencomputers" @@ -59,16 +54,6 @@ object OpenComputers { @Deprecated def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = proxy.openGui(player, guiId, world, x, y, z) - - @SubscribeEvent - def serverStart(e: FMLServerStartingEvent): Unit = { - ThreadPoolFactory.safePools.foreach(_.newThreadPool()) - } - - @SubscribeEvent - def serverStop(e: FMLServerStoppedEvent): Unit = { - ThreadPoolFactory.safePools.foreach(_.waitForCompletion()) - } } @Mod(OpenComputers.ID) diff --git a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala index 9c22b02b1c..24497d69ae 100644 --- a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala +++ b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala @@ -9,9 +9,15 @@ import java.util.concurrent.atomic.AtomicInteger import li.cil.oc.OpenComputers import li.cil.oc.Settings +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus +import net.minecraftforge.fml.event.server.FMLServerStartingEvent +import net.minecraftforge.fml.event.server.FMLServerStoppedEvent import scala.collection.mutable +@Mod.EventBusSubscriber(modid = OpenComputers.ID, bus = Bus.FORGE) object ThreadPoolFactory { val priority = { val custom = Settings.get.threadPriority @@ -19,6 +25,16 @@ object ThreadPoolFactory { else custom max Thread.MIN_PRIORITY min Thread.MAX_PRIORITY } + @SubscribeEvent + def serverStart(e: FMLServerStartingEvent): Unit = { + ThreadPoolFactory.safePools.foreach(_.newThreadPool()) + } + + @SubscribeEvent + def serverStop(e: FMLServerStoppedEvent): Unit = { + ThreadPoolFactory.safePools.foreach(_.waitForCompletion()) + } + def create(name: String, threads: Int) = Executors.newScheduledThreadPool(threads, new ThreadFactory() { private val baseName = "OpenComputers-" + name + "-" From 7233236b5a9b28d90b2a5b21563acd98b2ce5211 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 5 Aug 2022 20:20:30 +0200 Subject: [PATCH 028/159] Fix custom block item rendering --- .../cil/oc/client/renderer/block/NetSplitterModel.scala | 8 ++------ .../oc/client/renderer/block/SmartBlockModelBase.scala | 4 ++-- .../scala/li/cil/oc/common/block/ChameliumBlock.scala | 8 ++++++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala index 2eaef6559c..36ca8059cc 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala @@ -110,14 +110,10 @@ object NetSplitterModel extends SmartBlockModelBase { faces ++= bakeQuads(makeBox(new Vector3d(11 / 16f, 5 / 16f, 5 / 16f), new Vector3d(if (east) 16 / 16f else 14 / 16f, 11 / 16f, 11 / 16f)), splitterTexture(None), None) } - class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { - val data = new PrintData(stack) - + object ItemModel extends SmartBlockModelBase { override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] - Textures.Block.bind() - faces ++= BaseModel addSideQuads(faces, Direction.values().map(_ => false)) @@ -126,7 +122,7 @@ object NetSplitterModel extends SmartBlockModelBase { } object ItemOverride extends ItemOverrideList { - override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = new ItemModel(stack) + override def resolve(originalModel: IBakedModel, stack: ItemStack, world: ClientWorld, entity: LivingEntity): IBakedModel = ItemModel } } diff --git a/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala b/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala index 0cc99a04c9..6d65bfdd55 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/SmartBlockModelBase.scala @@ -24,7 +24,7 @@ trait SmartBlockModelBase extends IBakedModel { override def usesBlockLight = true - override def isCustomRenderer = true + override def isCustomRenderer = false // Note: we don't care about the actual texture here, we just need the block // texture atlas. So any of our textures we know is loaded into it will do. @@ -190,7 +190,7 @@ trait SmartBlockModelBase extends IBakedModel { getFaceShadeColor(face, colorRGB), java.lang.Float.floatToRawIntBits(u), java.lang.Float.floatToRawIntBits(v), - vx | (vy << 0x08) | (vz << 0x10) + 0, vx | (vy << 0x08) | (vz << 0x10) ) } diff --git a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala index a564d38a72..c5dc8ecb93 100644 --- a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala @@ -9,8 +9,10 @@ import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.material.Material import net.minecraft.state.EnumProperty import net.minecraft.item.DyeColor +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.loot.LootContext +import net.minecraft.util.NonNullList import net.minecraft.state.StateContainer object ChameliumBlock { @@ -29,4 +31,10 @@ class ChameliumBlock(props: Properties = Properties.of(Material.STONE).strength( stack.setDamageValue(state.getValue(ChameliumBlock.Color).getId) Collections.singletonList(stack) } + + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) { + val stack = new ItemStack(this, 1) + stack.setDamageValue(defaultBlockState.getValue(ChameliumBlock.Color).getId) + list.add(stack) + } } From aa5d9ae619f85cabbdc5517694296a68cf3d9c63 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 02:18:41 +0200 Subject: [PATCH 029/159] Fix block models not rendering --- .../client/renderer/block/ModelInitialization.scala | 12 ++++++------ .../scala/li/cil/oc/common/block/SimpleBlock.scala | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index a92fbbab52..2f05bd9b38 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -30,17 +30,17 @@ import scala.collection.mutable @Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object ModelInitialization { - final val CableBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "normal") + final val CableBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "") final val CableItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "inventory") - final val NetSplitterBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.NetSplitter, "normal") + final val NetSplitterBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.NetSplitter, "") final val NetSplitterItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.NetSplitter, "inventory") - final val PrintBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Print, "normal") + final val PrintBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Print, "") final val PrintItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Print, "inventory") - final val RobotBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Robot, "normal") + final val RobotBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Robot, "") final val RobotItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Robot, "inventory") - final val RobotAfterimageBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.RobotAfterimage, "normal") + final val RobotAfterimageBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.RobotAfterimage, "") final val RobotAfterimageItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.RobotAfterimage, "inventory") - final val RackBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Rack, "normal") + final val RackBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Rack, "") private val meshableItems = mutable.ArrayBuffer.empty[Item] private val modelRemappings = mutable.Map.empty[ModelResourceLocation, ModelResourceLocation] diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 68e8c6d69f..764ddbabdc 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -13,6 +13,7 @@ import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.IExtendedPositionPredicate import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState +import net.minecraft.block.BlockRenderType import net.minecraft.block.ContainerBlock import net.minecraft.block.material.Material import net.minecraft.client.util.ITooltipFlag @@ -47,7 +48,7 @@ import scala.collection.convert.ImplicitConversionsToScala._ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).strength(2, 5)) extends ContainerBlock(props.isValidSpawn(new IExtendedPositionPredicate[EntityType[_]] { override def test(state: BlockState, world: IBlockReader, pos: BlockPos, entity: EntityType[_]) = state.getBlock.asInstanceOf[SimpleBlock].isValidSpawn(state, world, pos, entity) -})) { +}).noOcclusion) { @Deprecated private var creativeTab: ItemGroup = CreativeTab @@ -72,6 +73,8 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str override def newBlockEntity(world: IBlockReader): TileEntity = null + override def getRenderShape(state: BlockState): BlockRenderType = BlockRenderType.MODEL + // ----------------------------------------------------------------------- // // BlockItem // ----------------------------------------------------------------------- // From 7598f1c6b663f0bda3f2babc2707067d509de70e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 02:26:15 +0200 Subject: [PATCH 030/159] Fix rendering crash due to pose stack growth --- .../cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala index e4148fbf29..ccb3b65efc 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala @@ -80,7 +80,7 @@ class DiskDriveRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti RenderState.enableEntityLighting() } - matrix.pushPose() + matrix.popPose() RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") From 954174363741774dccfc2f44973d0885bdfcbaa6 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 02:44:19 +0200 Subject: [PATCH 031/159] Fix robot rendering crash due bad vertex format --- .../client/renderer/tileentity/RobotRenderer.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index 3a19666baf..b63154ee3f 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -77,10 +77,10 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe // https://github.com/MinecraftForge/MinecraftForge/issues/2321 val NORMAL_3F = new VertexFormatElement(0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.NORMAL, 3) - val POSITION_TEX_NORMALF = new VertexFormat(ImmutableList.builder() + val POSITION_TEX_NORMAL = new VertexFormat(ImmutableList.builder() .add(DefaultVertexFormats.ELEMENT_POSITION) .add(DefaultVertexFormats.ELEMENT_UV0) - .add(NORMAL_3F) + .add(DefaultVertexFormats.ELEMENT_NORMAL) .build()) private implicit def extendWorldRenderer(self: IVertexBuilder): ExtendedWorldRenderer = new ExtendedWorldRenderer(self) @@ -96,7 +96,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val t = Tessellator.getInstance val r = t.getBuilder - r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMALF) + r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMAL) r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() @@ -107,7 +107,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe t.end() - r.begin(GL11.GL_QUADS, POSITION_TEX_NORMALF) + r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) r.vertex(stack.last.pose, l, gt, h).uv(0, 1).normal(stack.last.normal, 0, -1, 0).endVertex() r.vertex(stack.last.pose, l, gt, l).uv(0, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() @@ -121,7 +121,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val t = Tessellator.getInstance val r = t.getBuilder - r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMALF) + r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMAL) r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() @@ -132,7 +132,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe t.end() - r.begin(GL11.GL_QUADS, POSITION_TEX_NORMALF) + r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() r.vertex(stack.last.pose, l, gb, h).uv(0, 1).normal(stack.last.normal, 0, 1, 0).endVertex() From 728eba049bd4223cccb747220a86c50c73680e01 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 15:35:56 +0200 Subject: [PATCH 032/159] Fix S->C packet error due to missing sender --- src/main/scala/li/cil/oc/client/PacketHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index c234b64c85..327f0e1c9b 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -868,5 +868,5 @@ object PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } - protected override def createParser(stream: InputStream, player: PlayerEntity) = new PacketParser(stream, player) + protected override def createParser(stream: InputStream, player: PlayerEntity) = new PacketParser(stream, Minecraft.getInstance.player) } From 03215b47ffdf5ce316ad02b2c223bc9a014c5c40 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 18:45:17 +0200 Subject: [PATCH 033/159] Fix crash when creating file system from class --- .../li/cil/oc/server/fs/FileSystem.scala | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index 358ca8e13e..6222c65f25 100755 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -61,12 +61,21 @@ object FileSystem extends api.detail.FileSystemAPI { override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = { val innerPath = ("/assets/" + domain + "/" + (root.trim + "/")).replace("//", "/") - val codeSource = clazz.getProtectionDomain.getCodeSource.getLocation.getPath + val codeSource = clazz.getProtectionDomain.getCodeSource + val codePath = if (codeSource != null) codeSource.getLocation.getPath else { + val name = clazz.getName.replace('.', '/').concat(".class") + val resource = clazz.getClassLoader.getResource(name) + if (resource == null) throw new IllegalArgumentException(s"Could not locate ${clazz}") + resource.getPath + } val (codeUrl, isArchive) = - if (codeSource.contains(".zip!") || codeSource.contains(".jar!")) - (codeSource.substring(0, codeSource.lastIndexOf('!')), true) - else - (codeSource, false) + if (codePath.contains(".zip!") || codePath.contains(".jar!")) + (codePath.substring(0, codePath.lastIndexOf('!')), true) + else { + val name = clazz.getName.replace('.', '/').concat(".class") + if (!codePath.endsWith(name)) throw new IllegalArgumentException(s"Mismatched ${clazz} to '${codePath}'") + (codePath, false) + } val url = Try { new URL(codeUrl) @@ -77,7 +86,7 @@ object FileSystem extends api.detail.FileSystemAPI { } val file = url.map(url => new io.File(url.toURI)).recoverWith { case _: URISyntaxException => url.map(url => new io.File(url.getPath)) - }.getOrElse(new io.File(codeSource)) + }.getOrElse(new io.File(codePath)) if (isArchive) { ZipFileInputStreamFileSystem.fromFile(file, innerPath.substring(1)) From 0f2077d83bd9c5d9c09f80ff2c3a97cc2a41922d Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 6 Aug 2022 18:59:16 +0200 Subject: [PATCH 034/159] Fix crash when calling protected Entity.setRot --- src/main/scala/li/cil/oc/server/agent/Player.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index 3b86cc823e..e8321089af 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -100,7 +100,8 @@ object Player { val yaw = Math.toDegrees(-Math.atan2(direction.x, direction.z)).toFloat val pitch = Math.toDegrees(-Math.atan2(direction.y, Math.sqrt((direction.x * direction.x) + (direction.z * direction.z)))).toFloat * 0.99f player.setPos(player.agent.xPosition, player.agent.yPosition, player.agent.zPosition) - player.setRot(yaw, pitch) + player.xRot = pitch % 360f + player.yRot = yaw % 360f player.xRotO = player.xRot player.yRotO = player.yRot } From 542b4ebb2542e230d30c5d07c2752179a5d4c93c Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 7 Aug 2022 00:35:46 +0200 Subject: [PATCH 035/159] Fix robot rendering always on top --- .../cil/oc/client/renderer/PetRenderer.scala | 2 +- .../cil/oc/client/renderer/RenderTypes.java | 42 ++++ .../renderer/tileentity/RobotRenderer.scala | 233 +++++++----------- 3 files changed, 128 insertions(+), 149 deletions(-) create mode 100644 src/main/scala/li/cil/oc/client/renderer/RenderTypes.java diff --git a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala index 6fe801a532..971c98d8b5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala @@ -87,7 +87,7 @@ object PetRenderer { stack.scale(0.3f, 0.3f, 0.3f) stack.translate(0, hover, 0) - RobotRenderer.renderChassis(stack, offset, isRunningOverride = true) + RobotRenderer.renderChassis(stack, e.getBuffers, e.getLight, offset, isRunningOverride = true) RenderSystem.disableRescaleNormal() diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java new file mode 100644 index 0000000000..3f34f9cb83 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -0,0 +1,42 @@ +package li.cil.oc.client.renderer; + +import com.google.common.collect.ImmutableList; +import li.cil.oc.OpenComputers; +import li.cil.oc.client.Textures; +import net.minecraft.client.renderer.RenderState.TextureState; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderType.State; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import org.lwjgl.opengl.GL11; + +public class RenderTypes extends RenderType { + public static final VertexFormat POSITION_TEX_UV2_NORMAL = new VertexFormat(new ImmutableList.Builder() + .add(DefaultVertexFormats.ELEMENT_POSITION) + .add(DefaultVertexFormats.ELEMENT_UV0) + .add(DefaultVertexFormats.ELEMENT_UV2) + .add(DefaultVertexFormats.ELEMENT_NORMAL) + .add(DefaultVertexFormats.ELEMENT_PADDING) + .build()); + + public static final TextureState ROBOT_CHASSIS_TEXTURE = new TextureState(Textures.Model$.MODULE$.Robot(), false, false); + + public static final RenderType ROBOT_CHASSIS = create(OpenComputers.ID() + ":robot_chassis", + POSITION_TEX_UV2_NORMAL, GL11.GL_TRIANGLES, 1024, State.builder() + .setTextureState(ROBOT_CHASSIS_TEXTURE) + .setDiffuseLightingState(DIFFUSE_LIGHTING) + .setLightmapState(LIGHTMAP) + .createCompositeState(true)); + + public static final RenderType ROBOT_LIGHT = create(OpenComputers.ID() + ":robot_light", + DefaultVertexFormats.POSITION_COLOR_TEX, GL11.GL_QUADS, 256, State.builder() + .setTextureState(ROBOT_CHASSIS_TEXTURE) + .setTransparencyState(LIGHTNING_TRANSPARENCY) + .createCompositeState(true)); + + private RenderTypes() { + super(null, null, 0, 0, false, false, null, null); + throw new Error(); + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index b63154ee3f..a556728895 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -3,16 +3,14 @@ package li.cil.oc.client.renderer.tileentity import java.util.function.Function import com.google.common.base.Strings -import com.google.common.collect.ImmutableList import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api.driver.item.UpgradeRenderer import li.cil.oc.api.driver.item.UpgradeRenderer.MountPointName import li.cil.oc.api.event.RobotRenderEvent -import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.EventHandler import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState @@ -24,8 +22,6 @@ import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.client.renderer.vertex.VertexFormat -import net.minecraft.client.renderer.vertex.VertexFormatElement import net.minecraft.item.Items import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack @@ -36,7 +32,6 @@ import net.minecraft.util.math.vector.Matrix3f import net.minecraft.util.text.TextFormatting import net.minecraftforge.client.ForgeHooksClient import net.minecraftforge.common.MinecraftForge -import org.lwjgl.opengl.GL11 import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable @@ -47,8 +42,8 @@ object RobotRenderer extends Function[TileEntityRendererDispatcher, RobotRendere private val instance = new RobotRenderer(null) - def renderChassis(stack: MatrixStack, offset: Double = 0, isRunningOverride: Boolean = false) = - instance.renderChassis(stack, null, offset, isRunningOverride) + def renderChassis(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, offset: Double = 0, isRunningOverride: Boolean = false) = + instance.renderChassis(stack, buffer, light, null, offset, isRunningOverride) } class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[tileentity.RobotProxy](dispatch) { @@ -75,14 +70,6 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe private val gt = 0.5f + gap private val gb = 0.5f - gap - // https://github.com/MinecraftForge/MinecraftForge/issues/2321 - val NORMAL_3F = new VertexFormatElement(0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.NORMAL, 3) - val POSITION_TEX_NORMAL = new VertexFormat(ImmutableList.builder() - .add(DefaultVertexFormats.ELEMENT_POSITION) - .add(DefaultVertexFormats.ELEMENT_UV0) - .add(DefaultVertexFormats.ELEMENT_NORMAL) - .build()) - private implicit def extendWorldRenderer(self: IVertexBuilder): ExtendedWorldRenderer = new ExtendedWorldRenderer(self) private class ExtendedWorldRenderer(val buffer: IVertexBuilder) { @@ -92,54 +79,60 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe } } - private def drawTop(stack: MatrixStack): Unit = { - val t = Tessellator.getInstance - val r = t.getBuilder + private def drawTop(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int): Unit = { + val r = buffer.getBuffer(RenderTypes.ROBOT_CHASSIS) - r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMAL) + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(0, 0).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).normal(stack.last.normal, new Vector3d(-1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() - t.end() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() - r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(-1, 0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 1).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(0, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 1).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - t.end() + r.vertex(stack.last.pose, l, gt, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() } - private def drawBottom(stack: MatrixStack): Unit = { - val t = Tessellator.getInstance - val r = t.getBuilder + private def drawBottom(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int): Unit = { + val r = buffer.getBuffer(RenderTypes.ROBOT_CHASSIS) - r.begin(GL11.GL_TRIANGLE_FAN, POSITION_TEX_NORMAL) + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(1, 0).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).normal(stack.last.normal, new Vector3d(-1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() - t.end() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() - r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(-1, -0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(0, 1).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(0.5f, 0.5f).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - t.end() + r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() } def resetMountPoints(running: Boolean) { @@ -209,7 +202,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe mountPoints(6).rotation.setW(0) } - def renderChassis(stack: MatrixStack, robot: tileentity.Robot = null, offset: Double = 0, isRunningOverride: Boolean = false) { + def renderChassis(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, robot: tileentity.Robot = null, offset: Double = 0, isRunningOverride: Boolean = false) { val isRunning = if (robot == null) isRunningOverride else robot.isRunning val size = 0.3f @@ -229,63 +222,46 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val event = new RobotRenderEvent(robot, mountPoints) MinecraftForge.EVENT_BUS.post(event) if (!event.isCanceled) { - Textures.bind(Textures.Model.Robot) if (!isRunning) { stack.translate(0, -2 * gap, 0) } - drawBottom(stack) + drawBottom(stack, buffer, light) if (!isRunning) { stack.translate(0, -2 * gap, 0) } if (ForgeHooksClient.getWorldRenderPass > 0) return - drawTop(stack) - RenderSystem.color3f(1, 1, 1) + drawTop(stack, buffer, light) if (isRunning) { - RenderState.disableEntityLighting() - - { - // Additive blending for the light. - RenderState.makeItBlend() - RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - // Light color. - val lightColor = if (robot != null && robot.info != null) robot.info.lightColor else 0xF23030 - val r = (lightColor >>> 16) & 0xFF - val g = (lightColor >>> 8) & 0xFF - val b = (lightColor >>> 0) & 0xFF - RenderSystem.color3f(r / 255f, g / 255f, b / 255f) - } - - val t = Tessellator.getInstance - val r = t.getBuilder - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - r.vertex(stack.last.pose, l, gt, l).uv(u0, v0).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(u0, v1).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(u1, v1).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(u1, v0).endVertex() - - r.vertex(stack.last.pose, l, gt, h).uv(u0, v0).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(u0, v1).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(u1, v1).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(u1, v0).endVertex() - - r.vertex(stack.last.pose, h, gt, h).uv(u0, v0).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(u0, v1).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(u1, v1).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(u1, v0).endVertex() - - r.vertex(stack.last.pose, h, gt, l).uv(u0, v0).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(u0, v1).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(u1, v1).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(u1, v0).endVertex() - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() + // Light color. + val lightColor = if (robot != null && robot.info != null) robot.info.lightColor else 0xF23030 + val red = (lightColor >>> 16) & 0xFF + val green = (lightColor >>> 8) & 0xFF + val blue = (lightColor >>> 0) & 0xFF + + val r = buffer.getBuffer(RenderTypes.ROBOT_LIGHT) + r.vertex(stack.last.pose, l, gt, l).color(red, green, blue, 0xFF).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, l, gb, h).color(red, green, blue, 0xFF).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, l, gb, h).color(red, green, blue, 0xFF).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, h, gt, h).color(red, green, blue, 0xFF).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, h, gt, h).color(red, green, blue, 0xFF).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, h, gb, l).color(red, green, blue, 0xFF).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(u1, v0).endVertex() + + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(u0, v0).endVertex() + r.vertex(stack.last.pose, h, gb, l).color(red, green, blue, 0xFF).uv(u0, v1).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(u1, v1).endVertex() + r.vertex(stack.last.pose, l, gt, l).color(red, green, blue, 0xFF).uv(u1, v0).endVertex() } - RenderSystem.color4f(1, 1, 1, 1) } } @@ -296,7 +272,6 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val worldTime = proxy.getLevel.getGameTime + f matrix.pushPose() - RenderState.pushAttrib() matrix.translate(0.5, 0.5, 0.5) @@ -321,10 +296,6 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe matrix.pushPose() - RenderSystem.depthMask(true) - RenderState.enableEntityLighting() - RenderSystem.disableBlend() - if (robot.isAnimatingTurn) { val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toFloat val axis = if (robot.turnAxis < 0) Vector3f.YN else Vector3f.YP @@ -341,7 +312,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe matrix.translate(-0.5f, -0.5f, -0.5f) val offset = timeJitter + worldTime / 20.0 - renderChassis(matrix, robot, offset) + renderChassis(matrix, buffer, light, robot, offset) val pos = proxy.getBlockPos val dist = Minecraft.getInstance.player.position.distanceToSqr(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) @@ -350,15 +321,11 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe StackOption(robot.getItem(0)) match { case SomeStack(stack) => - RenderState.pushAttrib() matrix.pushPose() try { // Copy-paste from player render code, with minor adjustments for // robot scale. - RenderSystem.disableCull() - RenderSystem.enableRescaleNormal() - matrix.scale(1, -1, -1) matrix.translate(0, -8 * 0.0625F - 0.0078125F, -0.5F) @@ -372,22 +339,21 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val item = stack.getItem if (item.isInstanceOf[BlockItem]) { - matrix.mulPose(Vector3f.XP.rotationDegrees(-90.0F)) - matrix.mulPose(Vector3f.YP.rotationDegrees(180.0F)) + matrix.mulPose(Vector3f.XP.rotationDegrees(180.0F)) + matrix.mulPose(Vector3f.YP.rotationDegrees(90.0F)) val scale = 0.625F matrix.scale(scale, scale, scale) } else if (item == Items.BOW) { - matrix.translate(1.5f/16f, -0.125F, -0.125F) - matrix.mulPose(Vector3f.ZP.rotationDegrees(10.0F)) + matrix.translate(0, -3f/16f, -0.125F) + matrix.mulPose(Vector3f.ZP.rotationDegrees(170.0F)) val scale = 0.625F - matrix.scale(scale, -scale, scale) + matrix.scale(scale, scale, scale) } else { - matrix.translate(0, 2f/16f, 0) - val scale = 0.875F + matrix.translate(1f/16f, 1f/16f, -2f/16f) + val scale = 0.625F matrix.scale(scale, scale, scale) - matrix.mulPose(Vector3f.YP.rotationDegrees(90.0F)) matrix.mulPose(Vector3f.ZP.rotationDegrees(180.0F)) } @@ -398,10 +364,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe OpenComputers.log.warn("Failed rendering equipped item.", e) robot.renderingErrored = true } - RenderSystem.enableCull() - RenderSystem.disableRescaleNormal() matrix.popPose() - RenderState.popAttrib() case _ => } @@ -446,49 +409,23 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe matrix.popPose() val name = robot.name - if (Settings.get.robotLabels && ForgeHooksClient.getWorldRenderPass == 1 && !Strings.isNullOrEmpty(name) && ForgeHooksClient.isNameplateInRenderDistance(null, dist)) { - matrix.pushPose() - + if (Settings.get.robotLabels && !Strings.isNullOrEmpty(name) && ForgeHooksClient.isNameplateInRenderDistance(null, dist)) { // This is pretty much copy-pasta from the entity's label renderer. - val t = Tessellator.getInstance - val r = t.getBuilder val f = Minecraft.getInstance.font val scale = 1.6f / 60f val width = f.width(name) val halfWidth = width / 2 + val bgColor = (255f * Minecraft.getInstance.options.getBackgroundOpacity(0.25F)).asInstanceOf[Int] << 24 matrix.translate(0, 0.8, 0) - GL11.glNormal3f(0, 1, 0) - RenderSystem.color3f(1, 1, 1) - - matrix.mulPose(Vector3f.YP.rotationDegrees(-renderer.camera.getYRot)) - matrix.mulPose(Vector3f.XP.rotationDegrees(renderer.camera.getXRot)) + matrix.mulPose(Minecraft.getInstance.getEntityRenderDispatcher.cameraOrientation) matrix.scale(-scale, -scale, scale) - RenderState.makeItBlend() - RenderSystem.depthMask(false) - RenderSystem.disableLighting() - RenderSystem.disableTexture() - - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR) - r.vertex(matrix.last.pose, -halfWidth - 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() - r.vertex(matrix.last.pose, -halfWidth - 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() - r.vertex(matrix.last.pose, halfWidth + 1, 8, 0).color(0, 0, 0, 0.5f).endVertex() - r.vertex(matrix.last.pose, halfWidth + 1, -1, 0).color(0, 0, 0, 0.5f).endVertex() - t.end() - - RenderSystem.enableTexture() // For the font. - f.draw(matrix, (if (EventHandler.isItTime) TextFormatting.OBFUSCATED.toString else "") + name, -halfWidth, 0, 0xFFFFFFFF) - - RenderSystem.depthMask(true) - RenderSystem.enableLighting() - RenderState.disableBlend() - - matrix.popPose() + f.drawInBatch((if (EventHandler.isItTime) TextFormatting.OBFUSCATED.toString else "") + name, + -halfWidth, 0, -1, false, matrix.last.pose, buffer, false, bgColor, light) } matrix.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } From e0a8b2b2c3e1ec5eddf2f635597b72005ffa7237 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 7 Aug 2022 01:23:40 +0200 Subject: [PATCH 036/159] Fix robot upgrade rendering --- .../oc/api/driver/item/UpgradeRenderer.java | 3 +- .../cil/oc/client/renderer/RenderTypes.java | 21 +++++++++++ .../renderer/item/UpgradeRenderer.scala | 35 ++++++------------- .../renderer/tileentity/RobotRenderer.scala | 2 +- .../oc/common/item/traits/SimpleItem.scala | 3 +- 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java index e3d39ed84c..5edf64b84b 100644 --- a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java +++ b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.matrix.MatrixStack; import li.cil.oc.api.event.RobotRenderEvent; import li.cil.oc.api.internal.Robot; +import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.item.ItemStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -73,7 +74,7 @@ public interface UpgradeRenderer { * @param robot the robot the upgrade is rendered on. * @param pt partial tick time, e.g. for animations. */ - void render(MatrixStack matrix, ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot, float pt); + void render(MatrixStack matrix, IRenderTypeBuffer buffer, ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot, float pt); /** * Mount point names for {@link #computePreferredMountPoint}. diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 3f34f9cb83..7febc31ce4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -9,9 +9,17 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL11; public class RenderTypes extends RenderType { + public static final VertexFormat POSITION_TEX_NORMAL = new VertexFormat(new ImmutableList.Builder() + .add(DefaultVertexFormats.ELEMENT_POSITION) + .add(DefaultVertexFormats.ELEMENT_UV0) + .add(DefaultVertexFormats.ELEMENT_NORMAL) + .add(DefaultVertexFormats.ELEMENT_PADDING) + .build()); + public static final VertexFormat POSITION_TEX_UV2_NORMAL = new VertexFormat(new ImmutableList.Builder() .add(DefaultVertexFormats.ELEMENT_POSITION) .add(DefaultVertexFormats.ELEMENT_UV0) @@ -35,6 +43,19 @@ public class RenderTypes extends RenderType { .setTransparencyState(LIGHTNING_TRANSPARENCY) .createCompositeState(true)); + private static final RenderType createUpgrade(String name, ResourceLocation texture) { + return create(OpenComputers.ID() + ":upgrade_" + name, + POSITION_TEX_NORMAL, GL11.GL_QUADS, 1024, State.builder() + .setTextureState(new TextureState(texture, false, false)) + .createCompositeState(true)); + } + + public static final RenderType UPGRADE_CRAFTING = createUpgrade("crafting", Textures.Model$.MODULE$.UpgradeCrafting()); + + public static final RenderType UPGRADE_GENERATOR = createUpgrade("generator", Textures.Model$.MODULE$.UpgradeGenerator()); + + public static final RenderType UPGRADE_INVENTORY = createUpgrade("inventory", Textures.Model$.MODULE$.UpgradeInventory()); + private RenderTypes() { super(null, null, 0, 0, false, false, null, null); throw new Error(); diff --git a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala index 89d5e23176..27efacf8e1 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala @@ -3,28 +3,22 @@ package li.cil.oc.client.renderer.item import com.google.common.collect.ImmutableList import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Constants import li.cil.oc.api import li.cil.oc.api.driver.item.UpgradeRenderer.MountPointName import li.cil.oc.api.event.RobotRenderEvent.MountPoint -import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.integration.opencomputers.Item import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.client.renderer.vertex.VertexFormat import net.minecraft.item.ItemStack -import net.minecraft.util.math.vector.Quaternion +import net.minecraft.util.math.vector.Vector3f import org.lwjgl.opengl.GL11 object UpgradeRenderer { - final val POSITION_TEX_NORMAL = new VertexFormat(ImmutableList.builder() - .add(DefaultVertexFormats.ELEMENT_POSITION) - .add(DefaultVertexFormats.ELEMENT_UV0) - .add(DefaultVertexFormats.ELEMENT_NORMAL) - .add(DefaultVertexFormats.ELEMENT_PADDING) - .build()) - lazy val craftingUpgrade = api.Items.get(Constants.ItemName.CraftingUpgrade) lazy val generatorUpgrade = api.Items.get(Constants.ItemName.GeneratorUpgrade) lazy val inventoryUpgrade = api.Items.get(Constants.ItemName.InventoryUpgrade) @@ -46,26 +40,23 @@ object UpgradeRenderer { descriptor == craftingUpgrade || descriptor == generatorUpgrade || descriptor == inventoryUpgrade } - def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint): Unit = { + def render(matrix: MatrixStack, buffer: IRenderTypeBuffer, stack: ItemStack, mountPoint: MountPoint): Unit = { val descriptor = api.Items.get(stack) if (descriptor == api.Items.get(Constants.ItemName.CraftingUpgrade)) { - Textures.bind(Textures.Model.UpgradeCrafting) - drawSimpleBlock(matrix, mountPoint) + drawSimpleBlock(matrix, buffer.getBuffer(RenderTypes.UPGRADE_CRAFTING), mountPoint) RenderState.checkError(getClass.getName + ".renderItem: crafting upgrade") } else if (descriptor == api.Items.get(Constants.ItemName.GeneratorUpgrade)) { - Textures.bind(Textures.Model.UpgradeGenerator) - drawSimpleBlock(matrix, mountPoint, if (Item.dataTag(stack).getInt("remainingTicks") > 0) 0.5f else 0) + drawSimpleBlock(matrix, buffer.getBuffer(RenderTypes.UPGRADE_GENERATOR), mountPoint, if (Item.dataTag(stack).getInt("remainingTicks") > 0) 0.5f else 0) RenderState.checkError(getClass.getName + ".renderItem: generator upgrade") } else if (descriptor == api.Items.get(Constants.ItemName.InventoryUpgrade)) { - Textures.bind(Textures.Model.UpgradeInventory) - drawSimpleBlock(matrix, mountPoint) + drawSimpleBlock(matrix, buffer.getBuffer(RenderTypes.UPGRADE_INVENTORY), mountPoint) RenderState.checkError(getClass.getName + ".renderItem: inventory upgrade") } @@ -74,14 +65,10 @@ object UpgradeRenderer { private val (minX, minY, minZ) = (-0.1f, -0.1f, -0.1f) private val (maxX, maxY, maxZ) = (0.1f, 0.1f, 0.1f) - private def drawSimpleBlock(stack: MatrixStack, mountPoint: MountPoint, frontOffset: Float = 0) { - stack.mulPose(new Quaternion(mountPoint.rotation.w, mountPoint.rotation.x, mountPoint.rotation.y, mountPoint.rotation.z)) + private def drawSimpleBlock(stack: MatrixStack, r: IVertexBuilder, mountPoint: MountPoint, frontOffset: Float = 0) { + stack.mulPose(new Vector3f(mountPoint.rotation.x, mountPoint.rotation.y, mountPoint.rotation.z).rotationDegrees(mountPoint.rotation.w)) stack.translate(mountPoint.offset.x, mountPoint.offset.y, mountPoint.offset.z) - val t = Tessellator.getInstance() - val r = t.getBuilder - r.begin(GL11.GL_QUADS, POSITION_TEX_NORMAL) - // Front. r.vertex(stack.last.pose, minX, minY, maxZ).uv(frontOffset, 0.5f).normal(stack.last.normal, 0, 0, 1).endVertex() r.vertex(stack.last.pose, maxX, minY, maxZ).uv(frontOffset + 0.5f, 0.5f).normal(stack.last.normal, 0, 0, 1).endVertex() @@ -111,7 +98,5 @@ object UpgradeRenderer { r.vertex(stack.last.pose, minX, maxY, maxZ).uv(0, 0.5f).normal(stack.last.normal, -1, 0, 0).endVertex() r.vertex(stack.last.pose, minX, maxY, minZ).uv(0.5f, 0.5f).normal(stack.last.normal, -1, 0, 0).endVertex() r.vertex(stack.last.pose, minX, minY, minZ).uv(0.5f, 1).normal(stack.last.normal, -1, 0, 0).endVertex() - - t.end() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index a556728895..0e93102677 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -396,7 +396,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val (stack, renderer) = info matrix.pushPose() matrix.translate(0.5f, 0.5f, 0.5f) - renderer.render(matrix, stack, mountPoint, robot, f) + renderer.render(matrix, buffer, stack, mountPoint, robot, f) matrix.popPose() } catch { diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 50c79d8214..c1103abdfc 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -13,6 +13,7 @@ import li.cil.oc.common.tileentity import li.cil.oc.integration.opencomputers.{Item => OpenComputersItem} import li.cil.oc.util.BlockPosition import li.cil.oc.util.Tooltip +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.BlockItemUseContext @@ -140,5 +141,5 @@ trait SimpleItem extends Item with api.driver.item.UpgradeRenderer { override def computePreferredMountPoint(stack: ItemStack, robot: Robot, availableMountPoints: util.Set[String]): String = UpgradeRenderer.preferredMountPoint(stack, availableMountPoints) - override def render(matrix: MatrixStack, stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(matrix, stack, mountPoint) + override def render(matrix: MatrixStack, buffer: IRenderTypeBuffer, stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(matrix, buffer, stack, mountPoint) } From faac8a139fe231aa7c39a48668a0406a7a705a66 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 7 Aug 2022 02:06:39 +0200 Subject: [PATCH 037/159] Fix robot pet rendering --- .../li/cil/oc/api/event/RobotRenderEvent.java | 51 ++++++ .../cil/oc/client/renderer/PetRenderer.scala | 26 ++- .../cil/oc/client/renderer/RenderTypes.java | 10 +- .../renderer/tileentity/RobotRenderer.scala | 151 +++++++++--------- .../event/ExperienceUpgradeHandler.scala | 7 +- 5 files changed, 138 insertions(+), 107 deletions(-) diff --git a/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java b/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java index fdb405b98f..9aeef42801 100644 --- a/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java +++ b/src/main/java/li/cil/oc/api/event/RobotRenderEvent.java @@ -5,6 +5,7 @@ import li.cil.oc.api.internal.Robot; import net.minecraft.item.ItemStack; import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.math.vector.Vector4f; @@ -34,9 +35,59 @@ public class RobotRenderEvent extends RobotEvent { */ public final MountPoint[] mountPoints; + /** + * Overrides the color of the robot chassis' light. Only used if greater + * or equal to zero. Consists of 8 bits per channel: red, green and blue. + * The color override does NOT apply to this color. + */ + public int lightColor; + + private float mulR, mulG, mulB; + public RobotRenderEvent(Agent agent, MountPoint[] mountPoints) { super(agent); this.mountPoints = mountPoints; + lightColor = -1; + mulR = mulG = mulB = 1.0f; + } + + /** + * Convenience method for setting {@link #lightColor}. Will clamp values + * to between 0 and 1 and pack them into an RGB integer. + */ + public void setLightColor(float r, float g, float b) { + int ir = MathHelper.floor(0.5f + 255 * MathHelper.clamp(r, 0.0f, 1.0f)); + int ig = MathHelper.floor(0.5f + 255 * MathHelper.clamp(g, 0.0f, 1.0f)); + int ib = MathHelper.floor(0.5f + 255 * MathHelper.clamp(b, 0.0f, 1.0f)); + lightColor = (ir << 16) | (ig << 8) | ib; + } + + /** + * Multiplies the color or the robot chassis by a certain value. Each + * color component is clamped to between 0 and 1. This multiplier is + * cumulative, meaning if it is update too many times the robot will + * end up black (multiplier zero). This does not affect the light in + * the middle of the robot, nor does it affect upgrades. + *

    + * Use {@link #getColorMultiplier()} to obtain the pure multiplier or + * {@link #getColorMultiplier(float, float, float)} if you need to mix + * your own color into it. + */ + public void multiplyColors(float r, float g, float b) { + mulR *= MathHelper.clamp(r, 0.0f, 1.0f); + mulG *= MathHelper.clamp(g, 0.0f, 1.0f); + mulB *= MathHelper.clamp(b, 0.0f, 1.0f); + } + + public int getColorMultiplier() { + return getColorValue(1.0f, 1.0f, 1.0f); + } + + public int getColorValue(float rm, float gm, float bm) { + int r = MathHelper.floor(0.5f + 255 * MathHelper.clamp(rm * mulR, 0.0f, 1.0f)); + int g = MathHelper.floor(0.5f + 255 * MathHelper.clamp(gm * mulG, 0.0f, 1.0f)); + int b = MathHelper.floor(0.5f + 255 * MathHelper.clamp(bm * mulB, 0.0f, 1.0f)); + return (r << 16) | (g << 8) | b; } /** diff --git a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala index 971c98d8b5..b062debe4d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/PetRenderer.scala @@ -39,7 +39,8 @@ object PetRenderer { "076541f1-f10a-46de-a127-dfab8adfbb75" ->(0.2, 1.0, 0.1), // vifino "e7e90198-0ccf-4662-a827-192ec8f4419d" ->(0.0, 0.2, 0.6), // Izaya "f514ee69-7bbb-4e46-9e94-d8176324cec2" ->(0.098, 0.471, 0.784), // Wobbo - "f812c043-78ba-4324-82ae-e8f05c52ae6e" ->(0.1, 0.8, 0.5) // payonel + "f812c043-78ba-4324-82ae-e8f05c52ae6e" ->(0.1, 0.8, 0.5), // payonel + "1db17ee7-8830-4bac-8018-de154340aae6" ->(0.0, 0.5, 1.0) // Kosmos ) private val petLocations = com.google.common.cache.CacheBuilder.newBuilder(). @@ -66,21 +67,12 @@ object PetRenderer { val stack = e.getMatrixStack stack.pushPose() - RenderState.pushAttrib() val self = Minecraft.getInstance.player - val lx = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick - val ly = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick + self.getEyeHeight(self.getPose) - val lz = self.xOld + (self.getX - self.xOld) * e.getPartialRenderTick val other = e.getPlayer val px = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick - val py = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick + other.getEyeHeight(other.getPose) - val pz = other.xOld + (other.getX - other.xOld) * e.getPartialRenderTick - stack.translate(px - lx, py - ly, pz - lz) - - RenderState.enableEntityLighting() - RenderSystem.disableBlend() - RenderSystem.enableRescaleNormal() - RenderSystem.color4f(1, 1, 1, 1) + val py = other.yOld + (other.getY - other.yOld) * e.getPartialRenderTick + other.getEyeHeight(other.getPose) + val pz = other.zOld + (other.getZ - other.zOld) * e.getPartialRenderTick + stack.translate(px - self.getX, py - self.getY, pz - self.getZ) location.applyInterpolatedTransformations(stack, e.getPartialRenderTick) @@ -89,9 +81,6 @@ object PetRenderer { RobotRenderer.renderChassis(stack, e.getBuffers, e.getLight, offset, isRunningOverride = true) - RenderSystem.disableRescaleNormal() - - RenderState.popAttrib() stack.popPose() rendering = None @@ -100,7 +89,10 @@ object PetRenderer { @SubscribeEvent(priority = EventPriority.LOWEST) def onRobotRender(e: RobotRenderEvent) { rendering match { - case Some((r, g, b)) => RenderSystem.color3f(r.toFloat, g.toFloat, b.toFloat) + case Some((r, g, b)) => { + e.setLightColor(r.toFloat, g.toFloat, b.toFloat) + e.multiplyColors(r.toFloat, g.toFloat, b.toFloat) + } case _ => } } diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 7febc31ce4..f07f60d9f3 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -20,18 +20,10 @@ public class RenderTypes extends RenderType { .add(DefaultVertexFormats.ELEMENT_PADDING) .build()); - public static final VertexFormat POSITION_TEX_UV2_NORMAL = new VertexFormat(new ImmutableList.Builder() - .add(DefaultVertexFormats.ELEMENT_POSITION) - .add(DefaultVertexFormats.ELEMENT_UV0) - .add(DefaultVertexFormats.ELEMENT_UV2) - .add(DefaultVertexFormats.ELEMENT_NORMAL) - .add(DefaultVertexFormats.ELEMENT_PADDING) - .build()); - public static final TextureState ROBOT_CHASSIS_TEXTURE = new TextureState(Textures.Model$.MODULE$.Robot(), false, false); public static final RenderType ROBOT_CHASSIS = create(OpenComputers.ID() + ":robot_chassis", - POSITION_TEX_UV2_NORMAL, GL11.GL_TRIANGLES, 1024, State.builder() + DefaultVertexFormats.BLOCK, GL11.GL_TRIANGLES, 1024, State.builder() .setTextureState(ROBOT_CHASSIS_TEXTURE) .setDiffuseLightingState(DIFFUSE_LIGHTING) .setLightmapState(LIGHTMAP) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index 0e93102677..d33ebe9c84 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -79,60 +79,60 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe } } - private def drawTop(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int): Unit = { + private def drawTop(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, red: Int, green: Int, blue: Int): Unit = { val r = buffer.getBuffer(RenderTypes.ROBOT_CHASSIS) - r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).color(red, green, blue, 0xFF).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, h).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).color(red, green, blue, 0xFF).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, h).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() - r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).color(red, green, blue, 0xFF).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gt, l).color(red, green, blue, 0xFF).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 1, 0.5f).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(-1, 0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 1, 0.5f).color(red, green, blue, 0xFF).uv(0.25f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gt, l).color(red, green, blue, 0xFF).uv(0, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, 0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(0, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(-1, 0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, l, gt, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, l).color(red, green, blue, 0xFF).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, l, gt, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, h, gt, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() - r.vertex(stack.last.pose, h, gt, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, l, gt, h).color(red, green, blue, 0xFF).uv(0, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, l).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() + r.vertex(stack.last.pose, h, gt, h).color(red, green, blue, 0xFF).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, -1, 0).endVertex() } - private def drawBottom(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int): Unit = { + private def drawBottom(stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, red: Int, green: Int, blue: Int): Unit = { val r = buffer.getBuffer(RenderTypes.ROBOT_CHASSIS) - r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).color(red, green, blue, 0xFF).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, l).color(red, green, blue, 0xFF).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).color(red, green, blue, 0xFF).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, l).color(red, green, blue, 0xFF).uv(1, 0).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() - r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).color(red, green, blue, 0xFF).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(1, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, l, gb, h).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() - r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(-1, -0.2, 0)).endVertex() + r.vertex(stack.last.pose, 0.5f, 0.03f, 0.5f).color(red, green, blue, 0xFF).uv(0.75f, 0.25f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, 1)).endVertex() + r.vertex(stack.last.pose, l, gb, h).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, new Vector3d(0, -0.2, -1)).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(0.5f, 0).uv2(light).normal(stack.last.normal, new Vector3d(-1, -0.2, 0)).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, l, gb, h).uv(0, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, h).color(red, green, blue, 0xFF).uv(0, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, l, gb, l).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, h, gb, h).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() - r.vertex(stack.last.pose, h, gb, l).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, l, gb, l).color(red, green, blue, 0xFF).uv(0, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, h).color(red, green, blue, 0xFF).uv(0.5f, 1).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() + r.vertex(stack.last.pose, h, gb, l).color(red, green, blue, 0xFF).uv(0.5f, 0.5f).uv2(light).normal(stack.last.normal, 0, 1, 0).endVertex() } def resetMountPoints(running: Boolean) { @@ -222,21 +222,22 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val event = new RobotRenderEvent(robot, mountPoints) MinecraftForge.EVENT_BUS.post(event) if (!event.isCanceled) { + val color = event.getColorMultiplier + val (cr, cg, cb) = ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF) if (!isRunning) { stack.translate(0, -2 * gap, 0) } - drawBottom(stack, buffer, light) + drawBottom(stack, buffer, light, cr, cg, cb) if (!isRunning) { stack.translate(0, -2 * gap, 0) } - - if (ForgeHooksClient.getWorldRenderPass > 0) return - - drawTop(stack, buffer, light) + drawTop(stack, buffer, light, cr, cg, cb) if (isRunning) { // Light color. - val lightColor = if (robot != null && robot.info != null) robot.info.lightColor else 0xF23030 + val lightColor = if (event.lightColor < 0) { + if (robot != null && robot.info != null) robot.info.lightColor else 0xF23030 + } else event.lightColor & 0xFFFFFF val red = (lightColor >>> 16) & 0xFF val green = (lightColor >>> 8) & 0xFF val blue = (lightColor >>> 0) & 0xFF @@ -316,7 +317,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe val pos = proxy.getBlockPos val dist = Minecraft.getInstance.player.position.distanceToSqr(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) - if (ForgeHooksClient.getWorldRenderPass == 0 && !robot.renderingErrored && dist < 24 * 24) { + if (!robot.renderingErrored && dist < 24 * 24) { val itemRenderer = Minecraft.getInstance.getItemRenderer StackOption(robot.getItem(0)) match { case SomeStack(stack) => @@ -368,42 +369,40 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe case _ => } - if (ForgeHooksClient.getWorldRenderPass == 0) { - lazy val availableSlots = slotNameMapping.keys.to[mutable.Set] - lazy val wildcardRenderers = mutable.Buffer.empty[(ItemStack, UpgradeRenderer)] - lazy val slotMapping = Array.fill(mountPoints.length)(null: (ItemStack, UpgradeRenderer)) + lazy val availableSlots = slotNameMapping.keys.to[mutable.Set] + lazy val wildcardRenderers = mutable.Buffer.empty[(ItemStack, UpgradeRenderer)] + lazy val slotMapping = Array.fill(mountPoints.length)(null: (ItemStack, UpgradeRenderer)) - val renderers = (robot.componentSlots ++ robot.containerSlots).map(robot.getItem). - collect { case stack if !stack.isEmpty && stack.getItem.isInstanceOf[UpgradeRenderer] => (stack, stack.getItem.asInstanceOf[UpgradeRenderer]) } + val renderers = (robot.componentSlots ++ robot.containerSlots).map(robot.getItem). + collect { case stack if !stack.isEmpty && stack.getItem.isInstanceOf[UpgradeRenderer] => (stack, stack.getItem.asInstanceOf[UpgradeRenderer]) } - for ((stack, renderer) <- renderers) { - val preferredSlot = renderer.computePreferredMountPoint(stack, robot, availableSlots) - if (availableSlots.remove(preferredSlot)) { - slotMapping(slotNameMapping(preferredSlot)) = (stack, renderer) - } - else if (preferredSlot == MountPointName.Any) { - wildcardRenderers += ((stack, renderer)) - } + for ((stack, renderer) <- renderers) { + val preferredSlot = renderer.computePreferredMountPoint(stack, robot, availableSlots) + if (availableSlots.remove(preferredSlot)) { + slotMapping(slotNameMapping(preferredSlot)) = (stack, renderer) } - - var firstEmpty = slotMapping.indexOf(null) - for (entry <- wildcardRenderers if firstEmpty >= 0) { - slotMapping(firstEmpty) = entry - firstEmpty = slotMapping.indexOf(null) + else if (preferredSlot == MountPointName.Any) { + wildcardRenderers += ((stack, renderer)) } + } - for ((info, mountPoint) <- (slotMapping, mountPoints).zipped if info != null) try { - val (stack, renderer) = info - matrix.pushPose() - matrix.translate(0.5f, 0.5f, 0.5f) - renderer.render(matrix, buffer, stack, mountPoint, robot, f) - matrix.popPose() - } - catch { - case e: Throwable => - OpenComputers.log.warn("Failed rendering equipped upgrade.", e) - robot.renderingErrored = true - } + var firstEmpty = slotMapping.indexOf(null) + for (entry <- wildcardRenderers if firstEmpty >= 0) { + slotMapping(firstEmpty) = entry + firstEmpty = slotMapping.indexOf(null) + } + + for ((info, mountPoint) <- (slotMapping, mountPoints).zipped if info != null) try { + val (stack, renderer) = info + matrix.pushPose() + matrix.translate(0.5f, 0.5f, 0.5f) + renderer.render(matrix, buffer, stack, mountPoint, robot, f) + matrix.popPose() + } + catch { + case e: Throwable => + OpenComputers.log.warn("Failed rendering equipped upgrade.", e) + robot.renderingErrored = true } } matrix.popPose() diff --git a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala index d88a972859..aa0afbbe1b 100644 --- a/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ExperienceUpgradeHandler.scala @@ -81,13 +81,10 @@ object ExperienceUpgradeHandler { case _ => 0 } if (level > 19) { - RenderSystem.color3f(0.4f, 1, 1) + e.multiplyColors(0.4f, 1, 1) } else if (level > 9) { - RenderSystem.color3f(1, 1, 0.4f) - } - else { - RenderSystem.color3f(0.5f, 0.5f, 0.5f) + e.multiplyColors(1, 1, 0.4f) } } From d27c1175f49981029d59a119abbc4e791121f25a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 8 Aug 2022 00:21:43 +0200 Subject: [PATCH 038/159] Fix item tooltip keybind and style --- .../scala/li/cil/oc/client/KeyBindings.scala | 16 +++++++++++++++- src/main/scala/li/cil/oc/client/Proxy.scala | 1 + src/main/scala/li/cil/oc/common/block/Case.scala | 2 +- .../li/cil/oc/common/block/Disassembler.scala | 2 +- .../scala/li/cil/oc/common/block/DiskDrive.scala | 2 +- .../scala/li/cil/oc/common/block/Hologram.scala | 2 +- src/main/scala/li/cil/oc/common/block/Item.scala | 9 --------- .../li/cil/oc/common/block/Microcontroller.scala | 3 ++- .../scala/li/cil/oc/common/block/Print.scala | 9 +++++---- src/main/scala/li/cil/oc/common/block/Raid.scala | 3 ++- .../scala/li/cil/oc/common/block/Redstone.scala | 2 +- .../li/cil/oc/common/block/RobotProxy.scala | 10 +++++----- .../scala/li/cil/oc/common/block/Screen.scala | 2 +- .../li/cil/oc/common/block/SimpleBlock.scala | 2 +- .../oc/common/block/traits/PowerAcceptor.scala | 2 +- src/main/scala/li/cil/oc/common/item/APU.scala | 2 +- src/main/scala/li/cil/oc/common/item/Drone.scala | 3 ++- .../scala/li/cil/oc/common/item/LinkedCard.scala | 4 ++-- .../scala/li/cil/oc/common/item/Server.scala | 6 +++--- .../scala/li/cil/oc/common/item/Tablet.scala | 4 ++-- .../scala/li/cil/oc/common/item/Terminal.scala | 1 + .../cil/oc/common/item/UpgradeExperience.scala | 3 ++- .../scala/li/cil/oc/common/item/UpgradeMF.scala | 3 ++- .../li/cil/oc/common/item/UpgradeTank.scala | 3 ++- .../li/cil/oc/common/item/traits/CPULike.scala | 2 +- .../oc/common/item/traits/FileSystemLike.scala | 11 ++++++----- .../li/cil/oc/common/item/traits/ItemTier.scala | 3 ++- .../cil/oc/common/item/traits/SimpleItem.scala | 4 ++-- .../oc/common/nanomachines/NeuralNetwork.scala | 2 +- .../appeng/ConverterCellInventory.java | 2 +- .../minecraft/ConverterFluidStack.scala | 2 +- .../minecraft/ConverterItemStack.scala | 2 +- src/main/scala/li/cil/oc/util/Tooltip.scala | 3 +++ 33 files changed, 73 insertions(+), 54 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/KeyBindings.scala b/src/main/scala/li/cil/oc/client/KeyBindings.scala index fafe6f1c87..5607693da6 100644 --- a/src/main/scala/li/cil/oc/client/KeyBindings.scala +++ b/src/main/scala/li/cil/oc/client/KeyBindings.scala @@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft import net.minecraft.client.settings.KeyBinding import net.minecraft.client.util.InputMappings import net.minecraftforge.client.settings.KeyConflictContext +import net.minecraftforge.client.settings.KeyModifier import org.lwjgl.glfw.GLFW import scala.collection.mutable @@ -16,7 +17,20 @@ object KeyBindings { def getKeyBindingName(keyBinding: KeyBinding) = keyBinding.getTranslatedKeyMessage.getString - val extendedTooltip = Minecraft.getInstance.options.keyShift + val extendedTooltip = new KeyBinding("key.opencomputers.extendedTooltip", KeyConflictContext.GUI, + InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_SHIFT, OpenComputers.Name) { + + override def isActiveAndMatches(input: InputMappings.Input): Boolean = { + input != InputMappings.UNKNOWN && input.equals(getKey) && isConflictContextAndModifierActive + } + + override def isConflictContextAndModifierActive: Boolean = { + // KeyModifier.NONE does not accept pure modifier keys by default (except for IN_GAME conflict contexts). + val modifierActive = getKeyModifier.isActive(getKeyConflictContext) || + (getKeyModifier == KeyModifier.NONE && KeyModifier.isKeyCodeModifier(getKey)) + getKeyConflictContext.isActive && modifierActive + } + } val analyzeCopyAddr = new KeyBinding("key.opencomputers.analyzeCopyAddress", KeyConflictContext.IN_GAME, InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_CONTROL, OpenComputers.Name) diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 6990765939..69ca5442fd 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -74,6 +74,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.SCREEN, ScreenRenderer) ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.TRANSPOSER, TransposerRenderer) + ClientRegistry.registerKeyBinding(KeyBindings.extendedTooltip) ClientRegistry.registerKeyBinding(KeyBindings.analyzeCopyAddr) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 61d0ae143e..0d495060f8 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -35,7 +35,7 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, slots)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index a1b1e65937..a558a29bf9 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -20,7 +20,7 @@ import scala.collection.convert.ImplicitConversionsToScala._ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, (Settings.get.disassemblerBreakChance * 100).toInt.toString)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 2d0888131c..88189e68df 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -32,7 +32,7 @@ class DiskDrive extends SimpleBlock with traits.GUI { override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.tooltipTail(stack, world, tooltip, flag) if (Mods.ComputerCraft.isModAvailable) { - for (curr <- Tooltip.get(getClass.getSimpleName + ".CC")) tooltip.add(new StringTextComponent(curr)) + for (curr <- Tooltip.get(getClass.getSimpleName + ".CC")) tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index 1f1b37a829..07c02a282c 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -32,7 +32,7 @@ class Hologram(val tier: Int) extends SimpleBlock { override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase() + tier)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 275b36e043..fa9540ddd3 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -36,15 +36,6 @@ object Item { } class Item(value: Block, props: Properties = new Properties()) extends BlockItem(value, Item.setCreativeTab(value, props)) { - override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { - super.appendHoverText(stack, world, tooltip, flag) - getBlock match { - case (simple: SimpleBlock) => - simple.appendHoverText(stack, world, tooltip, flag) - case _ => - } - } - override def getRarity(stack: ItemStack): Rarity = getBlock match { case simple: SimpleBlock => simple.rarity(stack) case _ => Rarity.COMMON diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index 1b615f4be9..fa77275d5d 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -15,6 +15,7 @@ import li.cil.oc.integration.util.Wrench import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity import li.cil.oc.util.StackOption._ +import li.cil.oc.util.Tooltip import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -57,7 +58,7 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc if (KeyBindings.showExtendedTooltips) { val info = new MicrocontrollerData(stack) for (component <- info.components if !component.isEmpty) { - tooltip.add(new StringTextComponent("- " + component.getDisplayName)) + tooltip.add(new StringTextComponent("- " + component.getHoverName.getString).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 2b0004f5b9..30483782a9 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -9,6 +9,7 @@ import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.util.InventoryUtils +import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.block.material.Material @@ -49,20 +50,20 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) = { super.tooltipBody(stack, world, tooltip, advanced) val data = new PrintData(stack) - data.tooltip.foreach(s => tooltip.addAll(s.lines.map(new StringTextComponent(_)).toIterable)) + data.tooltip.foreach(s => tooltip.addAll(s.lines.map(new StringTextComponent(_).setStyle(Tooltip.DefaultStyle)).toIterable)) } override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) = { super.tooltipTail(stack, world, tooltip, advanced) val data = new PrintData(stack) if (data.isBeaconBase) { - tooltip.add(new StringTextComponent(Localization.Tooltip.PrintBeaconBase)) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintBeaconBase).setStyle(Tooltip.DefaultStyle)) } if (data.emitRedstone) { - tooltip.add(new StringTextComponent(Localization.Tooltip.PrintRedstoneLevel(data.redstoneLevel))) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintRedstoneLevel(data.redstoneLevel)).setStyle(Tooltip.DefaultStyle)) } if (data.emitLight) { - tooltip.add(new StringTextComponent(Localization.Tooltip.PrintLightValue(data.lightLevel))) + tooltip.add(new StringTextComponent(Localization.Tooltip.PrintLightValue(data.lightLevel)).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index 2e65e3f969..53bb326e7d 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.GuiType import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity +import li.cil.oc.util.Tooltip import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -33,7 +34,7 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si if (KeyBindings.showExtendedTooltips) { val data = new RaidData(stack) for (disk <- data.disks if !disk.isEmpty) { - tooltip.add(new StringTextComponent("- " + disk.getDisplayName)) + tooltip.add(new StringTextComponent("- " + disk.getHoverName.getString).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index 8be44b651e..21c3420c6c 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -20,7 +20,7 @@ class Redstone extends RedstoneAware { super.tooltipTail(stack, world, tooltip, advanced) // todo more generic way for redstone mods to provide lines if (Mods.ProjectRedTransmission.isModAvailable) { - for (curr <- Tooltip.get("RedstoneCard.ProjectRed")) tooltip.add(new StringTextComponent(curr)) + for (curr <- Tooltip.get("RedstoneCard.ProjectRed")) tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 7d57bbbc5a..58be81bbff 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -92,7 +92,7 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get("robot")) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } @@ -103,10 +103,10 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 val components = info.containers ++ info.components if (components.length > 0) { for (curr <- Tooltip.get("server.Components")) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } for (component <- components if !component.isEmpty) { - tooltip.add(new StringTextComponent("- " + component.getDisplayName)) + tooltip.add(new StringTextComponent("- " + component.getHoverName.getString).setStyle(Tooltip.DefaultStyle)) } } } @@ -119,7 +119,7 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 val level = Math.min((Math.pow(xp - Settings.get.baseXpToLevel, 1 / Settings.get.exponentialXpGrowth) / Settings.get.constantXpGrowth).toInt, 30) if (level > 0) { for (curr <- Tooltip.get(getDescriptionId + "_level", level)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } } @@ -127,7 +127,7 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 val energy = stack.getTag.getInt(Settings.namespace + "storedEnergy") if (energy > 0) { for (curr <- Tooltip.get(getDescriptionId + "_storedenergy", energy)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 9386d859f7..0a7ec7acd7 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -46,7 +46,7 @@ class Screen(val tier: Int) extends RedstoneAware { val (w, h) = Settings.screenResolutionsByTier(tier) val depth = PackedColor.Depth.bits(Settings.screenDepthsByTier(tier)) for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, w, h, depth)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 764ddbabdc..5cc050efd9 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -93,7 +93,7 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala index e2608bb242..f2bb216dc5 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala @@ -21,7 +21,7 @@ trait PowerAcceptor extends SimpleBlock { override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { super.tooltipTail(stack, world, tooltip, advanced) for (curr <- Tooltip.extended("PowerAcceptor", energyThroughput.toInt)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/APU.scala b/src/main/scala/li/cil/oc/common/item/APU.scala index f43798ea4d..88fe8adc4f 100644 --- a/src/main/scala/li/cil/oc/common/item/APU.scala +++ b/src/main/scala/li/cil/oc/common/item/APU.scala @@ -24,7 +24,7 @@ class APU(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) override def gpuTier = tier - override protected def tooltipName = Option(super.getDescriptionId) + override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData: Seq[Any] = { super[CPULike].tooltipData ++ super[GPULike].tooltipData diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index ad1d782d87..6a56af79d1 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -14,6 +14,7 @@ import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.agent import li.cil.oc.util.BlockPosition import li.cil.oc.util.Rarity +import li.cil.oc.util.Tooltip import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item @@ -44,7 +45,7 @@ class Drone(props: Properties = new Properties().tab(CreativeTab)) extends Item( if (KeyBindings.showExtendedTooltips) { val info = new DroneData(stack) for (component <- info.components if !component.isEmpty) { - tooltip.add(new StringTextComponent("- " + component.getDisplayName)) + tooltip.add(new StringTextComponent("- " + component.getHoverName.getString).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala index fa70c99128..7e6eb2ce95 100644 --- a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala @@ -29,12 +29,12 @@ class LinkedCard(props: Properties = new Properties().tab(CreativeTab)) extends val channel = data.getString(Settings.namespace + "tunnel") if (channel.length > 13) { for (curr <- Tooltip.get(unlocalizedName + "_channel", channel.substring(0, 13) + "...")) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } else { for (curr <- Tooltip.get(unlocalizedName + "_channel", channel)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index ff10deab75..f10b2bf691 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -47,15 +47,15 @@ class Server(val tier: Int, props: Properties = new Properties().tab(CreativeTab HelperInventory.reinitialize() val stacks = mutable.Map.empty[String, Int] for (aStack <- (0 until HelperInventory.getContainerSize).map(HelperInventory.getItem) if !aStack.isEmpty) { - val displayName = aStack.getDisplayName.getString + val displayName = aStack.getHoverName.getString stacks += displayName -> (if (stacks.contains(displayName)) stacks(displayName) + 1 else 1) } if (stacks.nonEmpty) { for (curr <- Tooltip.get("server.Components")) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } for (itemName <- stacks.keys.toArray.sorted) { - tooltip.add(new StringTextComponent("- " + stacks(itemName) + "x " + itemName)) + tooltip.add(new StringTextComponent("- " + stacks(itemName) + "x " + itemName).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index fa708f64de..0e95507831 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -89,10 +89,10 @@ class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item val components = info.items.drop(1) if (components.length > 1) { for (curr <- Tooltip.get("server.Components")) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } components.collect { - case component if !component.isEmpty => tooltip.add(new StringTextComponent("- " + component.getDisplayName)) + case component if !component.isEmpty => tooltip.add(new StringTextComponent("- " + component.getHoverName.getString).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 1559fe6500..7a3ff90144 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -7,6 +7,7 @@ import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType +import li.cil.oc.util.Tooltip import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.util.ITooltipFlag diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala index 557a4fad18..b0f01c1afe 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala @@ -4,6 +4,7 @@ import java.util import li.cil.oc.CreativeTab import li.cil.oc.Localization +import li.cil.oc.util.Tooltip import li.cil.oc.util.{UpgradeExperience => ExperienceUtil} import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.Item @@ -24,7 +25,7 @@ class UpgradeExperience(props: Properties = new Properties().tab(CreativeTab)) e val experience = ExperienceUtil.getExperience(nbt) val level = ExperienceUtil.calculateLevelFromExperience(experience) val reportedLevel = ExperienceUtil.calculateExperienceLevel(level, experience) - tooltip.add(new StringTextComponent(Localization.Tooltip.ExperienceLevel(reportedLevel))) + tooltip.add(new StringTextComponent(Localization.Tooltip.ExperienceLevel(reportedLevel)).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala index 9b7daf8f94..4e50e18bfb 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala @@ -6,6 +6,7 @@ import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.util.BlockPosition +import li.cil.oc.util.Tooltip import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.Item.Properties @@ -35,6 +36,6 @@ class UpgradeMF(props: Properties = new Properties().tab(CreativeTab)) extends I tooltip.add(new StringTextComponent(Localization.Tooltip.MFULinked(stack.getTag match { case data: CompoundNBT => data.contains(Settings.namespace + "coord") case _ => false - }))) + })).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala index 487ee0a1cd..84e930957a 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala @@ -4,6 +4,7 @@ import java.util import li.cil.oc.CreativeTab import li.cil.oc.Settings +import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item @@ -24,7 +25,7 @@ class UpgradeTank(props: Properties = new Properties().tab(CreativeTab)) extends if (stack.hasTag) { FluidStack.loadFluidStackFromNBT(stack.getTag.getCompound(Settings.namespace + "data")) match { case stack: FluidStack => - tooltip.add(new StringTextComponent(stack.getFluid.getAttributes.getDisplayName(stack).getString + ": " + stack.getAmount + "/16000")) + tooltip.add(new StringTextComponent(stack.getFluid.getAttributes.getDisplayName(stack).getString + ": " + stack.getAmount + "/16000").setStyle(Tooltip.DefaultStyle)) case _ => } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala index cc8ab37f35..0ae5fda690 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala @@ -28,7 +28,7 @@ trait CPULike extends SimpleItem { override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { for (curr <- Tooltip.get("cpu.Architecture", api.Machine.getArchitectureName(DriverCPU.architecture(stack)))) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala index c7f4abea03..5968659d5e 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -6,8 +6,9 @@ import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType -import net.minecraft.client.util.ITooltipFlag import li.cil.oc.common.item.data.DriveData +import li.cil.oc.util.Tooltip +import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult @@ -32,19 +33,19 @@ trait FileSystemLike extends SimpleItem { if (nbt.contains(Settings.namespace + "data")) { val data = nbt.getCompound(Settings.namespace + "data") if (data.contains(Settings.namespace + "fs.label")) { - tooltip.add(new StringTextComponent(data.getString(Settings.namespace + "fs.label"))) + tooltip.add(new StringTextComponent(data.getString(Settings.namespace + "fs.label")).setStyle(Tooltip.DefaultStyle)) } if (flag.isAdvanced && data.contains("fs")) { val fsNbt = data.getCompound("fs") if (fsNbt.contains("capacity.used")) { val used = fsNbt.getLong("capacity.used") - tooltip.add(new StringTextComponent(Localization.Tooltip.DiskUsage(used, kiloBytes * 1024))) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskUsage(used, kiloBytes * 1024)).setStyle(Tooltip.DefaultStyle)) } } } val data = new DriveData(stack) - tooltip.add(new StringTextComponent(Localization.Tooltip.DiskMode(data.isUnmanaged))) - tooltip.add(new StringTextComponent(Localization.Tooltip.DiskLock(data.lockInfo))) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskMode(data.isUnmanaged)).setStyle(Tooltip.DefaultStyle)) + tooltip.add(new StringTextComponent(Localization.Tooltip.DiskLock(data.lockInfo)).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala index 1c2ce0a5c1..74c63c681e 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/ItemTier.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.item.traits import java.util import li.cil.oc.Localization +import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -17,7 +18,7 @@ trait ItemTier extends SimpleItem { override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) if (flag.isAdvanced) { - tooltip.add(new StringTextComponent(Localization.Tooltip.Tier(tierFromDriver(stack) + 1))) + tooltip.add(new StringTextComponent(Localization.Tooltip.Tier(tierFromDriver(stack) + 1)).setStyle(Tooltip.DefaultStyle)) } } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index c1103abdfc..2abea2e9f3 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -113,13 +113,13 @@ trait SimpleItem extends Item with api.driver.item.UpgradeRenderer { override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { if (tooltipName.isDefined) { for (curr <- Tooltip.get(tooltipName.get, tooltipData: _*)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } tooltipExtended(stack, tooltip) } else { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase)) { - tooltip.add(new StringTextComponent(curr)) + tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } tooltipCosts(stack, tooltip) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala index 4a5e47b12c..6d85d834e2 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala @@ -103,7 +103,7 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { case playerMP: ServerPlayerEntity => (s: String) => PacketSender.sendClientLog(s, playerMP) case _ => (s: String) => OpenComputers.log.info(s) } - log(s"Creating debug configuration for nanomachines in player ${controller.player.getDisplayName}.") + log(s"Creating debug configuration for nanomachines in player ${controller.player.getDisplayName.getString}.") behaviors.clear() behaviors ++= api.Nanomachines.getProviders. diff --git a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java index 573cea61c7..c6abf036cb 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java +++ b/src/main/scala/li/cil/oc/integration/appeng/ConverterCellInventory.java @@ -30,7 +30,7 @@ public void convert(final Object value, final Map output) { //output.put("getPreformattedItems",cell.getConfigInventory()); output.put("fuzzyMode", cell.getFuzzyMode().toString()); - output.put("name", cell.getItemStack().getDisplayName()); + output.put("name", cell.getItemStack().getDisplayName().getString()); } else if (value instanceof ICellInventoryHandler) { convert(((ICellInventoryHandler) value).getCellInv(), output); } else if ((value instanceof ItemStack) && (((ItemStack)value).getItem() instanceof IStorageCell)) { diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala index b7ae55a38f..069dc44e51 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterFluidStack.scala @@ -14,7 +14,7 @@ object ConverterFluidStack extends api.driver.Converter { output += "hasTag" -> Boolean.box(stack.hasTag) val fluid = stack.getFluid output += "name" -> fluid.getRegistryName.toString - output += "label" -> fluid.getAttributes.getDisplayName(stack) + output += "label" -> fluid.getAttributes.getDisplayName(stack).getString case _ => } } diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala index d7b55baa9b..f8e263e1c7 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ConverterItemStack.scala @@ -57,7 +57,7 @@ object ConverterItemStack extends api.driver.Converter { output += "maxSize" -> Int.box(stack.getMaxStackSize) output += "hasTag" -> Boolean.box(stack.hasTag) output += "name" -> stack.getItem.getRegistryName - output += "label" -> stack.getDisplayName + output += "label" -> stack.getDisplayName.getString // custom mod tags if (stack.hasTag) { diff --git a/src/main/scala/li/cil/oc/util/Tooltip.scala b/src/main/scala/li/cil/oc/util/Tooltip.scala index f2f4369de6..b4debf5004 100644 --- a/src/main/scala/li/cil/oc/util/Tooltip.scala +++ b/src/main/scala/li/cil/oc/util/Tooltip.scala @@ -7,6 +7,7 @@ import net.minecraft.client.Minecraft import net.minecraft.client.gui.FontRenderer import net.minecraft.util.text.CharacterManager.ISliceAcceptor import net.minecraft.util.text.Style +import net.minecraft.util.text.TextFormatting import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ @@ -16,6 +17,8 @@ object Tooltip { private def font = Minecraft.getInstance.font + val DefaultStyle = Style.EMPTY.applyFormat(TextFormatting.GRAY) + def get(name: String, args: Any*): java.util.List[String] = { if (!Localization.canLocalize(Settings.namespace + "tooltip." + name)) return Seq.empty[String] val tooltip = Localization.localizeImmediately("tooltip." + name). From 6f536730daa4ec9e551ed5a7ae704d1244112fb2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 8 Aug 2022 21:51:24 +0200 Subject: [PATCH 039/159] Fix MFU renderer misalignment --- .../client/renderer/MFUTargetRenderer.scala | 170 +++++++----------- .../cil/oc/client/renderer/RenderTypes.java | 20 +++ 2 files changed, 88 insertions(+), 102 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala index b861b05069..dd7f6f150f 100644 --- a/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala @@ -1,11 +1,14 @@ package li.cil.oc.client.renderer +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.util.BlockPosition import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.RenderType import net.minecraft.item.ItemStack import net.minecraft.util.Hand import net.minecraft.util.ResourceLocation @@ -14,10 +17,10 @@ import net.minecraft.util.math.vector.Matrix4f import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.eventbus.api.SubscribeEvent -import org.lwjgl.opengl.GL11 object MFUTargetRenderer { - private val color = 0x00FF00 + private val (drawRed, drawGreen, drawBlue) = (0.0f, 1.0f, 0.0f) + private lazy val mfu = api.Items.get(Constants.ItemName.MFU) @SubscribeEvent @@ -36,34 +39,22 @@ object MFUTargetRenderer { val bounds = BlockPosition(x, y, z).bounds.inflate(0.1, 0.1, 0.1) - val px = player.xOld + (player.getX - player.xOld) * e.getPartialTicks - val py = player.yOld + (player.getY - player.yOld) * e.getPartialTicks - val pz = player.zOld + (player.getZ - player.zOld) * e.getPartialTicks - RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: entering (aka: wasntme)") - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) val matrix = e.getMatrixStack matrix.pushPose() - matrix.translate(-px, -py, -pz) - RenderState.makeItBlend() - GL11.glDisable(GL11.GL_LIGHTING) - GL11.glDisable(GL11.GL_TEXTURE_2D) - GL11.glDisable(GL11.GL_DEPTH_TEST) - GL11.glDisable(GL11.GL_CULL_FACE) + val camPos = Minecraft.getInstance.gameRenderer.getMainCamera.getPosition + matrix.translate(-camPos.x, -camPos.y, -camPos.z) - GL11.glColor4f( - ((color >> 16) & 0xFF) / 255f, - ((color >> 8) & 0xFF) / 255f, - ((color >> 0) & 0xFF) / 255f, - 0.25f) - GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE) - drawBox(matrix.last.pose, new Vector4f(), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat) - GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL) - drawFace(matrix.last.pose, new Vector4f(), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat, side) + RenderSystem.disableDepthTest() // Default state for depth test is disabled, but it's enabled here so we have to change it manually. + val buffer = Minecraft.getInstance.renderBuffers.bufferSource + drawBox(matrix.last.pose, buffer.getBuffer(RenderTypes.MFU_LINES), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, + bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat, drawRed, drawGreen, drawBlue) + drawFace(matrix.last.pose, buffer.getBuffer(RenderTypes.MFU_QUADS), bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat, + bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat, side, drawRed, drawGreen, drawBlue) + buffer.endBatch() matrix.popPose() - GL11.glPopAttrib() RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: leaving") } @@ -71,95 +62,70 @@ object MFUTargetRenderer { } } - def glVertex(matrix: Matrix4f, temp: Vector4f, x: Float, y: Float, z: Float) { - temp.set(x, y, z, 1) - temp.transform(matrix) - GL11.glVertex3f(temp.x, temp.y, temp.z) - } + def drawBox(matrix: Matrix4f, builder: IVertexBuilder, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float, r: Float, g: Float, b: Float) { + // Bottom square. + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.5f).endVertex() + + // Vertical bars. + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() - def drawBox(matrix: Matrix4f, temp: Vector4f, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float) { - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, minX, minY, maxZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, maxX, minY, minZ) - GL11.glEnd() - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, maxX, minY, minZ) - glVertex(matrix, temp, maxX, maxY, minZ) - glVertex(matrix, temp, minX, maxY, minZ) - GL11.glEnd() - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, maxY, minZ) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, minX, maxY, maxZ) - glVertex(matrix, temp, minX, maxY, minZ) - GL11.glEnd() - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, minX, minY, maxZ) - glVertex(matrix, temp, minX, maxY, maxZ) - GL11.glEnd() - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, minX, maxY, minZ) - glVertex(matrix, temp, minX, maxY, maxZ) - glVertex(matrix, temp, minX, minY, maxZ) - GL11.glEnd() - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, minY, minZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, maxX, maxY, minZ) - GL11.glEnd() + // Top square. + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.5f).endVertex() + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.5f).endVertex() } - private def drawFace(matrix: Matrix4f, temp: Vector4f, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float, side: Int): Unit = { + private def drawFace(matrix: Matrix4f, builder: IVertexBuilder, minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float, side: Int, r: Float, g: Float, b: Float): Unit = { side match { case 0 => // Down - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, minX, minY, maxZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, maxX, minY, minZ) - GL11.glEnd() + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.25f).endVertex() case 1 => // Up - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, maxY, minZ) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, minX, maxY, maxZ) - glVertex(matrix, temp, minX, maxY, minZ) - GL11.glEnd() + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.25f).endVertex() case 2 => // North - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, maxX, minY, minZ) - glVertex(matrix, temp, maxX, maxY, minZ) - glVertex(matrix, temp, minX, maxY, minZ) - GL11.glEnd() + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.25f).endVertex() case 3 => // South - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, minX, minY, maxZ) - glVertex(matrix, temp, minX, maxY, maxZ) - GL11.glEnd() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() case 4 => // East - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, minX, minY, minZ) - glVertex(matrix, temp, minX, maxY, minZ) - glVertex(matrix, temp, minX, maxY, maxZ) - glVertex(matrix, temp, minX, minY, maxZ) - GL11.glEnd() + builder.vertex(matrix, minX, minY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, minX, minY, maxZ).color(r, g, b, 0.25f).endVertex() case 5 => // West - GL11.glBegin(GL11.GL_QUADS) - glVertex(matrix, temp, maxX, minY, minZ) - glVertex(matrix, temp, maxX, minY, maxZ) - glVertex(matrix, temp, maxX, maxY, maxZ) - glVertex(matrix, temp, maxX, maxY, minZ) - GL11.glEnd() + builder.vertex(matrix, maxX, minY, minZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, minY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, maxY, maxZ).color(r, g, b, 0.25f).endVertex() + builder.vertex(matrix, maxX, maxY, minZ).color(r, g, b, 0.25f).endVertex() case _ => // WTF? } } diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index f07f60d9f3..107f9834d0 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -1,8 +1,11 @@ package li.cil.oc.client.renderer; +import java.util.OptionalDouble; + import com.google.common.collect.ImmutableList; import li.cil.oc.OpenComputers; import li.cil.oc.client.Textures; +import net.minecraft.client.renderer.RenderState.LineState; import net.minecraft.client.renderer.RenderState.TextureState; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType.State; @@ -48,6 +51,23 @@ private static final RenderType createUpgrade(String name, ResourceLocation text public static final RenderType UPGRADE_INVENTORY = createUpgrade("inventory", Textures.Model$.MODULE$.UpgradeInventory()); + public static final RenderType MFU_LINES = create(OpenComputers.ID() + ":mfu_lines", + DefaultVertexFormats.POSITION_COLOR, GL11.GL_LINES, 1024, State.builder() + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(NO_DEPTH_TEST) + .setOutputState(TRANSLUCENT_TARGET) + .setLineState(new LineState(OptionalDouble.of(2.0))) + .createCompositeState(false)); + + public static final RenderType MFU_QUADS = create(OpenComputers.ID() + ":mfu_quads", + DefaultVertexFormats.POSITION_COLOR, GL11.GL_QUADS, 256, State.builder() + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(NO_DEPTH_TEST) + .setCullState(NO_CULL) + .setOutputState(TRANSLUCENT_TARGET) + .setWriteMaskState(COLOR_WRITE) + .createCompositeState(false)); + private RenderTypes() { super(null, null, 0, 0, false, false, null, null); throw new Error(); From d839afbe20ab588cc778daac7287355a0ca7c649 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 9 Aug 2022 00:33:14 +0200 Subject: [PATCH 040/159] Fix keyboards not being placeable --- .../scala/li/cil/oc/common/block/Item.scala | 4 +-- .../li/cil/oc/common/block/Keyboard.scala | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index fa9540ddd3..910c3054b6 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -90,9 +90,7 @@ class Item(value: Block, props: Properties = new Properties()) extends BlockItem if (super.placeBlock(ctxToUse, newState)) { // If it's a rotatable block try to make it face the player. ctx.getLevel.getBlockEntity(ctxToUse.getClickedPos) match { - case keyboard: tileentity.Keyboard => - keyboard.setFromEntityPitchAndYaw(ctxToUse.getPlayer) - keyboard.setFromFacing(ctxToUse.getClickedFace) + case keyboard: tileentity.Keyboard => // Ignore. case rotatable: tileentity.traits.Rotatable => rotatable.setFromEntityPitchAndYaw(ctxToUse.getPlayer) if (!rotatable.validFacings.contains(rotatable.pitch)) { diff --git a/src/main/scala/li/cil/oc/common/block/Keyboard.scala b/src/main/scala/li/cil/oc/common/block/Keyboard.scala index b6131b3496..7e88048574 100644 --- a/src/main/scala/li/cil/oc/common/block/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/block/Keyboard.scala @@ -17,6 +17,7 @@ import net.minecraft.block.Blocks import net.minecraft.block.BlockState import net.minecraft.block.material.Material import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.BlockItemUseContext import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -77,19 +78,26 @@ class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5). world.getBlockTicks.scheduleTick(pos, this, 10) } + override def getStateForPlacement(ctx: BlockItemUseContext): BlockState = { + val (pitch, yaw) = ctx.getClickedFace match { + case side@(Direction.DOWN | Direction.UP) => (side, ctx.getHorizontalDirection) + case side => (Direction.NORTH, side) + } + super.getStateForPlacement(ctx).setValue(PropertyRotatable.Pitch, pitch).setValue(PropertyRotatable.Yaw, yaw) + } + override def canSurvive(state: BlockState, world: IWorldReader, pos: BlockPos) = { - world.getBlockEntity(pos) match { - case keyboard: tileentity.Keyboard => { - val side = keyboard.facing - val sidePos = pos.relative(side.getOpposite) - world.getBlockState(sidePos).isFaceSturdy(world, sidePos, side) && - (world.getBlockEntity(pos.relative(side.getOpposite)) match { - case screen: tileentity.Screen => screen.facing != side - case _ => true - }) - } - case _ => false + // Check without the TE because this is called to check if the block may be placed. + val side = state.getValue(PropertyRotatable.Pitch) match { + case pitch@(Direction.UP | Direction.DOWN) => pitch + case _ => state.getValue(PropertyRotatable.Yaw) } + val sidePos = pos.relative(side.getOpposite) + world.getBlockState(sidePos).isFaceSturdy(world, sidePos, side) && + (world.getBlockEntity(pos.relative(side.getOpposite)) match { + case screen: tileentity.Screen => screen.facing != side + case _ => true + }) } @Deprecated From d3133ee6a3dc0c9a822933f9b2b43372b02b5d6d Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 9 Aug 2022 00:31:38 +0200 Subject: [PATCH 041/159] Fix keyboard block lighting --- .../li/cil/oc/common/block/Keyboard.scala | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Keyboard.scala b/src/main/scala/li/cil/oc/common/block/Keyboard.scala index 7e88048574..aeabc49bb3 100644 --- a/src/main/scala/li/cil/oc/common/block/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/block/Keyboard.scala @@ -21,13 +21,17 @@ import net.minecraft.item.BlockItemUseContext import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.state.StateContainer import net.minecraft.world.IBlockReader import net.minecraft.world.IWorldReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5).noOcclusion()) extends SimpleBlock(props) { // For Immibis Microblock support. @@ -38,25 +42,22 @@ class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5). // ----------------------------------------------------------------------- // - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = - world.getBlockEntity(pos) match { - case keyboard: tileentity.Keyboard => - val (pitch, yaw) = (keyboard.pitch, keyboard.yaw) - val (forward, up) = pitch match { - case side@(Direction.DOWN | Direction.UP) => (side, yaw) - case _ => (yaw, Direction.UP) - } - val side = forward.getRotation(up) - val sizes = Array(7f / 16f, 4f / 16f, 7f / 16f) - val x0 = -up.getStepX * sizes(1) - side.getStepX * sizes(2) - forward.getStepX * sizes(0) - val x1 = up.getStepX * sizes(1) + side.getStepX * sizes(2) - forward.getStepX * 0.5f - val y0 = -up.getStepY * sizes(1) - side.getStepY * sizes(2) - forward.getStepY * sizes(0) - val y1 = up.getStepY * sizes(1) + side.getStepY * sizes(2) - forward.getStepY * 0.5f - val z0 = -up.getStepZ * sizes(1) - side.getStepZ * sizes(2) - forward.getStepZ * sizes(0) - val z1 = up.getStepZ * sizes(1) + side.getStepZ * sizes(2) - forward.getStepZ * 0.5f - new AxisAlignedBB(x0, y0, z0, x1, y1, z1).move(0.5, 0.5, 0.5) - case _ => super.getBoundingBox(state, world, pos) + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = { + val (pitch, yaw) = (state.getValue(PropertyRotatable.Pitch), state.getValue(PropertyRotatable.Yaw)) + val (forward, up) = pitch match { + case side@(Direction.DOWN | Direction.UP) => (side, yaw) + case _ => (yaw, Direction.UP) } + val side = forward.getRotation(up) + val sizes = Array(7f / 16f, 4f / 16f, 7f / 16f) + val x0 = -up.getStepX * sizes(1) - side.getStepX * sizes(2) - forward.getStepX * sizes(0) + val x1 = up.getStepX * sizes(1) + side.getStepX * sizes(2) - forward.getStepX * 0.5f + val y0 = -up.getStepY * sizes(1) - side.getStepY * sizes(2) - forward.getStepY * sizes(0) + val y1 = up.getStepY * sizes(1) + side.getStepY * sizes(2) - forward.getStepY * 0.5f + val z0 = -up.getStepZ * sizes(1) - side.getStepZ * sizes(2) - forward.getStepZ * sizes(0) + val z1 = up.getStepZ * sizes(1) + side.getStepZ * sizes(2) - forward.getStepZ * 0.5f + VoxelShapes.box(0.5 + x0, 0.5 + y0, 0.5 + z0, 0.5 + x1, 0.5 + y1, 0.5 + z1) + } // ----------------------------------------------------------------------- // From de14235173c8b05a84ea169540e3278d417cec60 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 9 Aug 2022 19:51:01 +0200 Subject: [PATCH 042/159] Fix screens not connecting visually --- .../scala/li/cil/oc/common/tileentity/Screen.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 8c9b9f183e..9f70f51133 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -221,6 +221,7 @@ class Screen(selfType: TileEntityType[_ <: Screen], var tier: Int) extends TileE queue += screen } } + if (isClient) updateMergedModels() // Update visibility after everything is done, to avoid noise. queue.foreach(screen => { val buffer = screen.buffer @@ -259,6 +260,16 @@ class Screen(selfType: TileEntityType[_ <: Screen], var tier: Int) extends TileE } } + private def updateMergedModels() { + if (getLevel == Minecraft.getInstance.level) { + val renderer = Minecraft.getInstance.levelRenderer + screens.foreach(screen => { + val pos = screen.getBlockPos + renderer.setSectionDirty(pos.getX >> 4, pos.getY >> 4, pos.getZ >> 4) + }) + } + } + private def isClientReadyForMultiBlockCheck = if (delayUntilCheckForMultiBlock > 0) { delayUntilCheckForMultiBlock -= 1 false From f1b26aadf4f79d3e5511f69a5b586d9b0bd0d104 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 10 Aug 2022 21:52:16 +0200 Subject: [PATCH 043/159] Remove SimpleComponent functionality The class transformer is gone, and OC doesn't use it --- .../cil/oc/api/network/SimpleComponent.java | 111 ------------------ .../asm/SimpleComponentTickHandler.java | 55 --------- .../asm/template/SimpleComponentImpl.java | 30 ----- .../asm/template/SimpleEnvironment.java | 91 -------------- .../asm/template/StaticSimpleEnvironment.java | 92 --------------- .../assets/opencomputers/lang/de_de.json | 1 - .../assets/opencomputers/lang/en_us.json | 1 - .../assets/opencomputers/lang/fr_fr.json | 1 - .../assets/opencomputers/lang/pt_br.json | 1 - .../assets/opencomputers/lang/ru_ru.json | 1 - .../assets/opencomputers/lang/tr_tr.json | 1 - .../assets/opencomputers/lang/zh_cn.json | 1 - .../assets/opencomputers/lang/zh_tw.json | 1 - src/main/scala/li/cil/oc/Localization.scala | 2 - .../opencomputers/ModOpenComputers.scala | 2 - 15 files changed, 391 deletions(-) delete mode 100644 src/main/java/li/cil/oc/api/network/SimpleComponent.java delete mode 100644 src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java delete mode 100644 src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java delete mode 100644 src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java delete mode 100644 src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java diff --git a/src/main/java/li/cil/oc/api/network/SimpleComponent.java b/src/main/java/li/cil/oc/api/network/SimpleComponent.java deleted file mode 100644 index e59e58654d..0000000000 --- a/src/main/java/li/cil/oc/api/network/SimpleComponent.java +++ /dev/null @@ -1,111 +0,0 @@ -package li.cil.oc.api.network; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This interface can be used to easily convert tile entities to components, - * without having to implement {@link li.cil.oc.api.network.Environment} - * themselves. The simple implementation will provide no access to OC's internal - * component network, since you won't have access to the node representing the - * tile entity. Use this only for simple cases, where you want to expose a - * couple of methods to the programs running computers. - *

    - * Classes implementing this interface will be expanded with the methods - * required for them to function as native block components (say, like the - * screen or keyboard). This means functions in the Environment - * interface have to created using a class transformer. If any of the methods - * already exist, this will fail! If things don't work, check your logs, first. - *

    - * To expose methods to OC, tag them with {@link li.cil.oc.api.machine.Callback} - * and have them use the according signature (see the documentation on the - * Callback annotation). - *

    - * Alternatively, implement {@link li.cil.oc.api.network.ManagedPeripheral} in - * addition to this interface, to make methods available ComputerCraft style. - *

    - * So, in short: - *

      - *
    • Implement this interface on a tile entity that should expose - * methods to computers.
    • - *
    • Annotate methods with Callback so they exported.
    • - *
    • Alternatively/additionally implement ManagedPeripheral to - * provide methods via a list of names and single callback method.
    • - *
    - *

    - * For example: - *

    - *     {@literal @}Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers")
    - *     public class TileEntityMyFancyThing extends TileEntity
    - *            implements SimpleComponent
    - *     {
    - *         {@literal @}Override
    - *         public String getComponentName() {
    - *             return "fancy_thing";
    - *         }
    - *
    - *         {@literal @}Callback
    - *         {@literal @}Optional.Method(modid = "OpenComputers")
    - *         public Object[] greet(Context context, Arguments args) {
    - *             return new Object[]{String.format("Hello, %s!", args.checkString(0))};
    - *         }
    - *     }
    - * 
    - * Using the alternative method to provide methods: - *
    - *     {@literal @}Optional.InterfaceList({
    - *         {@literal @}Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers"),
    - *         {@literal @}Optional.Interface(iface = "li.cil.oc.api.network.ManagedPeripheral", modid = "OpenComputers")
    - *     })
    - *     public class TileEntityMyFancyThing extends TileEntity
    - *            implements SimpleComponent, ManagedPeripheral
    - *     {
    - *         {@literal @}Override
    - *         public String getComponentName() {
    - *             return "fancy_thing";
    - *         }
    - *
    - *         public String[] methods() {
    - *             return new String[] {"greet"};
    - *         }
    - *
    - *         {@literal @}Optional.Method(modid = "OpenComputers")
    - *         public Object[] invoke(String method, Context context, Arguments args) {
    - *             if ("greet".equals(method)) {
    - *                 return new Object[]{String.format("Hello, %s!", args.checkString(0))};
    - *             } else {
    - *                 throw new NoSuchMethodException();
    - *             }
    - *         }
    - *     }
    - * 
    - */ -@Deprecated -public interface SimpleComponent { - /** - * The name the component should be made available as. - *

    component.list() in Lua, for - * example. You'll want to make this short and descriptive. The convention - * for component names is: all lowercase, underscores where necessary. Good - * component names are for example: disk_drive, furnace, crafting_table. - * - * @return the component's name. - */ - String getComponentName(); - - /** - * Use this to skip logic injection for the class this is implemented by. - *

    - * For example, if you have a class transformer that injects logic from a - * template class into your actual tile entities, OC's class transformer - * would complain when it finds the interface on the template class. That - * warning can be suppressed by using this annotation on the template. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @interface SkipInjection { - } -} diff --git a/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java b/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java deleted file mode 100644 index 0d1ab88e82..0000000000 --- a/src/main/java/li/cil/oc/common/asm/SimpleComponentTickHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package li.cil.oc.common.asm; - -import li.cil.oc.api.Network; -import li.cil.oc.util.SideTracker; -import net.minecraft.tileentity.TileEntity; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.event.TickEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.ArrayList; - -// This class is used for adding simple components to the component network. -// It is triggered from a validate call, and executed in the next update tick. -public final class SimpleComponentTickHandler { - private static final Logger log = LogManager.getLogger("OpenComputers"); - - public static final ArrayList pending = new java.util.ArrayList(); - - public static final SimpleComponentTickHandler Instance = new SimpleComponentTickHandler(); - - private SimpleComponentTickHandler() { - } - - public static void schedule(final TileEntity tileEntity) { - if (SideTracker.isServer()) { - synchronized (pending) { - pending.add(new Runnable() { - @Override - public void run() { - Network.joinOrCreateNetwork(tileEntity); - } - }); - } - } - } - - @SubscribeEvent - public void onTick(TickEvent.ServerTickEvent e) { - if (e.phase == TickEvent.Phase.START) { - final Runnable[] adds; - synchronized (pending) { - adds = pending.toArray(new Runnable[pending.size()]); - pending.clear(); - } - for (Runnable runnable : adds) { - try { - runnable.run(); - } catch (Throwable t) { - log.warn("Error in scheduled tick action.", t); - } - } - } - } -} diff --git a/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java b/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java deleted file mode 100644 index 2d5631b399..0000000000 --- a/src/main/java/li/cil/oc/common/asm/template/SimpleComponentImpl.java +++ /dev/null @@ -1,30 +0,0 @@ -package li.cil.oc.common.asm.template; - -import li.cil.oc.api.network.Environment; -import li.cil.oc.api.network.SimpleComponent; -import net.minecraft.block.BlockState; -import net.minecraft.nbt.CompoundNBT; - -/** - * This interface defines the names to which existing or placeholders for - * existing methods will be moved. This allows transparent injection of our - * functionality, i.e. existing clearRemoved() etc. methods will be called as - * if we didn't inject our code. - *

    - * Yes, the names are not "conventional", but that is by design, to avoid - * naming collisions. - */ -@Deprecated -public interface SimpleComponentImpl extends Environment, SimpleComponent { - public static final String PostFix = "_OpenComputers"; - - void validate_OpenComputers(); - - void invalidate_OpenComputers(); - - void onChunkUnloaded_OpenComputers(); - - void readFromNBT_OpenComputers(BlockState state, CompoundNBT nbt); - - CompoundNBT writeToNBT_OpenComputers(CompoundNBT nbt); -} diff --git a/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java b/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java deleted file mode 100644 index 6697b85523..0000000000 --- a/src/main/java/li/cil/oc/common/asm/template/SimpleEnvironment.java +++ /dev/null @@ -1,91 +0,0 @@ -package li.cil.oc.common.asm.template; - -import li.cil.oc.api.network.Message; -import li.cil.oc.api.network.Node; -import net.minecraft.block.BlockState; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.tileentity.TileEntityType; - -// This is a template implementation of methods injected into classes that are -// marked for component functionality. These methods will be copied into tile -// entities marked as simple components as necessary by the class transformer. -@SuppressWarnings("unused") -public abstract class SimpleEnvironment extends TileEntity implements SimpleComponentImpl { - public SimpleEnvironment(TileEntityType type) { - super(type); - } - - @Override - public Node node() { - return StaticSimpleEnvironment.node(this); - } - - @Override - public void onConnect(Node node) { - } - - @Override - public void onDisconnect(Node node) { - } - - @Override - public void onMessage(Message message) { - } - - // These are always injected, after possibly existing versions have been - // renamed to the below variants from the SimpleComponentImpl interface. - // This allows transparent wrapping of already present implementations, - // instead of plain overwriting them. - - @Override - public void clearRemoved() { - StaticSimpleEnvironment.validate(this); - } - - @Override - public void setRemoved() { - StaticSimpleEnvironment.invalidate(this); - } - - @Override - public void onChunkUnloaded() { - StaticSimpleEnvironment.onChunkUnloaded(this); - } - - @Override - public void load(BlockState state, CompoundNBT nbt) { - StaticSimpleEnvironment.load(this, state, nbt); - } - - @Override - public CompoundNBT save(CompoundNBT nbt) { - return StaticSimpleEnvironment.save(this, nbt); - } - - // The following methods are only injected if their real versions do not - // exist in the class we're injecting into. Otherwise their real versions - // are renamed to these variations, which simply delegate to the parent. - // This way they are always guaranteed to be present, so we can simply call - // them through an interface, and need no runtime reflection. - - public void validate_OpenComputers() { - super.clearRemoved(); - } - - public void invalidate_OpenComputers() { - super.setRemoved(); - } - - public void onChunkUnloaded_OpenComputers() { - super.onChunkUnloaded(); - } - - public void readFromNBT_OpenComputers(BlockState state, CompoundNBT nbt) { - super.load(state, nbt); - } - - public CompoundNBT writeToNBT_OpenComputers(CompoundNBT nbt) { - return super.save(nbt); - } -} diff --git a/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java b/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java deleted file mode 100644 index 3d5eb86933..0000000000 --- a/src/main/java/li/cil/oc/common/asm/template/StaticSimpleEnvironment.java +++ /dev/null @@ -1,92 +0,0 @@ -package li.cil.oc.common.asm.template; - -import com.google.common.base.Strings; -import li.cil.oc.api.Network; -import li.cil.oc.api.network.Environment; -import li.cil.oc.api.network.Node; -import li.cil.oc.api.network.Visibility; -import li.cil.oc.common.asm.SimpleComponentTickHandler; -import li.cil.oc.util.SideTracker; -import net.minecraft.block.BlockState; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.tileentity.TileEntity; - -import java.util.HashMap; -import java.util.Map; - -// This class contains actual implementations of methods injected into tile -// entities marked as simple components using the SimpleComponent interface. -// They are called from the template methods, to keep the injected methods -// minimal, instruction wise, and avoid weird dependencies making the injection -// unnecessarily complicated. -public final class StaticSimpleEnvironment { - private StaticSimpleEnvironment() { - } - - private static final Map nodes = new HashMap(); - - public static Node node(final SimpleComponentImpl self) { - // Save ourselves the lookup time in the hash map and avoid mixing in - // client side tile entities into the map when in single player. - if (SideTracker.isClient()) { - return null; - } - final String name = self.getComponentName(); - // If the name is null (or empty) this indicates we don't have a valid - // component right now, so if we have a node we kill it. - if (Strings.isNullOrEmpty(name)) { - final Node node = nodes.remove(self); - if (node != null) { - node.remove(); - } - } else if (!nodes.containsKey(self)) { - nodes.put(self, Network. - newNode(self, Visibility.Network). - withComponent(name). - create()); - } - return nodes.get(self); - } - - public static void validate(final SimpleComponentImpl self) { - self.validate_OpenComputers(); - SimpleComponentTickHandler.schedule((TileEntity) self); - } - - public static void invalidate(final SimpleComponentImpl self) { - self.invalidate_OpenComputers(); - final Node node = node(self); - if (node != null) { - node.remove(); - nodes.remove(self); - } - } - - public static void onChunkUnloaded(final SimpleComponentImpl self) { - self.onChunkUnloaded_OpenComputers(); - final Node node = node(self); - if (node != null) { - node.remove(); - nodes.remove(self); - } - } - - public static void load(final SimpleComponentImpl self, BlockState state, CompoundNBT nbt) { - self.readFromNBT_OpenComputers(state, nbt); - final Node node = node(self); - if (node != null) { - node.loadData(nbt.getCompound("oc:node")); - } - } - - public static CompoundNBT save(final SimpleComponentImpl self, CompoundNBT nbt) { - nbt = self.writeToNBT_OpenComputers(nbt); - final Node node = node(self); - if (node != null) { - final CompoundNBT nodeNbt = new CompoundNBT(); - node.saveData(nodeNbt); - nbt.put("oc:node", nodeNbt); - } - return nbt; - } -} diff --git a/src/main/resources/assets/opencomputers/lang/de_de.json b/src/main/resources/assets/opencomputers/lang/de_de.json index 17a71d3fec..346c0c8700 100644 --- a/src/main/resources/assets/opencomputers/lang/de_de.json +++ b/src/main/resources/assets/opencomputers/lang/de_de.json @@ -188,7 +188,6 @@ "oc:gui.Chat.WarningLuaFallback": "Die native Lua-Implementierung ist nicht verfügbar. Computer können ihren Ausführungszustand nicht speichern. Sie werden automatisch neu starten, sobald ein Chunk neu geladen wird.", "oc:gui.Chat.WarningProjectRed": "Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red.", "oc:gui.Chat.WarningRecipes": "Es gab Fehler beim Laden eines oder mehrerer Rezepte. Einige Gegenstände können unter Umständen nicht gefertigt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen.", - "oc:gui.Chat.WarningSimpleComponent": "Eine Erweiterung (deine?) welche das §aSimpleComponent§f-Interface verwendet, hat etwas §efalsch gemacht§f. Komponentenlogik konnte nicht eingefügt werden. Bitte wirf einen Blick in deine Log-Datei für weitere Informationen.", "oc:gui.Drive.Managed": "Managed", "oc:gui.Drive.Unmanaged": "Unmanaged", "oc:gui.Drive.ReadOnlyLock": "Sperre", diff --git a/src/main/resources/assets/opencomputers/lang/en_us.json b/src/main/resources/assets/opencomputers/lang/en_us.json index 8fb23ab819..c995688edc 100644 --- a/src/main/resources/assets/opencomputers/lang/en_us.json +++ b/src/main/resources/assets/opencomputers/lang/en_us.json @@ -189,7 +189,6 @@ "oc:gui.Chat.WarningLuaFallback": "Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads.", "oc:gui.Chat.WarningProjectRed": "You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red.", "oc:gui.Chat.WarningRecipes": "There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information.", - "oc:gui.Chat.WarningSimpleComponent": "An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information.", "oc:gui.Drive.Managed": "Managed", "oc:gui.Drive.Unmanaged": "Unmanaged", "oc:gui.Drive.ReadOnlyLock": "Lock", diff --git a/src/main/resources/assets/opencomputers/lang/fr_fr.json b/src/main/resources/assets/opencomputers/lang/fr_fr.json index ac98b76c96..27a6159bcd 100644 --- a/src/main/resources/assets/opencomputers/lang/fr_fr.json +++ b/src/main/resources/assets/opencomputers/lang/fr_fr.json @@ -178,7 +178,6 @@ "oc:gui.Chat.WarningLuaFallback": "Les bibliothèques natives Lua ne sont pas disponibles, les ordinateurs ne pourront pas persister en leur état. Ils réamorceront sur la recharge des chunks.", "oc:gui.Chat.WarningProjectRed": "Vous utilisez une version de Project: Red qui est incompatible avec OpenComputers. Essayez de mettre à jour votre version de Project: Red.", "oc:gui.Chat.WarningRecipes": "il y a des erreurs au lancement de recettes. Certains éléments doivent être infabricables. Veuillez vérifier votre fichier log pour plus d'information.", - "oc:gui.Chat.WarningSimpleComponent": "Un ajout (le votre ?) utilisant l'interface §aComposant Simple§f produit §equelquechose de mauvais§f. Le composant logique ne peut être inséré. Veuillez vérifier votre fichier log pour plus d'information.", "oc:gui.Error.ComponentOverflow": "Beaucoup trop de composants connectés à l'ordinateur.", "oc:gui.Error.InternalError": "Erreur interne, Veuillez lire le fichier log. Ceci est probablement une erreur.", "oc:gui.Error.NoCPU": "Pas d'UCP installé dans cet ordinateur.", diff --git a/src/main/resources/assets/opencomputers/lang/pt_br.json b/src/main/resources/assets/opencomputers/lang/pt_br.json index e44e35451a..dc724a281a 100644 --- a/src/main/resources/assets/opencomputers/lang/pt_br.json +++ b/src/main/resources/assets/opencomputers/lang/pt_br.json @@ -190,7 +190,6 @@ "oc:gui.Chat.WarningLuaFallback": "Bibliotecas nativas do Lua não disponíveis, computadores não serão capazes de persistir seus estados. Eles reinicializarão quando chunks descarregarem.", "oc:gui.Chat.WarningProjectRed": "Você está usando uma versão do Project: Red que é incompativel com OpenComputers. Tente atualizar sua versão do Project: Red.", "oc:gui.Chat.WarningRecipes": "Houve erros ao carregar uma ou mais receitas. Alguns itens não poderão ser construídos. Verifique seu arquivo de log para mais informações.", - "oc:gui.Chat.WarningSimpleComponent": "Um addon (seu?) usa a interface §aSimpleComponent§f que faz §ealgo errado§f. A lógica de componente não pode ser injetada. Por favor verifique seu arquivo de log para mais informações.", "oc:gui.Drive.Managed": "Gerenciado", "oc:gui.Drive.Unmanaged": "Não Gerenciado", "oc:gui.Drive.ReadOnlyLock": "Bloqueado", diff --git a/src/main/resources/assets/opencomputers/lang/ru_ru.json b/src/main/resources/assets/opencomputers/lang/ru_ru.json index ed534742d1..b5942f9240 100644 --- a/src/main/resources/assets/opencomputers/lang/ru_ru.json +++ b/src/main/resources/assets/opencomputers/lang/ru_ru.json @@ -188,7 +188,6 @@ "oc:gui.Chat.WarningLuaFallback": "Исходные библиотеки Lua не доступны, компьютеры не смогут сохранять своё состояние. Они перезагрузятся после выгрузки чанка.", "oc:gui.Chat.WarningProjectRed": "Вы используете версию Project: Red, несовместимую с OpenComputers. Попробуйте обновить Project: Red.", "oc:gui.Chat.WarningRecipes": "Произошла ошибка при загрузке одного или нескольких рецептов. Некоторые предметы могут не крафтиться. Проверьте логи, чобы узнать подробнее.", - "oc:gui.Chat.WarningSimpleComponent": "Некоторый мод §eневерно§f использует интрефейс §aSimpleComponent§f. Логика компонента не смогла быть внедрена. Проверьте логи, чобы узнать подробнее.", "oc:gui.Drive.Managed": "Файловый режим", "oc:gui.Drive.Unmanaged": "Блочный режим", "oc:gui.Drive.Warning": "§lВнимание§r: переключение режимов очищает диск!", diff --git a/src/main/resources/assets/opencomputers/lang/tr_tr.json b/src/main/resources/assets/opencomputers/lang/tr_tr.json index 3d9b0dc992..17fc3d3327 100644 --- a/src/main/resources/assets/opencomputers/lang/tr_tr.json +++ b/src/main/resources/assets/opencomputers/lang/tr_tr.json @@ -188,7 +188,6 @@ "oc:gui.Chat.WarningLuaFallback": "Orijinal Lua kütüphaneleri bulunamadı ve bilgisayarlar durumlarını koruyamayacak. Bölümler yeniden yüklenince bilgisayarlar yeniden başlayacak. (Yedek kütüphaneler kullanılıyor)", "oc:gui.Chat.WarningProjectRed": "OpenComputers'ı desteklemeyen bir Project: Red sürümünü kullanıyorsunuz. Project: Red'i güncellemeyi deneyin.", "oc:gui.Chat.WarningRecipes": "Bir veya birden fazla tarifi yüklerken hata oluştu. Bazı nesneler üretilemeyebilir. Daha fazla bilgi için kayıt defteri dosyanızı bakın.", - "oc:gui.Chat.WarningSimpleComponent": " §aSimpleComponent§f arayüzünü kullanan bir mod eklentisi (acep sizin mi?) §eyanlış bir şeyler§f yaptı. Parça mantığı enjekte edilemedi. Daha fazla bilgi için kayıt defteri dosyanıza bakın.", "oc:gui.Drive.Managed": "Denetimli", "oc:gui.Drive.Unmanaged": "Denetimsiz", "oc:gui.Drive.Warning": "§lUyarı§r: kip değiştirmek disk üzerindeki bütün verinin kaybına sebep olur.", diff --git a/src/main/resources/assets/opencomputers/lang/zh_cn.json b/src/main/resources/assets/opencomputers/lang/zh_cn.json index 5b1af1af02..e98748647e 100644 --- a/src/main/resources/assets/opencomputers/lang/zh_cn.json +++ b/src/main/resources/assets/opencomputers/lang/zh_cn.json @@ -188,7 +188,6 @@ "oc:gui.Chat.WarningLuaFallback": "无法使用原生 Lua 库,电脑无法持久化当前状态。它们会在区块载入时重启。", "oc:gui.Chat.WarningProjectRed": "你目前使用的 Project:Red 版本不兼容 OpenComputers,请更新 Project:Red。", "oc:gui.Chat.WarningRecipes": "加载合成表时遇到一个或多个错误。部分物品或因此无法合成。查阅日志文件以获取详细信息。", - "oc:gui.Chat.WarningSimpleComponent": "某个扩展 Mod 使用了 §aSimpleComponent§f 接口但§e做错了一些事情§f,导致组件逻辑无法注入。查阅日志文件以获取详细信息。", "oc:gui.Drive.Managed": "受管理", "oc:gui.Drive.Unmanaged": "不受管理", "oc:gui.Drive.ReadOnlyLock": "只读锁定", diff --git a/src/main/resources/assets/opencomputers/lang/zh_tw.json b/src/main/resources/assets/opencomputers/lang/zh_tw.json index 7cc31e1464..bdc095b6db 100644 --- a/src/main/resources/assets/opencomputers/lang/zh_tw.json +++ b/src/main/resources/assets/opencomputers/lang/zh_tw.json @@ -183,7 +183,6 @@ "oc:gui.Chat.WarningLuaFallback": "本機 LUA 函式庫無法使用,電腦將無法維持狀態,他會重新啟動並且載入區塊", "oc:gui.Chat.WarningProjectRed": "你正在使用的 Project: Red 模組與 OpenComputers 不相容. 請嘗試更新 Project: Red.", "oc:gui.Chat.WarningRecipes": "There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information.", - "oc:gui.Chat.WarningSimpleComponent": "An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information.", "oc:gui.Drive.Managed": "受管", "oc:gui.Drive.Unmanaged": "非受管", "oc:gui.Drive.Warning": "§l警告§r: 切換模式會導致目前儲存在硬碟上的所有資料遺失!", diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index 2533be739d..6d83b8977c 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -95,8 +95,6 @@ object Localization { def WarningClassTransformer: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningClassTransformer")) - def WarningSimpleComponent: ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningSimpleComponent")) - def WarningLink(url: String): ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.WarningLink", url)) def InfoNewVersion(version: String): ITextComponent = new StringTextComponent("§aOpenComputers§f: ").append(localizeLater("gui.Chat.NewVersion", version)) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index 935b237525..c0c777ae77 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -20,7 +20,6 @@ import li.cil.oc.client.renderer.markdown.segment.render.TextureImageProvider import li.cil.oc.common.EventHandler import li.cil.oc.common.Loot import li.cil.oc.common.SaveHandler -import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.block.SimpleBlock import li.cil.oc.common.event._ import li.cil.oc.common.item.Analyzer @@ -95,7 +94,6 @@ object ModOpenComputers extends ModProxy { MinecraftForge.EVENT_BUS.register(EventHandler) MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Common) - MinecraftForge.EVENT_BUS.register(SimpleComponentTickHandler.Instance) MinecraftForge.EVENT_BUS.register(Tablet) MinecraftForge.EVENT_BUS.register(Analyzer) MinecraftForge.EVENT_BUS.register(AngelUpgradeHandler) From 10b11fc8020d5c284696f57680eaf5cf615db84e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 10 Aug 2022 23:42:04 +0200 Subject: [PATCH 044/159] Port drone leash offset --- src/main/scala/li/cil/oc/common/entity/Drone.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 881483abbe..b617befa80 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -53,6 +53,8 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.text.ITextComponent import net.minecraft.world.World import net.minecraft.world.server.ServerWorld +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.fluids.IFluidTank import net.minecraftforge.fml.network.NetworkHooks @@ -237,6 +239,10 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, override def markChanged() {} + @OnlyIn(Dist.CLIENT) + override def getRopeHoldPosition(dt: Float): Vector3d = + getPosition(dt).add(0.0, -0.056, 0.0) // Offset: height * 0.85 * 0.7 - 0.25 + // ----------------------------------------------------------------------- // override def facing = Direction.SOUTH From b138f807bf374f0c971eeb952dfad3672224fc67 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 14 Aug 2022 20:35:32 +0200 Subject: [PATCH 045/159] Create types for containers --- .../scala/li/cil/oc/client/gui/Adapter.scala | 2 +- .../li/cil/oc/client/gui/Assembler.scala | 2 +- .../scala/li/cil/oc/client/gui/Case.scala | 2 +- .../scala/li/cil/oc/client/gui/Charger.scala | 2 +- .../scala/li/cil/oc/client/gui/Database.scala | 2 +- .../li/cil/oc/client/gui/Disassembler.scala | 2 +- .../li/cil/oc/client/gui/DiskDrive.scala | 2 +- .../scala/li/cil/oc/client/gui/Drone.scala | 2 +- .../scala/li/cil/oc/client/gui/Printer.scala | 2 +- .../scala/li/cil/oc/client/gui/Rack.scala | 2 +- .../scala/li/cil/oc/client/gui/Raid.scala | 2 +- .../scala/li/cil/oc/client/gui/Relay.scala | 2 +- .../scala/li/cil/oc/client/gui/Robot.scala | 2 +- .../scala/li/cil/oc/client/gui/Server.scala | 2 +- .../scala/li/cil/oc/client/gui/Tablet.scala | 2 +- .../scala/li/cil/oc/common/GuiHandler.scala | 37 ++++---- .../li/cil/oc/common/container/Adapter.scala | 7 +- .../cil/oc/common/container/Assembler.scala | 17 +++- .../li/cil/oc/common/container/Case.scala | 35 +++++--- .../li/cil/oc/common/container/Charger.scala | 6 +- .../oc/common/container/ContainerTypes.java | 86 +++++++++++++++++++ .../li/cil/oc/common/container/Database.scala | 12 ++- .../oc/common/container/Disassembler.scala | 11 ++- .../cil/oc/common/container/DiskDrive.scala | 5 +- .../li/cil/oc/common/container/Drone.scala | 7 +- .../li/cil/oc/common/container/Player.scala | 2 +- .../li/cil/oc/common/container/Printer.scala | 17 +++- .../li/cil/oc/common/container/Rack.scala | 35 +++++--- .../li/cil/oc/common/container/Raid.scala | 6 +- .../li/cil/oc/common/container/Relay.scala | 21 +++-- .../li/cil/oc/common/container/Robot.scala | 81 +++++++++++++---- .../li/cil/oc/common/container/Server.scala | 26 ++++-- .../li/cil/oc/common/container/Tablet.scala | 11 ++- 33 files changed, 341 insertions(+), 111 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/container/ContainerTypes.java diff --git a/src/main/scala/li/cil/oc/client/gui/Adapter.scala b/src/main/scala/li/cil/oc/client/gui/Adapter.scala index 715cab0b94..7f0cfb71c4 100644 --- a/src/main/scala/li/cil/oc/client/gui/Adapter.scala +++ b/src/main/scala/li/cil/oc/client/gui/Adapter.scala @@ -6,6 +6,6 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory class Adapter(id: Int, playerInventory: PlayerInventory, val adapter: tileentity.Adapter) - extends DynamicGuiContainer(new container.Adapter(id, playerInventory, adapter), + extends DynamicGuiContainer(new container.Adapter(container.ContainerTypes.ADAPTER, id, playerInventory, adapter), playerInventory, adapter.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 1024360359..a0cadcd69c 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -21,7 +21,7 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) - extends DynamicGuiContainer(new container.Assembler(id, playerInventory, assembler), + extends DynamicGuiContainer(new container.Assembler(container.ContainerTypes.ASSEMBLER, id, playerInventory, assembler), playerInventory, StringTextComponent.EMPTY) { imageWidth = 176 diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index e7bebb0e51..f6a2cb3394 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -14,7 +14,7 @@ import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ class Case(id: Int, playerInventory: PlayerInventory, val computer: tileentity.Case) - extends DynamicGuiContainer(new container.Case(id, playerInventory, computer), + extends DynamicGuiContainer(new container.Case(container.ContainerTypes.CASE, id, playerInventory, computer, computer.tier), playerInventory, computer.getName) { protected var powerButton: ImageButton = _ diff --git a/src/main/scala/li/cil/oc/client/gui/Charger.scala b/src/main/scala/li/cil/oc/client/gui/Charger.scala index 96cc61e011..9a19b8185a 100644 --- a/src/main/scala/li/cil/oc/client/gui/Charger.scala +++ b/src/main/scala/li/cil/oc/client/gui/Charger.scala @@ -6,6 +6,6 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory class Charger(id: Int, playerInventory: PlayerInventory, val charger: tileentity.Charger) - extends DynamicGuiContainer(new container.Charger(id, playerInventory, charger), + extends DynamicGuiContainer(new container.Charger(container.ContainerTypes.CHARGER, id, playerInventory, charger), playerInventory, charger.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 6cbc0f6a29..579dc558d2 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -10,7 +10,7 @@ import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.StringTextComponent class Database(id: Int, playerInventory: PlayerInventory, val databaseInventory: DatabaseInventory) - extends DynamicGuiContainer(new container.Database(id, playerInventory, databaseInventory), + extends DynamicGuiContainer(new container.Database(container.ContainerTypes.DATABASE, id, playerInventory, databaseInventory), playerInventory, StringTextComponent.EMPTY) with traits.LockedHotbar[container.Database] { diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index f8debe2a80..75423f967f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -10,7 +10,7 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory class Disassembler(id: Int, playerInventory: PlayerInventory, val disassembler: tileentity.Disassembler) - extends DynamicGuiContainer(new container.Disassembler(id, playerInventory, disassembler), + extends DynamicGuiContainer(new container.Disassembler(container.ContainerTypes.DISASSEMBLER, id, playerInventory, disassembler), playerInventory, disassembler.getName) { val progress = addCustomWidget(new ProgressBar(18, 65)) diff --git a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala index c5a88c11f2..f4bcb82a2b 100644 --- a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala @@ -7,5 +7,5 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory class DiskDrive(id: Int, playerInventory: PlayerInventory, val drive: SimpleInventory) - extends DynamicGuiContainer(new container.DiskDrive(id, playerInventory, drive), playerInventory, drive.getName) { + extends DynamicGuiContainer(new container.DiskDrive(container.ContainerTypes.DISK_DRIVE, id, playerInventory, drive), playerInventory, drive.getName) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 83ae228cb8..2aa16db70b 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -24,7 +24,7 @@ import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) - extends DynamicGuiContainer(new container.Drone(id, playerInventory, drone), + extends DynamicGuiContainer(new container.Drone(container.ContainerTypes.DRONE, id, playerInventory, drone.mainInventory), playerInventory, StringTextComponent.EMPTY) with traits.DisplayBuffer { diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index 5fa53572fd..5e4d2aaa2e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -12,7 +12,7 @@ import li.cil.oc.util.RenderState import net.minecraft.entity.player.PlayerInventory class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity.Printer) - extends DynamicGuiContainer(new container.Printer(id, playerInventory, printer), + extends DynamicGuiContainer(new container.Printer(container.ContainerTypes.PRINTER, id, playerInventory, printer), playerInventory, printer.getName) { imageWidth = 176 diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index 33d7c71afe..23e184eb96 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -19,7 +19,7 @@ import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) - extends DynamicGuiContainer(new container.Rack(id, playerInventory, rack), + extends DynamicGuiContainer(new container.Rack(container.ContainerTypes.RACK, id, playerInventory, rack), playerInventory, rack.getName) { imageHeight = 210 diff --git a/src/main/scala/li/cil/oc/client/gui/Raid.scala b/src/main/scala/li/cil/oc/client/gui/Raid.scala index cea87d4555..ab22e4e695 100644 --- a/src/main/scala/li/cil/oc/client/gui/Raid.scala +++ b/src/main/scala/li/cil/oc/client/gui/Raid.scala @@ -9,7 +9,7 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory class Raid(id: Int, playerInventory: PlayerInventory, val raid: tileentity.Raid) - extends DynamicGuiContainer(new container.Raid(id, playerInventory, raid), + extends DynamicGuiContainer(new container.Raid(container.ContainerTypes.RAID, id, playerInventory, raid), playerInventory, raid.getName) { override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala index 62dd995059..ea15d161cf 100644 --- a/src/main/scala/li/cil/oc/client/gui/Relay.scala +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -16,7 +16,7 @@ import net.minecraft.entity.player.PlayerInventory import org.lwjgl.opengl.GL11 class Relay(id: Int, playerInventory: PlayerInventory, val relay: tileentity.Relay) - extends DynamicGuiContainer(new container.Relay(id, playerInventory, relay), + extends DynamicGuiContainer(new container.Relay(container.ContainerTypes.RELAY, id, playerInventory, relay), playerInventory, relay.getName) { private val format = new DecimalFormat("#.##hz") diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 3d1ba96bd1..63623d69b7 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -28,7 +28,7 @@ import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Robot) - extends DynamicGuiContainer(new container.Robot(id, playerInventory, robot), + extends DynamicGuiContainer(new container.Robot(container.ContainerTypes.ROBOT, id, playerInventory, robot), playerInventory, StringTextComponent.EMPTY) with traits.InputBuffer with INestedGuiEventHandler { diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index 0fb265b9d0..5b51f8cd6f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -15,7 +15,7 @@ import net.minecraft.entity.player.PlayerInventory import scala.collection.JavaConverters.asJavaCollection class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val rack: Option[tileentity.Rack] = None, val slot: Int = 0) - extends DynamicGuiContainer(new container.Server(id, playerInventory, serverInventory), + extends DynamicGuiContainer(new container.Server(container.ContainerTypes.SERVER, id, playerInventory, serverInventory), playerInventory, serverInventory.getName) with traits.LockedHotbar[container.Server] { diff --git a/src/main/scala/li/cil/oc/client/gui/Tablet.scala b/src/main/scala/li/cil/oc/client/gui/Tablet.scala index a77c33b995..c7c2a85b6d 100644 --- a/src/main/scala/li/cil/oc/client/gui/Tablet.scala +++ b/src/main/scala/li/cil/oc/client/gui/Tablet.scala @@ -6,7 +6,7 @@ import li.cil.oc.common.item.TabletWrapper import net.minecraft.entity.player.PlayerInventory class Tablet(id: Int, playerInventory: PlayerInventory, val tablet: TabletWrapper) - extends DynamicGuiContainer(new container.Tablet(id, playerInventory, tablet), + extends DynamicGuiContainer(new container.Tablet(container.ContainerTypes.TABLET, id, playerInventory, tablet), playerInventory, tablet.getName) with traits.LockedHotbar[container.Tablet] { diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index 38e673dd52..348296abf9 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -1,5 +1,6 @@ package li.cil.oc.common +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventory, ServerInventory} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ @@ -16,54 +17,54 @@ abstract class GuiHandler { case Some(GuiType.Category.Block) => world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { case t: tileentity.Adapter if id == GuiType.Adapter.id => - new container.Adapter(containerId, player.inventory, t) + new container.Adapter(ContainerTypes.ADAPTER, containerId, player.inventory, t) case t: tileentity.Assembler if id == GuiType.Assembler.id => - new container.Assembler(containerId, player.inventory, t) + new container.Assembler(ContainerTypes.ASSEMBLER, containerId, player.inventory, t) case t: tileentity.Charger if id == GuiType.Charger.id => - new container.Charger(containerId, player.inventory, t) + new container.Charger(ContainerTypes.CHARGER, containerId, player.inventory, t) case t: tileentity.Case if id == GuiType.Case.id => - new container.Case(containerId, player.inventory, t) + new container.Case(ContainerTypes.CASE, containerId, player.inventory, t, t.tier) case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - new container.Disassembler(containerId, player.inventory, t) + new container.Disassembler(ContainerTypes.DISASSEMBLER, containerId, player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - new container.DiskDrive(containerId, player.inventory, t) + new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => - new container.Printer(containerId, player.inventory, t) + new container.Printer(ContainerTypes.PRINTER, containerId, player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => - new container.Raid(containerId, player.inventory, t) + new container.Raid(ContainerTypes.RAID, containerId, player.inventory, t) case t: tileentity.Relay if id == GuiType.Relay.id => - new container.Relay(containerId, player.inventory, t) + new container.Relay(ContainerTypes.RELAY, containerId, player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new container.Robot(containerId, player.inventory, t.robot) + new container.Robot(ContainerTypes.ROBOT, containerId, player.inventory, t.robot) case t: tileentity.Rack if id == GuiType.Rack.id => - new container.Rack(containerId, player.inventory, t) + new container.Rack(ContainerTypes.RACK, containerId, player.inventory, t) case t: tileentity.Rack if id == GuiType.ServerInRack.id => val slot = GuiType.extractSlot(y) val server = t.getMountable(slot).asInstanceOf[Server] - new container.Server(containerId, player.inventory, server, Option(server)) + new container.Server(ContainerTypes.SERVER, containerId, player.inventory, server, Option(server)) case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => val slot = GuiType.extractSlot(y) val drive = t.getMountable(slot).asInstanceOf[DiskDriveMountable] - new container.DiskDrive(containerId, player.inventory, drive) + new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, drive) case _ => null } case Some(GuiType.Category.Entity) => world.getEntity(x) match { case drone: entity.Drone if id == GuiType.Drone.id => - new container.Drone(containerId, player.inventory, drone) + new container.Drone(ContainerTypes.DRONE, containerId, player.inventory, drone.mainInventory) case _ => null } case Some(GuiType.Category.Item) => { val itemStackInUse = getItemStackInUse(id, player) itemStackInUse.getItem match { case database: item.UpgradeDatabase if id == GuiType.Database.id => - new container.Database(containerId, player.inventory, new DatabaseInventory { + new container.Database(ContainerTypes.DATABASE, containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) case server: item.Server if id == GuiType.Server.id => - new container.Server(containerId, player.inventory, new ServerInventory { + new container.Server(ContainerTypes.SERVER, containerId, player.inventory, new ServerInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player @@ -71,11 +72,11 @@ abstract class GuiHandler { case tablet: item.Tablet if id == GuiType.TabletInner.id => val stack = itemStackInUse if (stack.hasTag) - new container.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) + new container.Tablet(ContainerTypes.TABLET, containerId, player.inventory, item.Tablet.get(stack, player)) else null case drive: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => - new container.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { + new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, new DiskDriveMountableInventory { override def container: ItemStack = itemStackInUse override def stillValid(player: PlayerEntity) = player == player diff --git a/src/main/scala/li/cil/oc/common/container/Adapter.scala b/src/main/scala/li/cil/oc/common/container/Adapter.scala index bc4ab2200b..f1922d2f6c 100644 --- a/src/main/scala/li/cil/oc/common/container/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/container/Adapter.scala @@ -1,10 +1,13 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType + +class Adapter(selfType: ContainerType[_ <: Adapter], id: Int, playerInventory: PlayerInventory, adapter: IInventory) + extends Player(selfType, id, playerInventory, adapter) { -class Adapter(id: Int, playerInventory: PlayerInventory, adapter: tileentity.Adapter) extends Player(null, id, playerInventory, adapter) { addSlotToContainer(80, 35, Slot.Upgrade) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index 5d18366cf6..e17b5906c0 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -5,12 +5,16 @@ import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.common.tileentity +import net.minecraft.inventory.container.ContainerType import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory import net.minecraft.nbt.CompoundNBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) extends Player(null, id, playerInventory, assembler) { +class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventory: PlayerInventory, val assembler: IInventory) + extends Player(selfType, id, playerInventory, assembler) { + // Computer case. { val index = slots.size @@ -74,9 +78,14 @@ class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileen def assemblyRemainingTime = synchronizedData.getInt("assemblyRemainingTime") override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { - synchronizedData.putBoolean("isAssembling", assembler.isAssembling) - synchronizedData.putDouble("assemblyProgress", assembler.progress) - synchronizedData.putInt("assemblyRemainingTime", assembler.timeRemaining) + assembler match { + case te: tileentity.Assembler => { + synchronizedData.putBoolean("isAssembling", te.isAssembling) + synchronizedData.putDouble("assemblyProgress", te.progress) + synchronizedData.putInt("assemblyRemainingTime", te.timeRemaining) + } + case _ => + } super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Case.scala b/src/main/scala/li/cil/oc/common/container/Case.scala index 9520252a1e..c736da0dea 100644 --- a/src/main/scala/li/cil/oc/common/container/Case.scala +++ b/src/main/scala/li/cil/oc/common/container/Case.scala @@ -5,41 +5,45 @@ import li.cil.oc.common.Tier import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.util.text.ITextComponent -class Case(id: Int, playerInventory: PlayerInventory, computer: tileentity.Case) extends Player(null, id, playerInventory, computer) { - for (i <- 0 to (if (computer.tier >= Tier.Three) 2 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getItems.size) +class Case(selfType: ContainerType[_ <: Case], id: Int, playerInventory: PlayerInventory, computer: IInventory, tier: Int) + extends Player(selfType, id, playerInventory, computer) { + + for (i <- 0 to (if (tier >= Tier.Three) 2 else 1)) { + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(98, 16 + i * slotSize, slot.slot, slot.tier) } - for (i <- 0 to (if (computer.tier == Tier.One) 0 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + for (i <- 0 to (if (tier == Tier.One) 0 else 1)) { + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(120, 16 + (i + 1) * slotSize, slot.slot, slot.tier) } - for (i <- 0 to (if (computer.tier == Tier.One) 0 else 1)) { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + for (i <- 0 to (if (tier == Tier.One) 0 else 1)) { + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(142, 16 + i * slotSize, slot.slot, slot.tier) } - if (computer.tier >= Tier.Three) { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + if (tier >= Tier.Three) { + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(142, 16 + 2 * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(120, 16, slot.slot, slot.tier) } - if (computer.tier == Tier.One) { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + if (tier == Tier.One) { + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(120, 16 + 2 * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.computer(computer.tier)(getItems.size) + val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(48, 34, slot.slot, slot.tier) } @@ -47,5 +51,8 @@ class Case(id: Int, playerInventory: PlayerInventory, computer: tileentity.Case) addPlayerInventorySlots(8, 84) override def stillValid(player: PlayerEntity) = - super.stillValid(player) && computer.canInteract(player.getName.getString) + super.stillValid(player) && (computer match { + case te: tileentity.Case => te.canInteract(player.getName.getString) + case _ => true + }) } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/container/Charger.scala b/src/main/scala/li/cil/oc/common/container/Charger.scala index 4adf029aa1..1decd65607 100644 --- a/src/main/scala/li/cil/oc/common/container/Charger.scala +++ b/src/main/scala/li/cil/oc/common/container/Charger.scala @@ -2,8 +2,12 @@ package li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType + +class Charger(selfType: ContainerType[_ <: Charger], id: Int, playerInventory: PlayerInventory, charger: IInventory) + extends Player(selfType, id, playerInventory, charger) { -class Charger(id: Int, playerInventory: PlayerInventory, charger: tileentity.Charger) extends Player(null, id, playerInventory, charger) { addSlotToContainer(80, 35, "tablet") addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java new file mode 100644 index 0000000000..7d54a64e10 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java @@ -0,0 +1,86 @@ +package li.cil.oc.common.container; + +import li.cil.oc.OpenComputers; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.extensions.IForgeContainerType; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.fml.network.IContainerFactory; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.ObjectHolder; + +@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) +@ObjectHolder("opencomputers") +public final class ContainerTypes { + public static final ContainerType ADAPTER = null; + public static final ContainerType ASSEMBLER = null; + public static final ContainerType CASE = null; + public static final ContainerType CHARGER = null; + public static final ContainerType DATABASE = null; + public static final ContainerType DISASSEMBLER = null; + public static final ContainerType DISK_DRIVE = null; + public static final ContainerType DRONE = null; + public static final ContainerType PRINTER = null; + public static final ContainerType RACK = null; + public static final ContainerType RAID = null; + public static final ContainerType RELAY = null; + public static final ContainerType ROBOT = null; + public static final ContainerType SERVER = null; + public static final ContainerType TABLET = null; + + @SubscribeEvent + public static void registerContainers(RegistryEvent.Register> e) { + register(e.getRegistry(), "adapter", (id, plr, buff) -> new Adapter(ADAPTER, id, plr, new Inventory(1))); + register(e.getRegistry(), "assembler", (id, plr, buff) -> new Assembler(ASSEMBLER, id, plr, new Inventory(22))); + register(e.getRegistry(), "case", (id, plr, buff) -> { + int invSize = buff.readVarInt(); + int tier = buff.readVarInt(); + return new Case(CASE, id, plr, new Inventory(invSize), tier); + }); + register(e.getRegistry(), "charger", (id, plr, buff) -> new Charger(CHARGER, id, plr, new Inventory(1))); + register(e.getRegistry(), "database", (id, plr, buff) -> { + int invSize = buff.readVarInt(); + int tier = buff.readVarInt(); + return new Database(DATABASE, id, plr, new Inventory(invSize), tier); + }); + register(e.getRegistry(), "disassembler", (id, plr, buff) -> new Disassembler(DISASSEMBLER, id, plr, new Inventory(1))); + register(e.getRegistry(), "disk_drive", (id, plr, buff) -> new DiskDrive(DISK_DRIVE, id, plr, new Inventory(1))); + register(e.getRegistry(), "drone", (id, plr, buff) -> { + int invSize = buff.readVarInt(); + return new Drone(DRONE, id, plr, new Inventory(invSize)); + }); + register(e.getRegistry(), "printer", (id, plr, buff) -> new Printer(PRINTER, id, plr, new Inventory(3))); + register(e.getRegistry(), "rack", (id, plr, buff) -> new Rack(RACK, id, plr, new Inventory(4))); + register(e.getRegistry(), "raid", (id, plr, buff) -> new Raid(RAID, id, plr, new Inventory(3))); + register(e.getRegistry(), "relay", (id, plr, buff) -> new Relay(RELAY, id, plr, new Inventory(4))); + register(e.getRegistry(), "robot", (id, plr, buff) -> { + RobotInfo info = RobotInfo$.MODULE$.readRobotInfo(buff); + return new Robot(ROBOT, id, plr, new Inventory(100), info); + }); + register(e.getRegistry(), "server", (id, plr, buff) -> { + int invSize = buff.readVarInt(); + int tier = buff.readVarInt(); + return new Server(SERVER, id, plr, new Inventory(invSize), tier); + }); + register(e.getRegistry(), "tablet", (id, plr, buff) -> { + int invSize = buff.readVarInt(); + String slot1 = buff.readUtf(32); + int tier1 = buff.readVarInt(); + return new Tablet(TABLET, id, plr, new Inventory(invSize), slot1, tier1); + }); + } + + private static void register(IForgeRegistry> registry, String name, IContainerFactory factory) { + ContainerType type = IForgeContainerType.create(factory); + type.setRegistryName(new ResourceLocation(OpenComputers.ID(), name)); + registry.register(type); + } + + private ContainerTypes() { + throw new Error(); + } +} diff --git a/src/main/scala/li/cil/oc/common/container/Database.scala b/src/main/scala/li/cil/oc/common/container/Database.scala index bbc580e87c..16b60f76f8 100644 --- a/src/main/scala/li/cil/oc/common/container/Database.scala +++ b/src/main/scala/li/cil/oc/common/container/Database.scala @@ -3,14 +3,20 @@ package li.cil.oc.common.container import li.cil.oc.common.inventory.DatabaseInventory import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory -import net.minecraft.inventory._ +import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ClickType +import net.minecraft.inventory.container.ContainerType import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -class Database(id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) extends Player(null, id, playerInventory, databaseInventory) { +class Database(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, databaseInventory: IInventory, tier: Int) + extends Player(selfType, id, playerInventory, databaseInventory) { + + def this(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) = + this(selfType, id, playerInventory, databaseInventory, databaseInventory.tier) + val rows = math.sqrt(databaseInventory.getContainerSize).ceil.toInt - val offset = 8 + Array(3, 2, 0)(databaseInventory.tier) * slotSize + val offset = 8 + Array(3, 2, 0)(tier) * slotSize for (row <- 0 until rows; col <- 0 until rows) { addSlotToContainer(offset + col * slotSize, offset + row * slotSize) diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index 63a19641c0..b1ea2831e8 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -2,16 +2,23 @@ package li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT -class Disassembler(id: Int, playerInventory: PlayerInventory, val disassembler: tileentity.Disassembler) extends Player(null, id, playerInventory, disassembler) { +class Disassembler(selfType: ContainerType[_ <: Disassembler], id: Int, playerInventory: PlayerInventory, val disassembler: IInventory) + extends Player(selfType, id, playerInventory, disassembler) { + addSlotToContainer(80, 35, "ocitem") addPlayerInventorySlots(8, 84) def disassemblyProgress = synchronizedData.getDouble("disassemblyProgress") override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { - synchronizedData.putDouble("disassemblyProgress", disassembler.progress) + disassembler match { + case te: tileentity.Disassembler => synchronizedData.putDouble("disassemblyProgress", te.progress) + case _ => + } super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala index 71808b9d82..8b8015e01d 100644 --- a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala @@ -3,8 +3,11 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType + +class DiskDrive(selfType: ContainerType[_ <: DiskDrive], id: Int, playerInventory: PlayerInventory, drive: IInventory) + extends Player(selfType, id, playerInventory, drive) { -class DiskDrive(id: Int, playerInventory: PlayerInventory, drive: IInventory) extends Player(null, id, playerInventory, drive) { addSlotToContainer(80, 35, Slot.Floppy) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index f85e8f5b76..cb62e91004 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -5,11 +5,14 @@ import li.cil.oc.common import li.cil.oc.common.entity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.item.ItemStack import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Drone(id: Int, playerInventory: PlayerInventory, drone: entity.Drone) extends Player(null, id, playerInventory, drone.mainInventory) { +class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: PlayerInventory, droneInv: IInventory) + extends Player(selfType, id, playerInventory, droneInv) { + val deltaY = 0 for (i <- 0 to 1) { @@ -23,7 +26,7 @@ class Drone(id: Int, playerInventory: PlayerInventory, drone: entity.Drone) exte addPlayerInventorySlots(8, 66) class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { - def isValid = (0 until drone.mainInventory.getContainerSize).contains(getSlotIndex) + def isValid = (0 until droneInv.getContainerSize).contains(getSlotIndex) @OnlyIn(Dist.CLIENT) override def isActive = isValid && super.isActive diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index 245c944732..cdcbbc64f7 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -24,7 +24,7 @@ import net.minecraftforge.common.util.FakePlayer import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable -abstract class Player(cType: ContainerType[_ <: Player], id: Int, val playerInventory: PlayerInventory, val otherInventory: IInventory) extends Container(cType, id) { +abstract class Player(selfType: ContainerType[_ <: Player], id: Int, val playerInventory: PlayerInventory, val otherInventory: IInventory) extends Container(selfType, id) { /** Number of player inventory slots to display horizontally. */ protected val playerInventorySizeX = math.min(9, PlayerInventory.getSelectionSize) diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index 16d1fba76b..a10db0aac5 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -3,9 +3,13 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT -class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity.Printer) extends Player(null, id, playerInventory, printer) { +class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: PlayerInventory, val printer: IInventory) + extends Player(selfType, id, playerInventory, printer) { + addSlotToContainer(18, 19, Slot.Filtered) addSlotToContainer(18, 51, Slot.Filtered) addSlotToContainer(152, 35) @@ -20,9 +24,14 @@ class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity def amountInk = synchronizedData.getInt("amountInk") override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { - synchronizedData.putDouble("progress", if (printer.isPrinting) printer.progress / 100.0 else 0) - synchronizedData.putInt("amountMaterial", printer.amountMaterial) - synchronizedData.putInt("amountInk", printer.amountInk) + printer match { + case te: tileentity.Printer => { + synchronizedData.putDouble("progress", if (te.isPrinting) te.progress / 100.0 else 0) + synchronizedData.putInt("amountMaterial", te.amountMaterial) + synchronizedData.putInt("amountInk", te.amountInk) + } + case _ => + } super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Rack.scala b/src/main/scala/li/cil/oc/common/container/Rack.scala index 707b16d36c..67abc9142d 100644 --- a/src/main/scala/li/cil/oc/common/container/Rack.scala +++ b/src/main/scala/li/cil/oc/common/container/Rack.scala @@ -6,12 +6,16 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.RotationHelper import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.IntArrayNBT import net.minecraft.util.Direction import net.minecraftforge.common.util.Constants.NBT -class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) extends Player(null, id, playerInventory, rack) { +class Rack(selfType: ContainerType[_ <: Rack], id: Int, playerInventory: PlayerInventory, val rack: IInventory) + extends Player(selfType, id, playerInventory, rack) { + addSlotToContainer(20, 23, Slot.RackMountable) addSlotToContainer(20, 43, Slot.RackMountable) addSlotToContainer(20, 63, Slot.RackMountable) @@ -20,26 +24,33 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) final val MaxConnections = 4 val nodePresence: Array[Array[Boolean]] = Array.fill(4)(Array.fill(4)(false)) + val nodeMapping: Array[Array[Option[Direction]]] = Array.fill(rack.getContainerSize)(Array.fill[Option[Direction]](4)(None)) + var isRelayEnabled = false override def updateCustomData(nbt: CompoundNBT): Unit = { super.updateCustomData(nbt) nbt.getList("nodeMapping", NBT.TAG_INT_ARRAY).map((sides: IntArrayNBT) => { sides.getAsIntArray.map(side => if (side >= 0) Option(Direction.from3DDataValue(side)) else None) - }).copyToArray(rack.nodeMapping) + }).copyToArray(nodeMapping) nbt.getBooleanArray("nodePresence").grouped(MaxConnections).copyToArray(nodePresence) - rack.isRelayEnabled = nbt.getBoolean("isRelayEnabled") + isRelayEnabled = nbt.getBoolean("isRelayEnabled") } override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { super.detectCustomDataChanges(nbt) - nbt.setNewTagList("nodeMapping", rack.nodeMapping.map(sides => toNbt(sides.map { - case Some(side) => side.ordinal() - case _ => -1 - }))) - nbt.setBooleanArray("nodePresence", (0 until rack.getContainerSize).flatMap(slot => rack.getMountable(slot) match { - case mountable: RackMountable => (Seq(true) ++ (0 until math.min(MaxConnections - 1, mountable.getConnectableCount)).map(index => mountable.getConnectableAt(index) != null)).padTo(MaxConnections, false) - case _ => Array.fill(MaxConnections)(false) - }).toArray) - nbt.putBoolean("isRelayEnabled", rack.isRelayEnabled) + rack match { + case te: tileentity.Rack => { + nbt.setNewTagList("nodeMapping", te.nodeMapping.map(sides => toNbt(sides.map { + case Some(side) => side.ordinal() + case _ => -1 + }))) + nbt.setBooleanArray("nodePresence", (0 until te.getContainerSize).flatMap(slot => te.getMountable(slot) match { + case mountable: RackMountable => (Seq(true) ++ (0 until math.min(MaxConnections - 1, mountable.getConnectableCount)).map(index => mountable.getConnectableAt(index) != null)).padTo(MaxConnections, false) + case _ => Array.fill(MaxConnections)(false) + }).toArray) + nbt.putBoolean("isRelayEnabled", te.isRelayEnabled) + } + case _ => + } } } diff --git a/src/main/scala/li/cil/oc/common/container/Raid.scala b/src/main/scala/li/cil/oc/common/container/Raid.scala index cc630605da..46fc89348a 100644 --- a/src/main/scala/li/cil/oc/common/container/Raid.scala +++ b/src/main/scala/li/cil/oc/common/container/Raid.scala @@ -4,8 +4,12 @@ import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType + +class Raid(selfType: ContainerType[_ <: Raid], id: Int, playerInventory: PlayerInventory, raid: IInventory) + extends Player(selfType, id, playerInventory, raid) { -class Raid(id: Int, playerInventory: PlayerInventory, raid: tileentity.Raid) extends Player(null, id, playerInventory, raid) { addSlotToContainer(60, 23, Slot.HDD, Tier.Three) addSlotToContainer(80, 23, Slot.HDD, Tier.Three) addSlotToContainer(100, 23, Slot.HDD, Tier.Three) diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala index 4562e747cb..30e519ad9e 100644 --- a/src/main/scala/li/cil/oc/common/container/Relay.scala +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -3,9 +3,13 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT -class Relay(id: Int, playerInventory: PlayerInventory, relay: tileentity.Relay) extends Player(null, id, playerInventory, relay) { +class Relay(selfType: ContainerType[_ <: Relay], id: Int, playerInventory: PlayerInventory, relay: IInventory) + extends Player(selfType, id, playerInventory, relay) { + addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) addSlotToContainer(151, 53, Slot.HDD) @@ -23,11 +27,16 @@ class Relay(id: Int, playerInventory: PlayerInventory, relay: tileentity.Relay) def queueSize = synchronizedData.getInt("queueSize") override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { - synchronizedData.putInt("relayDelay", relay.relayDelay) - synchronizedData.putInt("relayAmount", relay.relayAmount) - synchronizedData.putInt("maxQueueSize", relay.maxQueueSize) - synchronizedData.putInt("packetsPerCycleAvg", relay.packetsPerCycleAvg()) - synchronizedData.putInt("queueSize", relay.queue.size) + relay match { + case te: tileentity.Relay => { + synchronizedData.putInt("relayDelay", te.relayDelay) + synchronizedData.putInt("relayAmount", te.relayAmount) + synchronizedData.putInt("maxQueueSize", te.maxQueueSize) + synchronizedData.putInt("packetsPerCycleAvg", te.packetsPerCycleAvg()) + synchronizedData.putInt("queueSize", te.queue.size) + } + case _ => + } super.detectCustomDataChanges(nbt) } } diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 3006cd51d2..ee425298c2 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -7,25 +7,66 @@ import li.cil.oc.common.tileentity import li.cil.oc.util.SideTracker import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.item.ItemStack +import net.minecraft.network.PacketBuffer import net.minecraft.util.IntReferenceHolder import net.minecraft.util.ResourceLocation import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Robot(id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) extends Player(null, id, playerInventory, robot) { - val hasScreen: Boolean = robot.components.exists { +object RobotInfo { + def hasScreen(robot: tileentity.Robot) = robot.components.exists { case Some(buffer: api.internal.TextBuffer) => true case _ => false } + + def readRobotInfo(buff: PacketBuffer): RobotInfo = { + val mainInvSize = buff.readVarInt() + val slot1 = buff.readUtf(32) + val tier1 = buff.readVarInt() + val slot2 = buff.readUtf(32) + val tier2 = buff.readVarInt() + val slot3 = buff.readUtf(32) + val tier3 = buff.readVarInt() + val hasScreen = buff.readBoolean() + new RobotInfo(mainInvSize, slot1, tier1, slot2, tier2, slot3, tier3, hasScreen) + } + + def writeRobotInfo(buff: PacketBuffer, info: RobotInfo) { + buff.writeVarInt(info.mainInvSize) + buff.writeUtf(info.slot1, 32) + buff.writeVarInt(info.tier1) + buff.writeUtf(info.slot2, 32) + buff.writeVarInt(info.tier2) + buff.writeUtf(info.slot3, 32) + buff.writeVarInt(info.tier3) + buff.writeBoolean(info.hasScreen) + } +} + +class RobotInfo(val mainInvSize: Int, val slot1: String, val tier1: Int, + val slot2: String, val tier2: Int, val slot3: String, val tier3: Int, val hasScreen: Boolean) { + + def this(robot: tileentity.Robot) = this(robot.mainInventory.getContainerSize, robot.containerSlotType(1), robot.containerSlotTier(1), + robot.containerSlotType(2), robot.containerSlotTier(2), robot.containerSlotType(3), robot.containerSlotTier(3), + RobotInfo.hasScreen(robot)) +} + +class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: IInventory, info: RobotInfo) + extends Player(selfType, id, playerInventory, robot) { + + def this(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) = + this(selfType, id, playerInventory, robot, new RobotInfo(robot)) + private val withScreenHeight = 256 private val noScreenHeight = 108 - val deltaY: Int = if (hasScreen) 0 else withScreenHeight - noScreenHeight + val deltaY: Int = if (info.hasScreen) 0 else withScreenHeight - noScreenHeight addSlotToContainer(170 + 0 * slotSize, 232 - deltaY, common.Slot.Tool) - addSlotToContainer(170 + 1 * slotSize, 232 - deltaY, robot.containerSlotType(1), robot.containerSlotTier(1)) - addSlotToContainer(170 + 2 * slotSize, 232 - deltaY, robot.containerSlotType(2), robot.containerSlotTier(2)) - addSlotToContainer(170 + 3 * slotSize, 232 - deltaY, robot.containerSlotType(3), robot.containerSlotTier(3)) + addSlotToContainer(170 + 1 * slotSize, 232 - deltaY, info.slot1, info.tier1) + addSlotToContainer(170 + 2 * slotSize, 232 - deltaY, info.slot2, info.tier2) + addSlotToContainer(170 + 3 * slotSize, 232 - deltaY, info.slot3, info.tier3) // Slot.x and Slot.y are final, so have to rebuild when scrolling def generateSlotsFor(scroll: Int) { @@ -49,22 +90,32 @@ class Robot(id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) // values as shorts over the net (for whatever reason). private val factor = 100 - addDataSlot(new IntReferenceHolder { - override def get(): Int = robot.globalBuffer.toInt / factor + val globalBuffer = robot match { + case te: tileentity.Robot => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = te.globalBuffer.toInt / factor - override def set(value: Int): Unit = robot.globalBuffer = value * factor - }) + override def set(value: Int): Unit = te.globalBuffer = value * factor + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } - addDataSlot(new IntReferenceHolder { - override def get(): Int = robot.globalBufferSize.toInt / factor + val globalBufferSize = robot match { + case te: tileentity.Robot => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = te.globalBufferSize.toInt / factor - override def set(value: Int): Unit = robot.globalBufferSize = value * factor - }) + override def set(value: Int): Unit = te.globalBufferSize = value * factor + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, var enabled: Boolean) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { - def isValid: Boolean = robot.isInventorySlot(getSlotIndex) + def isValid: Boolean = getSlotIndex >= 4 && getSlotIndex < 4 + info.mainInvSize @OnlyIn(Dist.CLIENT) override def isActive: Boolean = enabled && isValid && super.isActive diff --git a/src/main/scala/li/cil/oc/common/container/Server.scala b/src/main/scala/li/cil/oc/common/container/Server.scala index a9e5d28953..1ac67a3e6f 100644 --- a/src/main/scala/li/cil/oc/common/container/Server.scala +++ b/src/main/scala/li/cil/oc/common/container/Server.scala @@ -5,37 +5,47 @@ import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.server.component import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT -class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val server: Option[component.Server] = None) extends Player(null, id, playerInventory, serverInventory) { +class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: IInventory, tier: Int, val server: Option[component.Server]) + extends Player(selfType, id, playerInventory, serverInventory) { + + def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: IInventory, tier: Int) = + this(selfType, id, playerInventory, serverInventory, tier, None) + + def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, server: Option[component.Server] = None) = + this(selfType, id, playerInventory, serverInventory, serverInventory.tier, server) + for (i <- 0 to 1) { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) } - val verticalSlots = math.min(3, 1 + serverInventory.tier) + val verticalSlots = math.min(3, 1 + tier) for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(100, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(124, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 0 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(148, 7 + i * slotSize, slot.slot, slot.tier) } for (i <- 2 to verticalSlots) { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) } { - val slot = InventorySlots.server(serverInventory.tier)(getItems.size) + val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(26, 34, slot.slot, slot.tier) } diff --git a/src/main/scala/li/cil/oc/common/container/Tablet.scala b/src/main/scala/li/cil/oc/common/container/Tablet.scala index 03a420827e..d1b7ad3fc9 100644 --- a/src/main/scala/li/cil/oc/common/container/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/container/Tablet.scala @@ -3,9 +3,16 @@ package li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.ContainerType -class Tablet(id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) extends Player(null, id, playerInventory, tablet) { - addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, tablet.containerSlotType, tablet.containerSlotTier)) +class Tablet(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, tablet: IInventory, slot1: String, tier1: Int) + extends Player(selfType, id, playerInventory, tablet) { + + def this(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) = + this(selfType, id, playerInventory, tablet, tablet.containerSlotType, tablet.containerSlotTier) + + addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, slot1, tier1)) addPlayerInventorySlots(8, 84) From 5f282974a44cb6f80aea60ee7862a7b8bcf8e177 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 19 Aug 2022 00:28:40 +0200 Subject: [PATCH 046/159] Register screen constructors for containers --- .../scala/li/cil/oc/client/GuiHandler.scala | 38 +++---- .../scala/li/cil/oc/client/PacketSender.scala | 34 +++--- .../scala/li/cil/oc/client/gui/Adapter.scala | 11 +- .../li/cil/oc/client/gui/Assembler.scala | 12 ++- .../scala/li/cil/oc/client/gui/Case.scala | 18 ++-- .../scala/li/cil/oc/client/gui/Charger.scala | 11 +- .../scala/li/cil/oc/client/gui/Database.scala | 17 +-- .../li/cil/oc/client/gui/Disassembler.scala | 11 +- .../li/cil/oc/client/gui/DiskDrive.scala | 10 +- .../scala/li/cil/oc/client/gui/Drone.scala | 30 +++--- .../scala/li/cil/oc/client/gui/GuiTypes.java | 42 ++++++++ .../scala/li/cil/oc/client/gui/Printer.scala | 19 ++-- .../scala/li/cil/oc/client/gui/Rack.scala | 35 +++--- .../scala/li/cil/oc/client/gui/Raid.scala | 11 +- .../scala/li/cil/oc/client/gui/Relay.scala | 11 +- .../scala/li/cil/oc/client/gui/Robot.scala | 49 +++++---- .../scala/li/cil/oc/client/gui/Server.scala | 26 ++--- .../scala/li/cil/oc/client/gui/Tablet.scala | 13 ++- .../scala/li/cil/oc/common/GuiHandler.scala | 2 +- .../li/cil/oc/common/container/Case.scala | 13 +++ .../oc/common/container/ContainerTypes.java | 11 +- .../li/cil/oc/common/container/Database.scala | 4 +- .../li/cil/oc/common/container/Drone.scala | 65 +++++++++++ .../li/cil/oc/common/container/Printer.scala | 6 ++ .../li/cil/oc/common/container/Robot.scala | 66 +++++++++--- .../li/cil/oc/common/container/Server.scala | 20 ++-- .../li/cil/oc/common/container/Tablet.scala | 5 +- .../scala/li/cil/oc/common/entity/Drone.scala | 6 +- .../li/cil/oc/common/tileentity/Robot.scala | 4 +- .../li/cil/oc/server/PacketHandler.scala | 102 ++++++++++-------- .../li/cil/oc/server/component/Server.scala | 2 +- 31 files changed, 482 insertions(+), 222 deletions(-) create mode 100644 src/main/scala/li/cil/oc/client/gui/GuiTypes.java diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index adb775e997..ffdb64338a 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -26,39 +26,39 @@ object GuiHandler extends CommonGuiHandler { case Some(GuiType.Category.Block) => world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { case t: tileentity.Adapter if id == GuiType.Adapter.id => - new gui.Adapter(containerId, player.inventory, t) + gui.Adapter.of(containerId, player.inventory, t) case t: tileentity.Assembler if id == GuiType.Assembler.id => - new gui.Assembler(containerId, player.inventory, t) + gui.Assembler.of(containerId, player.inventory, t) case t: tileentity.Case if id == GuiType.Case.id => - new gui.Case(containerId, player.inventory, t) + gui.Case.of(containerId, player.inventory, t) case t: tileentity.Charger if id == GuiType.Charger.id => - new gui.Charger(containerId, player.inventory, t) + gui.Charger.of(containerId, player.inventory, t) case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - new gui.Disassembler(containerId, player.inventory, t) + gui.Disassembler.of(containerId, player.inventory, t) case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - new gui.DiskDrive(containerId, player.inventory, t) + gui.DiskDrive.of(containerId, player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => - new gui.Printer(containerId, player.inventory, t) + gui.Printer.of(containerId, player.inventory, t) case t: tileentity.Rack if id == GuiType.Rack.id => - new gui.Rack(containerId, player.inventory, t) + gui.Rack.of(containerId, player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => - new gui.Raid(containerId, player.inventory, t) + gui.Raid.of(containerId, player.inventory, t) case t: tileentity.Relay if id == GuiType.Relay.id => - new gui.Relay(containerId, player.inventory, t) + gui.Relay.of(containerId, player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new gui.Robot(containerId, player.inventory, t.robot) + gui.Robot.of(containerId, player.inventory, t.robot) case t: tileentity.Screen if id == GuiType.Screen.id => new gui.Screen(t.origin.buffer, t.tier > 0, () => t.origin.hasKeyboard, () => t.origin.buffer.isRenderingEnabled) case t: tileentity.Rack if id == GuiType.ServerInRack.id => val slot = GuiType.extractSlot(y) - new gui.Server(containerId, player.inventory, new ServerInventory { + gui.Server.of(containerId, player.inventory, new ServerInventory { override def container = t.getItem(slot) override def stillValid(player: PlayerEntity) = t.stillValid(player) - }, Option(t), slot) + }, slot) case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => val slot = GuiType.extractSlot(y) - new gui.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { + gui.DiskDrive.of(containerId, player.inventory, new DiskDriveMountableInventory { override def container: ItemStack = t.getItem(slot) override def stillValid(player: PlayerEntity): Boolean = t.stillValid(player) @@ -70,7 +70,7 @@ object GuiHandler extends CommonGuiHandler { case Some(GuiType.Category.Entity) => world.getEntity(x) match { case drone: entity.Drone if id == GuiType.Drone.id => - new gui.Drone(containerId, player.inventory, drone) + gui.Drone.of(containerId, player.inventory, drone) case _ => null } case Some(GuiType.Category.Item) => { @@ -79,13 +79,13 @@ object GuiHandler extends CommonGuiHandler { case drive: item.traits.FileSystemLike if id == GuiType.Drive.id => new gui.Drive(player.inventory, () => itemStackInUse) case database: item.UpgradeDatabase if id == GuiType.Database.id => - new gui.Database(containerId, player.inventory, new DatabaseInventory { + gui.Database.of(containerId, player.inventory, new DatabaseInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player }) case server: item.Server if id == GuiType.Server.id => - new gui.Server(containerId, player.inventory, new ServerInventory { + gui.Server.of(containerId, player.inventory, new ServerInventory { override def container = itemStackInUse override def stillValid(player: PlayerEntity) = player == player @@ -104,11 +104,11 @@ object GuiHandler extends CommonGuiHandler { case tablet: item.Tablet if id == GuiType.TabletInner.id => val stack = itemStackInUse if (stack.hasTag) { - new gui.Tablet(containerId, player.inventory, item.Tablet.get(stack, player)) + gui.Tablet.of(containerId, player.inventory, item.Tablet.get(stack, player)) } else null case _: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => - new gui.DiskDrive(containerId, player.inventory, new DiskDriveMountableInventory { + gui.DiskDrive.of(containerId, player.inventory, new DiskDriveMountableInventory { override def container = itemStackInUse override def stillValid(activePlayer : PlayerEntity): Boolean = activePlayer == player }) diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index ea7956f38e..5ebfca6400 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -4,6 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.common.CompressedPacketBuilder import li.cil.oc.common.PacketType import li.cil.oc.common.SimplePacketBuilder +import li.cil.oc.common.container import li.cil.oc.common.entity.Drone import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity.traits.Computer @@ -21,10 +22,19 @@ object PacketSender { // avoid spamming large packets on key repeat. protected var clipboardCooldown = 0L - def sendComputerPower(t: Computer, power: Boolean) { + def sendComputerPower(computer: container.Case, power: Boolean) { val pb = new SimplePacketBuilder(PacketType.ComputerPower) - pb.writeTileEntity(t) + pb.writeInt(computer.containerId) + pb.writeBoolean(power) + + pb.sendToServer() + } + + def sendRobotPower(robot: container.Robot, power: Boolean) { + val pb = new SimplePacketBuilder(PacketType.ComputerPower) + + pb.writeInt(robot.containerId) pb.writeBoolean(power) pb.sendToServer() @@ -44,10 +54,10 @@ object PacketSender { pb.sendToServer() } - def sendDronePower(e: Drone, power: Boolean) { + def sendDronePower(drone: container.Drone, power: Boolean) { val pb = new SimplePacketBuilder(PacketType.DronePower) - pb.writeEntity(e) + pb.writeInt(drone.containerId) pb.writeBoolean(power) pb.sendToServer() @@ -157,10 +167,10 @@ object PacketSender { pb.sendToServer() } - def sendRackMountableMapping(t: Rack, mountableIndex: Int, nodeIndex: Int, side: Option[Direction]) { + def sendRackMountableMapping(rack: container.Rack, mountableIndex: Int, nodeIndex: Int, side: Option[Direction]) { val pb = new SimplePacketBuilder(PacketType.RackMountableMapping) - pb.writeTileEntity(t) + pb.writeInt(rack.containerId) pb.writeInt(mountableIndex) pb.writeInt(nodeIndex) pb.writeDirection(side) @@ -168,19 +178,19 @@ object PacketSender { pb.sendToServer() } - def sendRackRelayState(t: Rack, enabled: Boolean) { + def sendRackRelayState(rack: container.Rack, enabled: Boolean) { val pb = new SimplePacketBuilder(PacketType.RackRelayState) - pb.writeTileEntity(t) + pb.writeInt(rack.containerId) pb.writeBoolean(enabled) pb.sendToServer() } - def sendRobotAssemblerStart(t: Assembler) { + def sendRobotAssemblerStart(assembler: container.Assembler) { val pb = new SimplePacketBuilder(PacketType.RobotAssemblerStart) - pb.writeTileEntity(t) + pb.writeInt(assembler.containerId) pb.sendToServer() } @@ -196,10 +206,10 @@ object PacketSender { pb.sendToServer() } - def sendServerPower(t: Rack, mountableIndex: Int, power: Boolean) { + def sendServerPower(server: container.Server, mountableIndex: Int, power: Boolean) { val pb = new SimplePacketBuilder(PacketType.ServerPower) - pb.writeTileEntity(t) + pb.writeInt(server.containerId) pb.writeInt(mountableIndex) pb.writeBoolean(power) diff --git a/src/main/scala/li/cil/oc/client/gui/Adapter.scala b/src/main/scala/li/cil/oc/client/gui/Adapter.scala index 7f0cfb71c4..8a4076c7f2 100644 --- a/src/main/scala/li/cil/oc/client/gui/Adapter.scala +++ b/src/main/scala/li/cil/oc/client/gui/Adapter.scala @@ -4,8 +4,13 @@ import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Adapter(id: Int, playerInventory: PlayerInventory, val adapter: tileentity.Adapter) - extends DynamicGuiContainer(new container.Adapter(container.ContainerTypes.ADAPTER, id, playerInventory, adapter), - playerInventory, adapter.getName) { +object Adapter { + def of(id: Int, playerInventory: PlayerInventory, adapter: tileentity.Adapter): Adapter = + new Adapter(new container.Adapter(container.ContainerTypes.ADAPTER, id, playerInventory, adapter), playerInventory, adapter.getName) +} + +class Adapter(state: container.Adapter, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index a0cadcd69c..b0653ffe2e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -20,9 +20,13 @@ import net.minecraft.util.text.StringTextComponent import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ -class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileentity.Assembler) - extends DynamicGuiContainer(new container.Assembler(container.ContainerTypes.ASSEMBLER, id, playerInventory, assembler), - playerInventory, StringTextComponent.EMPTY) { +object Assembler { + def of(id: Int, playerInventory: PlayerInventory, assembler: tileentity.Assembler) = + new Assembler(new container.Assembler(container.ContainerTypes.ASSEMBLER, id, playerInventory, assembler), playerInventory, StringTextComponent.EMPTY) +} + +class Assembler(state: container.Assembler, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { imageWidth = 176 imageHeight = 192 @@ -51,7 +55,7 @@ class Assembler(id: Int, playerInventory: PlayerInventory, val assembler: tileen override protected def init() { super.init() runButton = new ImageButton(leftPos + 7, topPos + 89, 18, 18, new Button.IPressable { - override def onPress(b: Button) = if (canBuild) ClientPacketSender.sendRobotAssemblerStart(assembler) + override def onPress(b: Button) = if (canBuild) ClientPacketSender.sendRobotAssemblerStart(inventoryContainer) }, Textures.GUI.ButtonRun, canToggle = true) addButton(runButton) } diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index f6a2cb3394..193af45266 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -9,35 +9,39 @@ import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -class Case(id: Int, playerInventory: PlayerInventory, val computer: tileentity.Case) - extends DynamicGuiContainer(new container.Case(container.ContainerTypes.CASE, id, playerInventory, computer, computer.tier), - playerInventory, computer.getName) { +object Case { + def of(id: Int, playerInventory: PlayerInventory, computer: tileentity.Case) = + new Case(new container.Case(container.ContainerTypes.CASE, id, playerInventory, computer, computer.tier), playerInventory, computer.getName) +} + +class Case(state: container.Case, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { protected var powerButton: ImageButton = _ override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { - powerButton.toggled = computer.isRunning + powerButton.toggled = inventoryContainer.isRunning super.render(stack, mouseX, mouseY, dt) } override protected def init() { super.init() powerButton = new ImageButton(leftPos + 70, topPos + 33, 18, 18, new Button.IPressable { - override def onPress(b: Button) = ClientPacketSender.sendComputerPower(computer, !computer.isRunning) + override def onPress(b: Button) = ClientPacketSender.sendComputerPower(inventoryContainer, !inventoryContainer.isRunning) }, Textures.GUI.ButtonPower, canToggle = true) addButton(powerButton) } override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) = { super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) - font.draw(stack, computer.getName, 8, 6, 0x404040) if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.addAll(asJavaCollection(if (computer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) + tooltip.addAll(asJavaCollection(if (inventoryContainer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Charger.scala b/src/main/scala/li/cil/oc/client/gui/Charger.scala index 9a19b8185a..e8eb28c1c5 100644 --- a/src/main/scala/li/cil/oc/client/gui/Charger.scala +++ b/src/main/scala/li/cil/oc/client/gui/Charger.scala @@ -4,8 +4,13 @@ import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Charger(id: Int, playerInventory: PlayerInventory, val charger: tileentity.Charger) - extends DynamicGuiContainer(new container.Charger(container.ContainerTypes.CHARGER, id, playerInventory, charger), - playerInventory, charger.getName) { +object Charger { + def of(id: Int, playerInventory: PlayerInventory, charger: tileentity.Charger) = + new Charger(new container.Charger(container.ContainerTypes.CHARGER, id, playerInventory, charger), playerInventory, charger.getName) +} + +class Charger(state: container.Charger, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 579dc558d2..8ff9a9dc7b 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -7,16 +7,21 @@ import li.cil.oc.common.Tier import li.cil.oc.common.container import li.cil.oc.common.inventory.DatabaseInventory import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent -class Database(id: Int, playerInventory: PlayerInventory, val databaseInventory: DatabaseInventory) - extends DynamicGuiContainer(new container.Database(container.ContainerTypes.DATABASE, id, playerInventory, databaseInventory), - playerInventory, StringTextComponent.EMPTY) +object Database { + def of(id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) = + new Database(new container.Database(container.ContainerTypes.DATABASE, id, playerInventory, databaseInventory), playerInventory, StringTextComponent.EMPTY) +} + +class Database(state: container.Database, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) with traits.LockedHotbar[container.Database] { imageHeight = 256 - override def lockedStack = databaseInventory.container + override def lockedStack = inventoryContainer.container override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) {} @@ -25,12 +30,12 @@ class Database(id: Int, playerInventory: PlayerInventory, val databaseInventory: Textures.bind(Textures.GUI.Database) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) - if (databaseInventory.tier > Tier.One) { + if (inventoryContainer.tier > Tier.One) { Textures.bind(Textures.GUI.Database1) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } - if (databaseInventory.tier > Tier.Two) { + if (inventoryContainer.tier > Tier.Two) { Textures.bind(Textures.GUI.Database2) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) } diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 75423f967f..14cfec7911 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -8,10 +8,15 @@ import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Disassembler(id: Int, playerInventory: PlayerInventory, val disassembler: tileentity.Disassembler) - extends DynamicGuiContainer(new container.Disassembler(container.ContainerTypes.DISASSEMBLER, id, playerInventory, disassembler), - playerInventory, disassembler.getName) { +object Disassembler { + def of(id: Int, playerInventory: PlayerInventory, disassembler: tileentity.Disassembler) = + new Disassembler(new container.Disassembler(container.ContainerTypes.DISASSEMBLER, id, playerInventory, disassembler), playerInventory, disassembler.getName) +} + +class Disassembler(state: container.Disassembler, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { val progress = addCustomWidget(new ProgressBar(18, 65)) diff --git a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala index f4bcb82a2b..cbe01ba47c 100644 --- a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala @@ -5,7 +5,13 @@ import li.cil.oc.common.container import li.cil.oc.common.inventory.SimpleInventory import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class DiskDrive(id: Int, playerInventory: PlayerInventory, val drive: SimpleInventory) - extends DynamicGuiContainer(new container.DiskDrive(container.ContainerTypes.DISK_DRIVE, id, playerInventory, drive), playerInventory, drive.getName) { +object DiskDrive { + def of(id: Int, playerInventory: PlayerInventory, drive: SimpleInventory) = + new DiskDrive(new container.DiskDrive(container.ContainerTypes.DISK_DRIVE, id, playerInventory, drive), playerInventory, drive.getName) +} + +class DiskDrive(state: container.DiskDrive, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 2aa16db70b..310f166039 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -17,15 +17,20 @@ import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) - extends DynamicGuiContainer(new container.Drone(container.ContainerTypes.DRONE, id, playerInventory, drone.mainInventory), - playerInventory, StringTextComponent.EMPTY) +object Drone { + def of(id: Int, playerInventory: PlayerInventory, drone: entity.Drone) = + new Drone(new container.Drone(container.ContainerTypes.DRONE, id, playerInventory, drone.mainInventory), playerInventory, StringTextComponent.EMPTY) +} + +class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) with traits.DisplayBuffer { imageWidth = 176 @@ -61,8 +66,8 @@ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) private val selectionStepV = 1 / selectionsStates.toFloat override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { - powerButton.toggled = drone.isRunning - bufferRenderer.dirty = drone.statusText.lines.zipWithIndex.exists { + powerButton.toggled = inventoryContainer.isRunning + bufferRenderer.dirty = inventoryContainer.statusText.lines.zipWithIndex.exists { case (line, i) => buffer.set(0, i, line, vertical = false) } super.render(stack, mouseX, mouseY, dt) @@ -71,7 +76,7 @@ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) override protected def init() { super.init() powerButton = new ImageButton(leftPos + 7, topPos + 45, 18, 18, new Button.IPressable { - override def onPress(b: Button) = ClientPacketSender.sendDronePower(drone, !drone.isRunning) + override def onPress(b: Button) = ClientPacketSender.sendDronePower(inventoryContainer, !inventoryContainer.isRunning) }, Textures.GUI.ButtonPower, canToggle = true) addButton(powerButton) } @@ -97,14 +102,13 @@ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) val tooltip = new java.util.ArrayList[String] val format = Localization.Computer.Power + ": %d%% (%d/%d)" tooltip.add(format.format( - drone.globalBuffer * 100 / math.max(drone.globalBufferSize, 1), - drone.globalBuffer, - drone.globalBufferSize)) + inventoryContainer.globalBuffer * 100 / math.max(inventoryContainer.globalBufferSize, 1), + inventoryContainer.globalBuffer, inventoryContainer.globalBufferSize)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.addAll(asJavaCollection(if (drone.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) + tooltip.addAll(asJavaCollection(if (inventoryContainer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() @@ -114,9 +118,9 @@ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Drone) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) - power.level = drone.globalBuffer.toFloat / math.max(drone.globalBufferSize.toFloat, 1.0f) + power.level = inventoryContainer.globalBuffer.toFloat / math.max(inventoryContainer.globalBufferSize.toFloat, 1.0f) drawWidgets(stack) - if (drone.mainInventory.getContainerSize > 0) { + if (inventoryContainer.otherInventory.getContainerSize > 0) { drawSelection(stack) } @@ -127,7 +131,7 @@ class Drone(id: Int, playerInventory: PlayerInventory, val drone: entity.Drone) override protected def drawSlotBackground(stack: MatrixStack, x: Int, y: Int) {} private def drawSelection(stack: MatrixStack) { - val slot = drone.selectedSlot + val slot = inventoryContainer.selectedSlot if (slot >= 0 && slot < 16) { RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) diff --git a/src/main/scala/li/cil/oc/client/gui/GuiTypes.java b/src/main/scala/li/cil/oc/client/gui/GuiTypes.java new file mode 100644 index 0000000000..056f709993 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/GuiTypes.java @@ -0,0 +1,42 @@ +package li.cil.oc.client.gui; + +import li.cil.oc.OpenComputers; +import li.cil.oc.common.container.ContainerTypes; +import net.minecraft.client.gui.ScreenManager; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; + +@Mod.EventBusSubscriber(value = {Dist.CLIENT}, modid = "opencomputers", bus = Bus.MOD) +public final class GuiTypes { + @SubscribeEvent + public static void clientSetup(FMLClientSetupEvent e) { + // ScreenManager.register is not thread-safe. + e.enqueueWork(() -> { + ScreenManager.register(ContainerTypes.ADAPTER, Adapter::new); + ScreenManager.register(ContainerTypes.ASSEMBLER, Assembler::new); + ScreenManager.register(ContainerTypes.CASE, Case::new); + ScreenManager.register(ContainerTypes.CHARGER, Charger::new); + ScreenManager.register(ContainerTypes.DATABASE, Database::new); + ScreenManager.register(ContainerTypes.DISASSEMBLER, Disassembler::new); + ScreenManager.register(ContainerTypes.DISK_DRIVE, DiskDrive::new); + ScreenManager.register(ContainerTypes.DRONE, Drone::new); + ScreenManager.register(ContainerTypes.PRINTER, Printer::new); + ScreenManager.register(ContainerTypes.RACK, Rack::new); + ScreenManager.register(ContainerTypes.RAID, Raid::new); + ScreenManager.register(ContainerTypes.RELAY, Relay::new); + ScreenManager.register(ContainerTypes.ROBOT, Robot::new); + ScreenManager.register(ContainerTypes.SERVER, Server::new); + ScreenManager.register(ContainerTypes.TABLET, Tablet::new); + }); + } + + private GuiTypes() { + throw new Error(); + } +} diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index 5e4d2aaa2e..2727f87ce6 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -10,10 +10,15 @@ import li.cil.oc.common.container.ComponentSlot import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity.Printer) - extends DynamicGuiContainer(new container.Printer(container.ContainerTypes.PRINTER, id, playerInventory, printer), - playerInventory, printer.getName) { +object Printer { + def of(id: Int, playerInventory: PlayerInventory, printer: tileentity.Printer) + = new Printer(new container.Printer(container.ContainerTypes.PRINTER, id, playerInventory, printer), playerInventory, printer.getName) +} + +class Printer(state: container.Printer, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { imageWidth = 176 imageHeight = 166 @@ -45,12 +50,12 @@ class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity RenderState.pushAttrib() if (isPointInRegion(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.add(inventoryContainer.amountMaterial + "/" + printer.maxAmountMaterial) + tooltip.add(inventoryContainer.amountMaterial + "/" + inventoryContainer.maxAmountMaterial) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } if (isPointInRegion(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.add(inventoryContainer.amountInk + "/" + printer.maxAmountInk) + tooltip.add(inventoryContainer.amountInk + "/" + inventoryContainer.maxAmountInk) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() @@ -60,8 +65,8 @@ class Printer(id: Int, playerInventory: PlayerInventory, val printer: tileentity RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Printer) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) - materialBar.level = inventoryContainer.amountMaterial / printer.maxAmountMaterial.toDouble - inkBar.level = inventoryContainer.amountInk / printer.maxAmountInk.toDouble + materialBar.level = inventoryContainer.amountMaterial / inventoryContainer.maxAmountMaterial.toDouble + inkBar.level = inventoryContainer.amountInk / inventoryContainer.maxAmountInk.toDouble progressBar.level = inventoryContainer.progress drawWidgets(stack) drawInventorySlots(stack) diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index 23e184eb96..f4ff8b33f9 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -13,14 +13,19 @@ import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.Direction +import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection -class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) - extends DynamicGuiContainer(new container.Rack(container.ContainerTypes.RACK, id, playerInventory, rack), - playerInventory, rack.getName) { +object Rack { + def of(id: Int, playerInventory: PlayerInventory, rack: tileentity.Rack) = + new Rack(new container.Rack(container.ContainerTypes.RACK, id, playerInventory, rack), playerInventory, rack.getName) +} + +class Rack(state: container.Rack, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { imageHeight = 210 @@ -84,7 +89,7 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) var relayButton: ImageButton = _ // bus -> mountable -> connectable - var wireButtons = Array.fill(rack.getContainerSize)(Array.fill(4)(Array.fill(5)(null: ImageButton))) + var wireButtons = Array.fill(inventoryContainer.otherInventory.getContainerSize)(Array.fill(4)(Array.fill(5)(null: ImageButton))) def sideName(side: Direction) = side match { case Direction.UP => Localization.Rack.Top @@ -96,24 +101,24 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) } protected def onRackButton(mountable: Int, connectable: Int, bus: Int) { - if (rack.nodeMapping(mountable)(connectable).contains(busToSide(bus))) { - ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, None) + if (inventoryContainer.nodeMapping(mountable)(connectable).contains(busToSide(bus))) { + ClientPacketSender.sendRackMountableMapping(inventoryContainer, mountable, connectable, None) } else { - ClientPacketSender.sendRackMountableMapping(rack, mountable, connectable, Option(busToSide(bus))) + ClientPacketSender.sendRackMountableMapping(inventoryContainer, mountable, connectable, Option(busToSide(bus))) } } override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { for (bus <- 0 until 5) { - for (mountable <- 0 until rack.getContainerSize) { + for (mountable <- 0 until inventoryContainer.otherInventory.getContainerSize) { val presence = inventoryContainer.nodePresence(mountable) for (connectable <- 0 until 4) { wireButtons(mountable)(connectable)(bus).visible = presence(connectable) } } } - val relayMessage = if (rack.isRelayEnabled) Localization.Rack.RelayEnabled else Localization.Rack.RelayDisabled + val relayMessage = if (inventoryContainer.isRelayEnabled) Localization.Rack.RelayEnabled else Localization.Rack.RelayDisabled relayButton.setMessage(new StringTextComponent(relayMessage)) super.render(stack, mouseX, mouseY, dt) } @@ -122,7 +127,7 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) super.init() relayButton = new ImageButton(leftPos + 101, topPos + 96, 65, 18, new Button.IPressable { - override def onPress(b: Button) = ClientPacketSender.sendRackRelayState(rack, !rack.isRelayEnabled) + override def onPress(b: Button) = ClientPacketSender.sendRackRelayState(inventoryContainer, !inventoryContainer.isRelayEnabled) }, Textures.GUI.ButtonRelay, new StringTextComponent(Localization.Rack.RelayDisabled), textIndent = 18) addButton(relayButton) @@ -131,7 +136,7 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) val (_, _, _, mbh) = busMasterBlankUVs val (_, _, _, sbh) = busSlaveBlankUVs for (bus <- 0 until 5) { - for (mountable <- 0 until rack.getContainerSize) { + for (mountable <- 0 until inventoryContainer.otherInventory.getContainerSize) { val offset = mountable * (mbh + sbh * 3 + busGap) val (bx, by) = busStart(bus) @@ -161,7 +166,7 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) RenderSystem.color3f(1, 1, 1) minecraft.getTextureManager.bind(Textures.GUI.Rack) - if (rack.isRelayEnabled) { + if (inventoryContainer.isRelayEnabled) { val (left, top, w, h) = relayModeUVs for ((x, y) <- wireRelay) { drawRect(stack, x, y, w, h, left, top) @@ -174,14 +179,14 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) val (scx, scy, scw, sch) = connectorSlaveUVs val (sbx, sby, sbw, sbh) = busSlaveBlankUVs val (spx, spy, spw, sph) = busSlavePresentUVs - for (mountable <- 0 until rack.getContainerSize) { + for (mountable <- 0 until inventoryContainer.otherInventory.getContainerSize) { val presence = inventoryContainer.nodePresence(mountable) // Draw connectable indicators next to item slots. val (cx, cy) = connectorStart(mountable) if (presence(0)) { drawRect(stack, cx, cy, mcw, mch, mcx, mcy) - rack.nodeMapping(mountable)(0) match { + inventoryContainer.nodeMapping(mountable)(0) match { case Some(side) => val bus = sideToBus(side) val (mwx, mwy, mww, mwh) = wireMasterUVs(bus) @@ -192,7 +197,7 @@ class Rack(id: Int, playerInventory: PlayerInventory, val rack: tileentity.Rack) case _ => } for (connectable <- 1 until 4) { - rack.nodeMapping(mountable)(connectable) match { + inventoryContainer.nodeMapping(mountable)(connectable) match { case Some(side) => val bus = sideToBus(side) val (swx, swy, sww, swh) = wireSlaveUVs(bus) diff --git a/src/main/scala/li/cil/oc/client/gui/Raid.scala b/src/main/scala/li/cil/oc/client/gui/Raid.scala index ab22e4e695..35e7419255 100644 --- a/src/main/scala/li/cil/oc/client/gui/Raid.scala +++ b/src/main/scala/li/cil/oc/client/gui/Raid.scala @@ -7,10 +7,15 @@ import li.cil.oc.client.Textures import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Raid(id: Int, playerInventory: PlayerInventory, val raid: tileentity.Raid) - extends DynamicGuiContainer(new container.Raid(container.ContainerTypes.RAID, id, playerInventory, raid), - playerInventory, raid.getName) { +object Raid { + def of(id: Int, playerInventory: PlayerInventory, raid: tileentity.Raid) = + new Raid(new container.Raid(container.ContainerTypes.RAID, id, playerInventory, raid), playerInventory, raid.getName) +} + +class Raid(state: container.Raid, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { RenderSystem.color3f(1, 1, 1) // Required under Linux. diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala index ea15d161cf..6d35bac712 100644 --- a/src/main/scala/li/cil/oc/client/gui/Relay.scala +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -13,11 +13,16 @@ import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.Rectangle2d import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import org.lwjgl.opengl.GL11 -class Relay(id: Int, playerInventory: PlayerInventory, val relay: tileentity.Relay) - extends DynamicGuiContainer(new container.Relay(container.ContainerTypes.RELAY, id, playerInventory, relay), - playerInventory, relay.getName) { +object Relay { + def of(id: Int, playerInventory: PlayerInventory, relay: tileentity.Relay) + = new Relay(new container.Relay(container.ContainerTypes.RELAY, id, playerInventory, relay), playerInventory, relay.getName) +} + +class Relay(state: container.Relay, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) { private val format = new DecimalFormat("#.##hz") diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 63623d69b7..17e09d267b 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -6,6 +6,7 @@ import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.internal.TextBuffer +import li.cil.oc.client.ComponentTracker import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.client.renderer.TextBufferRenderCache @@ -13,13 +14,14 @@ import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container import li.cil.oc.common.tileentity -import li.cil.oc.integration.opencomputers import li.cil.oc.util.RenderState +import net.minecraft.client.Minecraft import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.GL11 @@ -27,16 +29,22 @@ import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Robot) - extends DynamicGuiContainer(new container.Robot(container.ContainerTypes.ROBOT, id, playerInventory, robot), - playerInventory, StringTextComponent.EMPTY) +object Robot { + def of(id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) = + new Robot(new container.Robot(container.ContainerTypes.ROBOT, id, playerInventory, robot), playerInventory, StringTextComponent.EMPTY) +} + +class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) with traits.InputBuffer with INestedGuiEventHandler { - override protected val buffer: TextBuffer = robot.components.collect { - case Some(buffer: api.internal.TextBuffer) => buffer - }.headOption.orNull + override protected val buffer: TextBuffer = inventoryContainer.info.screenBuffer + .map(ComponentTracker.get(Minecraft.getInstance.level, _)) + .collectFirst { + case buffer: TextBuffer => buffer + }.orNull - override protected val hasKeyboard: Boolean = robot.info.components.map(api.Driver.driverFor(_, robot.getClass)).contains(opencomputers.DriverKeyboard) + override protected val hasKeyboard: Boolean = inventoryContainer.info.hasKeyboard private val withScreenHeight = 256 private val noScreenHeight = 108 @@ -53,9 +61,9 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob // Scroll offset for robot inventory. private var inventoryOffset = 0 - private def canScroll = robot.inventorySize > 16 + private def canScroll = inventoryContainer.otherInventory.getContainerSize > 16 - private def maxOffset = robot.inventorySize / 4 - 4 + private def maxOffset = inventoryContainer.otherInventory.getContainerSize / 4 - 4 private val slotSize = 18 @@ -85,11 +93,11 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob private val selectionStepV = 1 / selectionsStates.toFloat override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { - powerButton.toggled = robot.isRunning + powerButton.toggled = inventoryContainer.isRunning scrollButton.active = canScroll scrollButton.hoverOverride = isDragging - if (robot.inventorySize < 16 + inventoryOffset * 4) { - scrollTo(0) + if (inventoryContainer.otherInventory.getContainerSize < 16 + inventoryOffset * 4) { + if (inventoryOffset != 0) scrollTo(0) } super.render(stack, mouseX, mouseY, dt) } @@ -97,7 +105,7 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob override protected def init() { super.init() powerButton = new ImageButton(leftPos + 5, topPos + 153 - deltaY, 18, 18, new Button.IPressable { - override def onPress(b: Button) = ClientPacketSender.sendComputerPower(robot, !robot.isRunning) + override def onPress(b: Button) = ClientPacketSender.sendRobotPower(inventoryContainer, !inventoryContainer.isRunning) }, Textures.GUI.ButtonPower, canToggle = true) scrollButton = new ImageButton(leftPos + scrollX + 1, topPos + scrollY + 1, 6, 13, new Button.IPressable { override def onPress(b: Button) = Unit @@ -138,14 +146,13 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob val tooltip = new java.util.ArrayList[String] val format = Localization.Computer.Power + ": %d%% (%d/%d)" tooltip.add(format.format( - ((robot.globalBuffer / robot.globalBufferSize) * 100).toInt, - robot.globalBuffer.toInt, - robot.globalBufferSize.toInt)) + 100 * inventoryContainer.globalBuffer / inventoryContainer.globalBufferSize, + inventoryContainer.globalBuffer, inventoryContainer.globalBufferSize)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } if (powerButton.isMouseOver(mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.addAll(asJavaCollection(if (robot.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) + tooltip.addAll(asJavaCollection(if (inventoryContainer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } RenderState.popAttrib() @@ -156,9 +163,9 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob if (buffer != null) Textures.bind(Textures.GUI.Robot) else Textures.bind(Textures.GUI.RobotNoScreen) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) - power.level = robot.globalBuffer / robot.globalBufferSize + power.level = inventoryContainer.globalBuffer.toDouble / inventoryContainer.globalBufferSize drawWidgets(stack) - if (robot.inventorySize > 0) { + if (inventoryContainer.otherInventory.getContainerSize > 0) { drawSelection(stack) } @@ -231,7 +238,7 @@ class Robot(id: Int, playerInventory: PlayerInventory, val robot: tileentity.Rob } private def drawSelection(stack: MatrixStack) { - val slot = robot.selectedSlot - inventoryOffset * 4 + val slot = inventoryContainer.selectedSlot - inventoryOffset * 4 if (slot >= 0 && slot < 16) { RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index 5b51f8cd6f..6a3a91ad2c 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -11,27 +11,24 @@ import li.cil.oc.common.tileentity import net.minecraft.client.Minecraft import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent import scala.collection.JavaConverters.asJavaCollection -class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, val rack: Option[tileentity.Rack] = None, val slot: Int = 0) - extends DynamicGuiContainer(new container.Server(container.ContainerTypes.SERVER, id, playerInventory, serverInventory), - playerInventory, serverInventory.getName) +object Server { + def of(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, slot: Int = -1) + = new Server(new container.Server(container.ContainerTypes.SERVER, id, playerInventory, serverInventory, slot), playerInventory, serverInventory.getName) +} + +class Server(state: container.Server, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) with traits.LockedHotbar[container.Server] { protected var powerButton: ImageButton = _ - override def lockedStack = serverInventory.container + override def lockedStack = inventoryContainer.stack override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { - // Close GUI if item is removed from rack. - rack match { - case Some(t) if t.getItem(slot) != serverInventory.container => - onClose() - return - case _ => - } - powerButton.visible = !inventoryContainer.isItem powerButton.toggled = inventoryContainer.isRunning super.render(stack, mouseX, mouseY, dt) @@ -40,9 +37,8 @@ class Server(id: Int, playerInventory: PlayerInventory, serverInventory: ServerI override protected def init() { super.init() powerButton = new ImageButton(leftPos + 48, topPos + 33, 18, 18, new Button.IPressable { - override def onPress(b: Button) = rack match { - case Some(t) => ClientPacketSender.sendServerPower(t, slot, !inventoryContainer.isRunning) - case _ => + override def onPress(b: Button) = if (inventoryContainer.rackSlot >= 0) { + ClientPacketSender.sendServerPower(inventoryContainer, inventoryContainer.rackSlot, !inventoryContainer.isRunning) } }, Textures.GUI.ButtonPower, canToggle = true) addButton(powerButton) diff --git a/src/main/scala/li/cil/oc/client/gui/Tablet.scala b/src/main/scala/li/cil/oc/client/gui/Tablet.scala index c7c2a85b6d..169abf52e6 100644 --- a/src/main/scala/li/cil/oc/client/gui/Tablet.scala +++ b/src/main/scala/li/cil/oc/client/gui/Tablet.scala @@ -4,11 +4,16 @@ import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper import net.minecraft.entity.player.PlayerInventory +import net.minecraft.util.text.ITextComponent -class Tablet(id: Int, playerInventory: PlayerInventory, val tablet: TabletWrapper) - extends DynamicGuiContainer(new container.Tablet(container.ContainerTypes.TABLET, id, playerInventory, tablet), - playerInventory, tablet.getName) +object Tablet { + def of(id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) = + new Tablet(new container.Tablet(container.ContainerTypes.TABLET, id, playerInventory, tablet), playerInventory, tablet.getName) +} + +class Tablet(state: container.Tablet, playerInventory: PlayerInventory, name: ITextComponent) + extends DynamicGuiContainer(state, playerInventory, name) with traits.LockedHotbar[container.Tablet] { - override def lockedStack = tablet.stack + override def lockedStack = inventoryContainer.stack } diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index 348296abf9..df4d6bf5b8 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -41,7 +41,7 @@ abstract class GuiHandler { case t: tileentity.Rack if id == GuiType.ServerInRack.id => val slot = GuiType.extractSlot(y) val server = t.getMountable(slot).asInstanceOf[Server] - new container.Server(ContainerTypes.SERVER, containerId, player.inventory, server, Option(server)) + new container.Server(ContainerTypes.SERVER, containerId, player.inventory, server, slot) case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => val slot = GuiType.extractSlot(y) val drive = t.getMountable(slot).asInstanceOf[DiskDriveMountable] diff --git a/src/main/scala/li/cil/oc/common/container/Case.scala b/src/main/scala/li/cil/oc/common/container/Case.scala index c736da0dea..da006105c1 100644 --- a/src/main/scala/li/cil/oc/common/container/Case.scala +++ b/src/main/scala/li/cil/oc/common/container/Case.scala @@ -7,6 +7,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType +import net.minecraft.util.IntReferenceHolder import net.minecraft.util.text.ITextComponent class Case(selfType: ContainerType[_ <: Case], id: Int, playerInventory: PlayerInventory, computer: IInventory, tier: Int) @@ -50,6 +51,18 @@ class Case(selfType: ContainerType[_ <: Case], id: Int, playerInventory: PlayerI // Show the player's inventory. addPlayerInventorySlots(8, 84) + private val runningData = computer match { + case te: tileentity.Case => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = if (te.isRunning) 1 else 0 + + override def set(value: Int): Unit = te.setRunning(value != 0) + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def isRunning = runningData.get != 0 + override def stillValid(player: PlayerEntity) = super.stillValid(player) && (computer match { case te: tileentity.Case => te.canInteract(player.getName.getString) diff --git a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java index 7d54a64e10..bc25baede9 100644 --- a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java +++ b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java @@ -1,6 +1,7 @@ package li.cil.oc.common.container; import li.cil.oc.OpenComputers; +import net.minecraft.item.ItemStack; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.ContainerType; import net.minecraft.util.ResourceLocation; @@ -43,9 +44,10 @@ public static void registerContainers(RegistryEvent.Register> e }); register(e.getRegistry(), "charger", (id, plr, buff) -> new Charger(CHARGER, id, plr, new Inventory(1))); register(e.getRegistry(), "database", (id, plr, buff) -> { + ItemStack containerStack = buff.readItem(); int invSize = buff.readVarInt(); int tier = buff.readVarInt(); - return new Database(DATABASE, id, plr, new Inventory(invSize), tier); + return new Database(DATABASE, id, plr, containerStack, new Inventory(invSize), tier); }); register(e.getRegistry(), "disassembler", (id, plr, buff) -> new Disassembler(DISASSEMBLER, id, plr, new Inventory(1))); register(e.getRegistry(), "disk_drive", (id, plr, buff) -> new DiskDrive(DISK_DRIVE, id, plr, new Inventory(1))); @@ -62,15 +64,18 @@ public static void registerContainers(RegistryEvent.Register> e return new Robot(ROBOT, id, plr, new Inventory(100), info); }); register(e.getRegistry(), "server", (id, plr, buff) -> { + ItemStack containerStack = buff.readItem(); int invSize = buff.readVarInt(); int tier = buff.readVarInt(); - return new Server(SERVER, id, plr, new Inventory(invSize), tier); + int rackSlot = buff.readVarInt() - 1; + return new Server(SERVER, id, plr, containerStack, new Inventory(invSize), tier, rackSlot); }); register(e.getRegistry(), "tablet", (id, plr, buff) -> { + ItemStack containerStack = buff.readItem(); int invSize = buff.readVarInt(); String slot1 = buff.readUtf(32); int tier1 = buff.readVarInt(); - return new Tablet(TABLET, id, plr, new Inventory(invSize), slot1, tier1); + return new Tablet(TABLET, id, plr, containerStack, new Inventory(invSize), slot1, tier1); }); } diff --git a/src/main/scala/li/cil/oc/common/container/Database.scala b/src/main/scala/li/cil/oc/common/container/Database.scala index 16b60f76f8..3d1b0f33b8 100644 --- a/src/main/scala/li/cil/oc/common/container/Database.scala +++ b/src/main/scala/li/cil/oc/common/container/Database.scala @@ -9,11 +9,11 @@ import net.minecraft.inventory.container.ContainerType import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -class Database(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, databaseInventory: IInventory, tier: Int) +class Database(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, val container: ItemStack, databaseInventory: IInventory, val tier: Int) extends Player(selfType, id, playerInventory, databaseInventory) { def this(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) = - this(selfType, id, playerInventory, databaseInventory, databaseInventory.tier) + this(selfType, id, playerInventory, databaseInventory.container, databaseInventory, databaseInventory.tier) val rows = math.sqrt(databaseInventory.getContainerSize).ceil.toInt val offset = 8 + Array(3, 2, 0)(tier) * slotSize diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index cb62e91004..bd4f77fcd1 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -7,6 +7,8 @@ import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType import net.minecraft.item.ItemStack +import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.IntReferenceHolder import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn @@ -25,6 +27,69 @@ class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: Playe addPlayerInventorySlots(8, 66) + // This factor is used to make the energy values transferable using + // MCs 'progress bar' stuff, even though those internally send the + // values as shorts over the net (for whatever reason). + private val factor = 100 + + private val globalBufferData = droneInv match { + case droneInv: entity.DroneInventory => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = droneInv.drone.globalBuffer / factor + + override def set(value: Int): Unit = droneInv.drone.globalBuffer = value * factor + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def globalBuffer = globalBufferData.get * factor + + private val globalBufferSizeData = droneInv match { + case droneInv: entity.DroneInventory => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = droneInv.drone.globalBufferSize / factor + + override def set(value: Int): Unit = droneInv.drone.globalBufferSize = value * factor + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def globalBufferSize = globalBufferSizeData.get * factor + + private val runningData = droneInv match { + case droneInv: entity.DroneInventory => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = if (droneInv.drone.isRunning) 1 else 0 + + override def set(value: Int): Unit = droneInv.drone.setRunning(value != 0) + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def isRunning = runningData.get != 0 + + private val selectedSlotData = droneInv match { + case droneInv: entity.DroneInventory => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = droneInv.drone.selectedSlot + + override def set(value: Int): Unit = droneInv.drone.setSelectedSlot(value) + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def selectedSlot = selectedSlotData.get + + def statusText = synchronizedData.getString("statusText") + + override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { + droneInv match { + case droneInv: entity.DroneInventory => synchronizedData.putString("statusText", droneInv.drone.statusText) + case _ => + } + super.detectCustomDataChanges(nbt) + } + class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { def isValid = (0 until droneInv.getContainerSize).contains(getSlotIndex) diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index a10db0aac5..0cc3df66bd 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -19,15 +19,21 @@ class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: P def progress = synchronizedData.getDouble("progress") + def maxAmountMaterial = synchronizedData.getInt("maxAmountMaterial") + def amountMaterial = synchronizedData.getInt("amountMaterial") + def maxAmountInk = synchronizedData.getInt("maxAmountInk") + def amountInk = synchronizedData.getInt("amountInk") override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { printer match { case te: tileentity.Printer => { synchronizedData.putDouble("progress", if (te.isPrinting) te.progress / 100.0 else 0) + synchronizedData.putInt("maxAmountMaterial", te.maxAmountMaterial) synchronizedData.putInt("amountMaterial", te.amountMaterial) + synchronizedData.putInt("maxAmountInk", te.amountInk) synchronizedData.putInt("amountInk", te.amountInk) } case _ => diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index ee425298c2..708d838e5a 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -3,24 +3,28 @@ package li.cil.oc.common.container import li.cil.oc.api import li.cil.oc.client.Textures import li.cil.oc.common +import li.cil.oc.common.ComponentTracker import li.cil.oc.common.tileentity +import li.cil.oc.integration.opencomputers import li.cil.oc.util.SideTracker import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType import net.minecraft.item.ItemStack import net.minecraft.network.PacketBuffer +import net.minecraft.world.World import net.minecraft.util.IntReferenceHolder import net.minecraft.util.ResourceLocation import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn object RobotInfo { - def hasScreen(robot: tileentity.Robot) = robot.components.exists { - case Some(buffer: api.internal.TextBuffer) => true - case _ => false + def getScreenBuffer(robot: tileentity.Robot): Option[String] = robot.components.collectFirst { + case Some(buffer: api.internal.TextBuffer) if buffer.node != null => buffer.node.address } + def hasKeyboard(robot: tileentity.Robot) = robot.info.components.map(api.Driver.driverFor(_, robot.getClass)).contains(opencomputers.DriverKeyboard) + def readRobotInfo(buff: PacketBuffer): RobotInfo = { val mainInvSize = buff.readVarInt() val slot1 = buff.readUtf(32) @@ -29,8 +33,12 @@ object RobotInfo { val tier2 = buff.readVarInt() val slot3 = buff.readUtf(32) val tier3 = buff.readVarInt() - val hasScreen = buff.readBoolean() - new RobotInfo(mainInvSize, slot1, tier1, slot2, tier2, slot3, tier3, hasScreen) + val screenBuffer = buff.readBoolean() match { + case true => Some(buff.readUtf()) + case false => None + } + val hasKeyboard = buff.readBoolean() + new RobotInfo(mainInvSize, slot1, tier1, slot2, tier2, slot3, tier3, screenBuffer, hasKeyboard) } def writeRobotInfo(buff: PacketBuffer, info: RobotInfo) { @@ -41,19 +49,27 @@ object RobotInfo { buff.writeVarInt(info.tier2) buff.writeUtf(info.slot3, 32) buff.writeVarInt(info.tier3) - buff.writeBoolean(info.hasScreen) + info.screenBuffer match { + case Some(addr) => { + buff.writeBoolean(true) + buff.writeUtf(addr) + } + case _ => buff.writeBoolean(false) + } + buff.writeBoolean(info.hasKeyboard) } } class RobotInfo(val mainInvSize: Int, val slot1: String, val tier1: Int, - val slot2: String, val tier2: Int, val slot3: String, val tier3: Int, val hasScreen: Boolean) { + val slot2: String, val tier2: Int, val slot3: String, val tier3: Int, + val screenBuffer: Option[String], val hasKeyboard: Boolean) { def this(robot: tileentity.Robot) = this(robot.mainInventory.getContainerSize, robot.containerSlotType(1), robot.containerSlotTier(1), robot.containerSlotType(2), robot.containerSlotTier(2), robot.containerSlotType(3), robot.containerSlotTier(3), - RobotInfo.hasScreen(robot)) + RobotInfo.getScreenBuffer(robot), RobotInfo.hasKeyboard(robot)) } -class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: IInventory, info: RobotInfo) +class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: IInventory, val info: RobotInfo) extends Player(selfType, id, playerInventory, robot) { def this(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) = @@ -61,7 +77,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe private val withScreenHeight = 256 private val noScreenHeight = 108 - val deltaY: Int = if (info.hasScreen) 0 else withScreenHeight - noScreenHeight + val deltaY: Int = if (info.screenBuffer.isDefined) 0 else withScreenHeight - noScreenHeight addSlotToContainer(170 + 0 * slotSize, 232 - deltaY, common.Slot.Tool) addSlotToContainer(170 + 1 * slotSize, 232 - deltaY, info.slot1, info.tier1) @@ -90,7 +106,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe // values as shorts over the net (for whatever reason). private val factor = 100 - val globalBuffer = robot match { + private val globalBufferData = robot match { case te: tileentity.Robot => { addDataSlot(new IntReferenceHolder { override def get(): Int = te.globalBuffer.toInt / factor @@ -100,8 +116,9 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe } case _ => addDataSlot(IntReferenceHolder.standalone) } + def globalBuffer = globalBufferData.get * factor - val globalBufferSize = robot match { + private val globalBufferSizeData = robot match { case te: tileentity.Robot => { addDataSlot(new IntReferenceHolder { override def get(): Int = te.globalBufferSize.toInt / factor @@ -111,6 +128,31 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe } case _ => addDataSlot(IntReferenceHolder.standalone) } + def globalBufferSize = globalBufferSizeData.get * factor + + private val runningData = robot match { + case te: tileentity.Robot => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = if (te.isRunning) 1 else 0 + + override def set(value: Int): Unit = te.setRunning(value != 0) + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def isRunning = runningData.get != 0 + + private val selectedSlotData = robot match { + case te: tileentity.Robot => { + addDataSlot(new IntReferenceHolder { + override def get(): Int = te.selectedSlot + + override def set(value: Int): Unit = te.setSelectedSlot(value) + }) + } + case _ => addDataSlot(IntReferenceHolder.standalone) + } + def selectedSlot = selectedSlotData.get class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, var enabled: Boolean) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { diff --git a/src/main/scala/li/cil/oc/common/container/Server.scala b/src/main/scala/li/cil/oc/common/container/Server.scala index 1ac67a3e6f..9acafeeaec 100644 --- a/src/main/scala/li/cil/oc/common/container/Server.scala +++ b/src/main/scala/li/cil/oc/common/container/Server.scala @@ -7,16 +7,14 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType +import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT -class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: IInventory, tier: Int, val server: Option[component.Server]) +class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, serverInventory: IInventory, tier: Int, val rackSlot: Int) extends Player(selfType, id, playerInventory, serverInventory) { - def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: IInventory, tier: Int) = - this(selfType, id, playerInventory, serverInventory, tier, None) - - def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, server: Option[component.Server] = None) = - this(selfType, id, playerInventory, serverInventory, serverInventory.tier, server) + def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, rackSlot: Int = -1) = + this(selfType, id, playerInventory, serverInventory.container, serverInventory, serverInventory.tier, rackSlot) for (i <- 0 to 1) { val slot = InventorySlots.server(tier)(slots.size) @@ -53,8 +51,10 @@ class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: Pla addPlayerInventorySlots(8, 84) override def stillValid(player: PlayerEntity) = { - if (server.isDefined) super.stillValid(player) - else player == playerInventory.player + otherInventory match { + case _: component.Server => super.stillValid(player) + case _ => player == playerInventory.player + } } var isRunning = false @@ -68,8 +68,8 @@ class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: Pla override protected def detectCustomDataChanges(nbt: CompoundNBT): Unit = { super.detectCustomDataChanges(nbt) - server match { - case Some(s) => nbt.putBoolean("isRunning", s.machine.isRunning) + otherInventory match { + case s: component.Server => nbt.putBoolean("isRunning", s.machine.isRunning) case _ => nbt.putBoolean("isItem", true) } } diff --git a/src/main/scala/li/cil/oc/common/container/Tablet.scala b/src/main/scala/li/cil/oc/common/container/Tablet.scala index d1b7ad3fc9..fe2ed7a0ba 100644 --- a/src/main/scala/li/cil/oc/common/container/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/container/Tablet.scala @@ -5,12 +5,13 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType +import net.minecraft.item.ItemStack -class Tablet(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, tablet: IInventory, slot1: String, tier1: Int) +class Tablet(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, tablet: IInventory, slot1: String, tier1: Int) extends Player(selfType, id, playerInventory, tablet) { def this(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) = - this(selfType, id, playerInventory, tablet, tablet.containerSlotType, tablet.containerSlotTier) + this(selfType, id, playerInventory, tablet.stack, tablet, tablet.containerSlotType, tablet.containerSlotTier) addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, slot1, tier1)) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index b617befa80..02dcccb9f8 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -75,6 +75,8 @@ object Drone { val DataLightColor: DataParameter[Integer] = EntityDataManager.defineId(classOf[Drone], DataSerializers.INT) } +abstract class DroneInventory(val drone: Drone) extends Inventory + // internal.Rotatable is also in internal.Drone, but it wasn't since the start // so this is to ensure it is implemented here, in the very unlikely case that // someone decides to ship that specific version of the API. @@ -140,7 +142,7 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, override def stillValid(player: PlayerEntity) = false } - val mainInventory = new Inventory { + val mainInventory = new DroneInventory(this) { val items: Array[ItemStack] = Array.fill[ItemStack](8)(ItemStack.EMPTY) override def getContainerSize: Int = inventorySize @@ -151,7 +153,7 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, override def canPlaceItem(slot: Int, stack: ItemStack): Boolean = slot >= 0 && slot < getContainerSize - override def stillValid(player: PlayerEntity): Boolean = player.distanceToSqr(Drone.this) < 64 + override def stillValid(player: PlayerEntity): Boolean = player.distanceToSqr(drone) < 64 } val tank = new MultiTank { override def tankCount: Int = components.components.count { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 1a6a40d38b..004d1c254e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -393,7 +393,7 @@ class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with super.dispose() if (isClient) { Minecraft.getInstance.screen match { - case robotGui: gui.Robot if robotGui.robot == this => + case robotGui: gui.Robot if robotGui.inventoryContainer.otherInventory == this => robotGui.onClose() case _ => } @@ -600,7 +600,7 @@ class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with } else if (isClient) { Minecraft.getInstance.screen match { - case robotGui: gui.Robot if robotGui.robot == this => + case robotGui: gui.Robot if robotGui.inventoryContainer.otherInventory == this => robotGui.onClose() case _ => } diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index 49e1fb0f49..06132c61c0 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -10,7 +10,9 @@ import li.cil.oc.api.network.Connector import li.cil.oc.common.Achievement import li.cil.oc.common.PacketType import li.cil.oc.common.component.TextBuffer +import li.cil.oc.common.container import li.cil.oc.common.entity.Drone +import li.cil.oc.common.entity.DroneInventory import li.cil.oc.common.item.{Tablet, TabletWrapper} import li.cil.oc.common.item.data.DriveData import li.cil.oc.common.item.traits.FileSystemLike @@ -60,33 +62,32 @@ object PacketHandler extends CommonPacketHandler { } def onComputerPower(p: PacketParser): Unit = { - val entity = p.readBlockEntity[Computer]() + val containerId = p.readInt() val setPower = p.readBoolean() - entity match { - case Some(t) => p.player match { - case player: ServerPlayerEntity => trySetComputerPower(t.machine, setPower, player) - case _ => + p.player.containerMenu match { + case computer: container.Case if computer.containerId == containerId => { + (computer.otherInventory, p.player) match { + case (te: Computer, player: ServerPlayerEntity) => trySetComputerPower(te.machine, setPower, player) + case _ => + } } - case _ => // Invalid packet. + case _ => // Invalid packet or container closed early. } } def onServerPower(p: PacketParser): Unit = { - val entity = p.readBlockEntity[Rack]() + val containerId = p.readInt() val index = p.readInt() val setPower = p.readBoolean() - entity match { - case Some(t) => { - val mountableIndex = index - t.getMountable(mountableIndex) match { - case server: Server => p.player match { - case player: ServerPlayerEntity => trySetComputerPower(server.machine, setPower, player) - case _ => // Invalid packet. - } + p.player.containerMenu match { + case server: container.Server if server.containerId == containerId => { + (server.otherInventory, p.player) match { + case (comp: component.Server, player: ServerPlayerEntity) if comp.rack.getMountable(index) == comp => + trySetComputerPower(comp.machine, setPower, player) case _ => // Invalid packet. } } - case _ => // Invalid packet. + case _ => // Invalid packet or container closed early. } } @@ -124,18 +125,16 @@ object PacketHandler extends CommonPacketHandler { } def onDronePower(p: PacketParser): Unit = { - val entity = p.readEntity[Drone]() + val containerId = p.readInt() val power = p.readBoolean() - entity match { - case Some(drone) => p.player match { - case player: ServerPlayerEntity => - if (power) { - drone.preparePowerUp() - } - trySetComputerPower(drone.machine, power, player) - case _ => + p.player.containerMenu match { + case drone: container.Drone if drone.containerId == containerId => { + (drone.otherInventory, p.player) match { + case (droneInv: DroneInventory, player: ServerPlayerEntity) => trySetComputerPower(droneInv.drone.machine, power, player) + case _ => + } } - case _ => // Invalid packet. + case _ => // Invalid packet or container closed early. } } @@ -242,42 +241,51 @@ object PacketHandler extends CommonPacketHandler { } def onRackMountableMapping(p: PacketParser): Unit = { - val entity = p.readBlockEntity[Rack]() + val containerId = p.readInt() val mountableIndex = p.readInt() val nodeIndex = p.readInt() val side = p.readDirection() - entity match { - case Some(t) => p.player match { - case player: ServerPlayerEntity if t.stillValid(player) => - t.connect(mountableIndex, nodeIndex, side) - case _ => + p.player.containerMenu match { + case rack: container.Rack if rack.containerId == containerId => { + (rack.otherInventory, p.player) match { + case (t: Rack, player: ServerPlayerEntity) if t.stillValid(player) => + t.connect(mountableIndex, nodeIndex, side) + case _ => + } } - case _ => // Invalid packet. + case _ => // Invalid packet or container closed early. } } def onRackRelayState(p: PacketParser): Unit = { - val entity = p.readBlockEntity[Rack]() + val containerId = p.readInt() val enabled = p.readBoolean() - entity match { - case Some(t) => p.player match { - case player: ServerPlayerEntity if t.stillValid(player) => + p.player.containerMenu match { + case rack: container.Rack if rack.containerId == containerId => { + (rack.otherInventory, p.player) match { + case (t: Rack, player: ServerPlayerEntity) if t.stillValid(player) => t.isRelayEnabled = enabled - case _ => + case _ => + } } - case _ => // Invalid packet. + case _ => // Invalid packet or container closed early. } } def onRobotAssemblerStart(p: PacketParser): Unit = { - val entity = p.readBlockEntity[Assembler]() - entity match { - case Some(assembler) => - if (assembler.start(p.player match { - case player: ServerPlayerEntity => player.isCreative - case _ => false - })) assembler.output.foreach(stack => Achievement.onAssemble(stack, p.player)) - case _ => // Invalid packet. + val containerId = p.readInt() + p.player.containerMenu match { + case assembler: container.Assembler if assembler.containerId == containerId => { + assembler.assembler match { + case te: Assembler => + if (te.start(p.player match { + case player: ServerPlayerEntity => player.isCreative + case _ => false + })) te.output.foreach(stack => Achievement.onAssemble(stack, p.player)) + case _ => + } + } + case _ => // Invalid packet or container closed early. } } diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index 4fc4b9fe35..4ccc9a31dd 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -129,7 +129,7 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit case _ => 0 } - override def stillValid(player: PlayerEntity): Boolean = rack.stillValid(player) + override def stillValid(player: PlayerEntity): Boolean = rack.stillValid(player) && rack.indexOfMountable(this) >= 0 // ----------------------------------------------------------------------- // // ItemStackInventory From 80328fa0dbbf552accf43b4767311e0ce81024cd Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 25 Aug 2022 00:50:43 +0200 Subject: [PATCH 047/159] Replace GuiHandler with networked containers --- src/main/scala/li/cil/oc/OpenComputers.scala | 3 - .../scala/li/cil/oc/client/GuiHandler.scala | 152 ------------------ src/main/scala/li/cil/oc/client/Manual.scala | 4 +- src/main/scala/li/cil/oc/client/Proxy.scala | 13 -- .../scala/li/cil/oc/client/gui/Adapter.scala | 7 - .../li/cil/oc/client/gui/Assembler.scala | 7 - .../scala/li/cil/oc/client/gui/Case.scala | 6 - .../scala/li/cil/oc/client/gui/Charger.scala | 7 - .../scala/li/cil/oc/client/gui/Database.scala | 7 - .../li/cil/oc/client/gui/Disassembler.scala | 7 - .../li/cil/oc/client/gui/DiskDrive.scala | 8 - .../scala/li/cil/oc/client/gui/Drone.scala | 7 - .../scala/li/cil/oc/client/gui/Printer.scala | 7 - .../scala/li/cil/oc/client/gui/Rack.scala | 6 - .../scala/li/cil/oc/client/gui/Raid.scala | 7 - .../scala/li/cil/oc/client/gui/Relay.scala | 6 - .../scala/li/cil/oc/client/gui/Robot.scala | 7 - .../scala/li/cil/oc/client/gui/Server.scala | 7 - .../scala/li/cil/oc/client/gui/Tablet.scala | 7 - .../scala/li/cil/oc/common/GuiHandler.scala | 106 ------------ src/main/scala/li/cil/oc/common/GuiType.scala | 56 ------- src/main/scala/li/cil/oc/common/Proxy.scala | 18 --- .../li/cil/oc/common/block/Adapter.scala | 8 +- .../li/cil/oc/common/block/Assembler.scala | 8 +- .../scala/li/cil/oc/common/block/Case.scala | 8 +- .../li/cil/oc/common/block/Charger.scala | 8 +- .../li/cil/oc/common/block/Disassembler.scala | 9 +- .../li/cil/oc/common/block/DiskDrive.scala | 8 +- .../li/cil/oc/common/block/Printer.scala | 8 +- .../scala/li/cil/oc/common/block/Rack.scala | 8 +- .../scala/li/cil/oc/common/block/Raid.scala | 8 +- .../scala/li/cil/oc/common/block/Relay.scala | 9 +- .../li/cil/oc/common/block/RobotProxy.scala | 9 +- .../scala/li/cil/oc/common/block/Screen.scala | 10 +- .../li/cil/oc/common/block/Waypoint.scala | 8 +- .../li/cil/oc/common/block/traits/GUI.scala | 14 +- .../oc/common/container/ContainerTypes.java | 91 +++++++++++ .../li/cil/oc/common/container/Database.scala | 3 - .../li/cil/oc/common/container/Robot.scala | 3 - .../li/cil/oc/common/container/Server.scala | 3 - .../li/cil/oc/common/container/Tablet.scala | 3 - .../scala/li/cil/oc/common/entity/Drone.scala | 20 ++- .../common/inventory/DatabaseInventory.scala | 13 +- .../DiskDriveMountableInventory.scala | 15 +- .../oc/common/inventory/ServerInventory.scala | 11 +- .../oc/common/item/DiskDriveMountable.scala | 16 +- .../scala/li/cil/oc/common/item/Server.scala | 19 ++- .../scala/li/cil/oc/common/item/Tablet.scala | 28 +++- .../li/cil/oc/common/item/Terminal.scala | 35 +++- .../cil/oc/common/item/UpgradeDatabase.scala | 13 +- .../common/item/traits/FileSystemLike.scala | 9 +- .../oc/common/template/ServerTemplate.scala | 2 + .../li/cil/oc/common/tileentity/Adapter.scala | 15 +- .../cil/oc/common/tileentity/Assembler.scala | 17 +- .../li/cil/oc/common/tileentity/Case.scala | 11 +- .../li/cil/oc/common/tileentity/Charger.scala | 13 +- .../oc/common/tileentity/Disassembler.scala | 13 +- .../cil/oc/common/tileentity/DiskDrive.scala | 14 +- .../li/cil/oc/common/tileentity/Printer.scala | 14 +- .../li/cil/oc/common/tileentity/Rack.scala | 12 +- .../li/cil/oc/common/tileentity/Raid.scala | 13 +- .../li/cil/oc/common/tileentity/Relay.scala | 13 +- .../li/cil/oc/common/tileentity/Robot.scala | 16 +- .../InventoryProviderServer.scala | 2 + .../scala/li/cil/oc/server/GuiHandler.scala | 8 - .../server/component/DiskDriveMountable.scala | 28 +++- .../li/cil/oc/server/component/Server.scala | 11 +- 67 files changed, 502 insertions(+), 560 deletions(-) delete mode 100644 src/main/scala/li/cil/oc/client/GuiHandler.scala delete mode 100644 src/main/scala/li/cil/oc/common/GuiHandler.scala delete mode 100644 src/main/scala/li/cil/oc/common/GuiType.scala delete mode 100644 src/main/scala/li/cil/oc/server/GuiHandler.scala diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 1c6f77a55c..e547ab06ba 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -51,9 +51,6 @@ object OpenComputers { case Some(oc) => oc case _ => throw new IllegalStateException("not initialized") } - - @Deprecated - def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = proxy.openGui(player, guiId, world, x, y, z) } @Mod(OpenComputers.ID) diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala deleted file mode 100644 index ffdb64338a..0000000000 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ /dev/null @@ -1,152 +0,0 @@ -package li.cil.oc.client - -import com.google.common.base.Strings -import li.cil.oc.Localization -import li.cil.oc.Settings -import li.cil.oc.api -import li.cil.oc.common.GuiType -import li.cil.oc.common.component -import li.cil.oc.common.entity -import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventory, ServerInventory} -import li.cil.oc.common.item -import li.cil.oc.common.tileentity -import li.cil.oc.common.tileentity.traits.TileEntity -import li.cil.oc.common.{GuiHandler => CommonGuiHandler} -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.client.Minecraft -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.world.World -import net.minecraft.item.ItemStack - -@Deprecated -object GuiHandler extends CommonGuiHandler { - override def getClientGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = { - GuiType.Categories.get(id) match { - case Some(GuiType.Category.Block) => - world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { - case t: tileentity.Adapter if id == GuiType.Adapter.id => - gui.Adapter.of(containerId, player.inventory, t) - case t: tileentity.Assembler if id == GuiType.Assembler.id => - gui.Assembler.of(containerId, player.inventory, t) - case t: tileentity.Case if id == GuiType.Case.id => - gui.Case.of(containerId, player.inventory, t) - case t: tileentity.Charger if id == GuiType.Charger.id => - gui.Charger.of(containerId, player.inventory, t) - case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - gui.Disassembler.of(containerId, player.inventory, t) - case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - gui.DiskDrive.of(containerId, player.inventory, t) - case t: tileentity.Printer if id == GuiType.Printer.id => - gui.Printer.of(containerId, player.inventory, t) - case t: tileentity.Rack if id == GuiType.Rack.id => - gui.Rack.of(containerId, player.inventory, t) - case t: tileentity.Raid if id == GuiType.Raid.id => - gui.Raid.of(containerId, player.inventory, t) - case t: tileentity.Relay if id == GuiType.Relay.id => - gui.Relay.of(containerId, player.inventory, t) - case t: tileentity.RobotProxy if id == GuiType.Robot.id => - gui.Robot.of(containerId, player.inventory, t.robot) - case t: tileentity.Screen if id == GuiType.Screen.id => - new gui.Screen(t.origin.buffer, t.tier > 0, () => t.origin.hasKeyboard, () => t.origin.buffer.isRenderingEnabled) - case t: tileentity.Rack if id == GuiType.ServerInRack.id => - val slot = GuiType.extractSlot(y) - gui.Server.of(containerId, player.inventory, new ServerInventory { - override def container = t.getItem(slot) - - override def stillValid(player: PlayerEntity) = t.stillValid(player) - }, slot) - case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => - val slot = GuiType.extractSlot(y) - gui.DiskDrive.of(containerId, player.inventory, new DiskDriveMountableInventory { - override def container: ItemStack = t.getItem(slot) - - override def stillValid(player: PlayerEntity): Boolean = t.stillValid(player) - }) - case t: tileentity.Waypoint if id == GuiType.Waypoint.id => - new gui.Waypoint(t) - case _ => null - } - case Some(GuiType.Category.Entity) => - world.getEntity(x) match { - case drone: entity.Drone if id == GuiType.Drone.id => - gui.Drone.of(containerId, player.inventory, drone) - case _ => null - } - case Some(GuiType.Category.Item) => { - val itemStackInUse = getItemStackInUse(id, player) - itemStackInUse.getItem match { - case drive: item.traits.FileSystemLike if id == GuiType.Drive.id => - new gui.Drive(player.inventory, () => itemStackInUse) - case database: item.UpgradeDatabase if id == GuiType.Database.id => - gui.Database.of(containerId, player.inventory, new DatabaseInventory { - override def container = itemStackInUse - - override def stillValid(player: PlayerEntity) = player == player - }) - case server: item.Server if id == GuiType.Server.id => - gui.Server.of(containerId, player.inventory, new ServerInventory { - override def container = itemStackInUse - - override def stillValid(player: PlayerEntity) = player == player - }) - case tablet: item.Tablet if id == GuiType.Tablet.id => - val stack = itemStackInUse - if (stack.hasTag) { - item.Tablet.get(stack, player).components.collect { - case Some(buffer: api.internal.TextBuffer) => buffer - }.headOption match { - case Some(buffer: api.internal.TextBuffer) => new gui.Screen(buffer, true, () => true, () => buffer.isRenderingEnabled) - case _ => null - } - } - else null - case tablet: item.Tablet if id == GuiType.TabletInner.id => - val stack = itemStackInUse - if (stack.hasTag) { - gui.Tablet.of(containerId, player.inventory, item.Tablet.get(stack, player)) - } - else null - case _: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => - gui.DiskDrive.of(containerId, player.inventory, new DiskDriveMountableInventory { - override def container = itemStackInUse - override def stillValid(activePlayer : PlayerEntity): Boolean = activePlayer == player - }) - case terminal: item.Terminal if id == GuiType.Terminal.id => - val stack = itemStackInUse - if (stack.hasTag) { - val address = stack.getTag.getString(Settings.namespace + "server") - val key = stack.getTag.getString(Settings.namespace + "key") - if (!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(address)) { - component.TerminalServer.loaded.find(address) match { - case Some(term) if term != null && term.rack != null => term.rack match { - case rack: TileEntity with api.internal.Rack => - def inRange = player.isAlive && !rack.isRemoved && player.distanceToSqr(rack.x + 0.5, rack.y + 0.5, rack.z + 0.5) < term.range * term.range - if (inRange) { - if (term.sidedKeys.contains(key)) return new gui.Screen(term.buffer, true, () => true, () => { - // Check if someone else bound a term to our server. - if (stack.getTag.getString(Settings.namespace + "key") != key) Minecraft.getInstance.popGuiLayer - // Check whether we're still in range. - if (!inRange) Minecraft.getInstance.popGuiLayer - true - }) - else player.displayClientMessage(Localization.Terminal.InvalidKey, true) - } - else player.displayClientMessage(Localization.Terminal.OutOfRange, true) - case _ => // Eh? - } - case _ => player.displayClientMessage(Localization.Terminal.OutOfRange, true) - } - } - } - null - case _ => null - } - } - case Some(GuiType.Category.None) => - if (id == GuiType.Manual.id) new gui.Manual() - else null - case _ => null - } - } -} diff --git a/src/main/scala/li/cil/oc/client/Manual.scala b/src/main/scala/li/cil/oc/client/Manual.scala index f5eaeaf0dd..7b0ec8132d 100644 --- a/src/main/scala/li/cil/oc/client/Manual.scala +++ b/src/main/scala/li/cil/oc/client/Manual.scala @@ -8,7 +8,6 @@ import li.cil.oc.api.manual.ImageProvider import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.PathProvider import li.cil.oc.api.manual.TabIconRenderer -import li.cil.oc.common.GuiType import net.minecraft.client.Minecraft import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -109,7 +108,8 @@ object Manual extends ManualAPI { override def openFor(player: PlayerEntity): Unit = { if (player.level.isClientSide) { - OpenComputers.openGui(player, GuiType.Manual.id, player.level, 0, 0, 0) + val mc = Minecraft.getInstance + if (player == mc.player) mc.pushGuiLayer(new gui.Manual()) } } diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 69ca5442fd..2e5d197c5e 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -97,19 +97,6 @@ private[oc] class Proxy extends CommonProxy { else RenderSystem.recordRenderCall(call) } - override def getGuiHandler(): common.GuiHandler = client.GuiHandler - - @Deprecated - override def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = { - player match { - case _: ClientPlayerEntity => { - val screen = getGuiHandler.getClientGuiElement(guiId, 0, player, world, x, y, z).asInstanceOf[Screen] - if (screen != null) Minecraft.getInstance.pushGuiLayer(screen) - } - case _ => super.openGui(player, guiId, world, x, y, z) - } - } - override def registerModel(instance: Item, id: String): Unit = ModelInitialization.registerModel(instance, id) override def registerModel(instance: Block, id: String): Unit = ModelInitialization.registerModel(instance, id) diff --git a/src/main/scala/li/cil/oc/client/gui/Adapter.scala b/src/main/scala/li/cil/oc/client/gui/Adapter.scala index 8a4076c7f2..475685f0ba 100644 --- a/src/main/scala/li/cil/oc/client/gui/Adapter.scala +++ b/src/main/scala/li/cil/oc/client/gui/Adapter.scala @@ -1,16 +1,9 @@ package li.cil.oc.client.gui -import li.cil.oc.Localization import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Adapter { - def of(id: Int, playerInventory: PlayerInventory, adapter: tileentity.Adapter): Adapter = - new Adapter(new container.Adapter(container.ContainerTypes.ADAPTER, id, playerInventory, adapter), playerInventory, adapter.getName) -} - class Adapter(state: container.Adapter, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index b0653ffe2e..2ce7fbad45 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -9,22 +9,15 @@ import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container import li.cil.oc.common.container.ComponentSlot import li.cil.oc.common.template.AssemblerTemplates -import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.container.Slot import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ -object Assembler { - def of(id: Int, playerInventory: PlayerInventory, assembler: tileentity.Assembler) = - new Assembler(new container.Assembler(container.ContainerTypes.ASSEMBLER, id, playerInventory, assembler), playerInventory, StringTextComponent.EMPTY) -} - class Assembler(state: container.Assembler, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index 193af45266..27ab95b807 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -6,7 +6,6 @@ import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent @@ -14,11 +13,6 @@ import net.minecraft.util.text.ITextComponent import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -object Case { - def of(id: Int, playerInventory: PlayerInventory, computer: tileentity.Case) = - new Case(new container.Case(container.ContainerTypes.CASE, id, playerInventory, computer, computer.tier), playerInventory, computer.getName) -} - class Case(state: container.Case, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Charger.scala b/src/main/scala/li/cil/oc/client/gui/Charger.scala index e8eb28c1c5..afb106a42c 100644 --- a/src/main/scala/li/cil/oc/client/gui/Charger.scala +++ b/src/main/scala/li/cil/oc/client/gui/Charger.scala @@ -1,16 +1,9 @@ package li.cil.oc.client.gui -import li.cil.oc.Localization import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Charger { - def of(id: Int, playerInventory: PlayerInventory, charger: tileentity.Charger) = - new Charger(new container.Charger(container.ContainerTypes.CHARGER, id, playerInventory, charger), playerInventory, charger.getName) -} - class Charger(state: container.Charger, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 8ff9a9dc7b..54be9c2f7e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -5,15 +5,8 @@ import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import li.cil.oc.common.Tier import li.cil.oc.common.container -import li.cil.oc.common.inventory.DatabaseInventory import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent - -object Database { - def of(id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) = - new Database(new container.Database(container.ContainerTypes.DATABASE, id, playerInventory, databaseInventory), playerInventory, StringTextComponent.EMPTY) -} class Database(state: container.Database, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 14cfec7911..f48e52235f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -2,19 +2,12 @@ package li.cil.oc.client.gui import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem -import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Disassembler { - def of(id: Int, playerInventory: PlayerInventory, disassembler: tileentity.Disassembler) = - new Disassembler(new container.Disassembler(container.ContainerTypes.DISASSEMBLER, id, playerInventory, disassembler), playerInventory, disassembler.getName) -} - class Disassembler(state: container.Disassembler, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala index cbe01ba47c..0db7dd3b8e 100644 --- a/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/client/gui/DiskDrive.scala @@ -1,17 +1,9 @@ package li.cil.oc.client.gui -import li.cil.oc.Localization import li.cil.oc.common.container -import li.cil.oc.common.inventory.SimpleInventory -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object DiskDrive { - def of(id: Int, playerInventory: PlayerInventory, drive: SimpleInventory) = - new DiskDrive(new container.DiskDrive(container.ContainerTypes.DISK_DRIVE, id, playerInventory, drive), playerInventory, drive.getName) -} - class DiskDrive(state: container.DiskDrive, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { } diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 310f166039..945d0c502e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -9,7 +9,6 @@ import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.font.TextBufferRenderData import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.common.entity import li.cil.oc.util.PackedColor import li.cil.oc.util.RenderState import li.cil.oc.util.TextBuffer @@ -18,17 +17,11 @@ import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -object Drone { - def of(id: Int, playerInventory: PlayerInventory, drone: entity.Drone) = - new Drone(new container.Drone(container.ContainerTypes.DRONE, id, playerInventory, drone.mainInventory), playerInventory, StringTextComponent.EMPTY) -} - class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) with traits.DisplayBuffer { diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index 2727f87ce6..38d12eee33 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -2,21 +2,14 @@ package li.cil.oc.client.gui import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem -import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar import li.cil.oc.common.container import li.cil.oc.common.container.ComponentSlot -import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Printer { - def of(id: Int, playerInventory: PlayerInventory, printer: tileentity.Printer) - = new Printer(new container.Printer(container.ContainerTypes.PRINTER, id, playerInventory, printer), playerInventory, printer.getName) -} - class Printer(state: container.Printer, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index f4ff8b33f9..4861af022e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -6,7 +6,6 @@ import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.renderer.Tessellator @@ -19,11 +18,6 @@ import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection -object Rack { - def of(id: Int, playerInventory: PlayerInventory, rack: tileentity.Rack) = - new Rack(new container.Rack(container.ContainerTypes.RACK, id, playerInventory, rack), playerInventory, rack.getName) -} - class Rack(state: container.Rack, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Raid.scala b/src/main/scala/li/cil/oc/client/gui/Raid.scala index 35e7419255..ffd2ba6706 100644 --- a/src/main/scala/li/cil/oc/client/gui/Raid.scala +++ b/src/main/scala/li/cil/oc/client/gui/Raid.scala @@ -2,18 +2,11 @@ package li.cil.oc.client.gui import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem -import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Raid { - def of(id: Int, playerInventory: PlayerInventory, raid: tileentity.Raid) = - new Raid(new container.Raid(container.ContainerTypes.RAID, id, playerInventory, raid), playerInventory, raid.getName) -} - class Raid(state: container.Raid, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala index 6d35bac712..93684d6fb7 100644 --- a/src/main/scala/li/cil/oc/client/gui/Relay.scala +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -7,7 +7,6 @@ import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.common.container -import li.cil.oc.common.tileentity import net.minecraft.client.Minecraft import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.Rectangle2d @@ -16,11 +15,6 @@ import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent import org.lwjgl.opengl.GL11 -object Relay { - def of(id: Int, playerInventory: PlayerInventory, relay: tileentity.Relay) - = new Relay(new container.Relay(container.ContainerTypes.RELAY, id, playerInventory, relay), playerInventory, relay.getName) -} - class Relay(state: container.Relay, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) { diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 17e09d267b..79ac20c97f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -13,7 +13,6 @@ import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.gui.INestedGuiEventHandler @@ -22,18 +21,12 @@ import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.GL11 import scala.collection.JavaConverters.asJavaCollection import scala.collection.convert.ImplicitConversionsToJava._ -object Robot { - def of(id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) = - new Robot(new container.Robot(container.ContainerTypes.ROBOT, id, playerInventory, robot), playerInventory, StringTextComponent.EMPTY) -} - class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) with traits.InputBuffer with INestedGuiEventHandler { diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index 6a3a91ad2c..4d7ad48268 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -6,8 +6,6 @@ import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.common.inventory.ServerInventory -import li.cil.oc.common.tileentity import net.minecraft.client.Minecraft import net.minecraft.client.gui.widget.button.Button import net.minecraft.entity.player.PlayerInventory @@ -15,11 +13,6 @@ import net.minecraft.util.text.ITextComponent import scala.collection.JavaConverters.asJavaCollection -object Server { - def of(id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, slot: Int = -1) - = new Server(new container.Server(container.ContainerTypes.SERVER, id, playerInventory, serverInventory, slot), playerInventory, serverInventory.getName) -} - class Server(state: container.Server, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) with traits.LockedHotbar[container.Server] { diff --git a/src/main/scala/li/cil/oc/client/gui/Tablet.scala b/src/main/scala/li/cil/oc/client/gui/Tablet.scala index 169abf52e6..d0816a535e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Tablet.scala +++ b/src/main/scala/li/cil/oc/client/gui/Tablet.scala @@ -1,16 +1,9 @@ package li.cil.oc.client.gui -import li.cil.oc.Localization import li.cil.oc.common.container -import li.cil.oc.common.item.TabletWrapper import net.minecraft.entity.player.PlayerInventory import net.minecraft.util.text.ITextComponent -object Tablet { - def of(id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) = - new Tablet(new container.Tablet(container.ContainerTypes.TABLET, id, playerInventory, tablet), playerInventory, tablet.getName) -} - class Tablet(state: container.Tablet, playerInventory: PlayerInventory, name: ITextComponent) extends DynamicGuiContainer(state, playerInventory, name) with traits.LockedHotbar[container.Tablet] { diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala deleted file mode 100644 index df4d6bf5b8..0000000000 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ /dev/null @@ -1,106 +0,0 @@ -package li.cil.oc.common - -import li.cil.oc.common.container.ContainerTypes -import li.cil.oc.common.inventory.{DatabaseInventory, DiskDriveMountableInventory, ServerInventory} -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.ExtendedWorld._ -import li.cil.oc.server.component.{DiskDriveMountable, Server} -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.ItemStack -import net.minecraft.util.Hand -import net.minecraft.world.World - -@Deprecated -abstract class GuiHandler { - def getServerGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = { - GuiType.Categories.get(id) match { - case Some(GuiType.Category.Block) => - world.getBlockEntity(BlockPosition(x, GuiType.extractY(y), z)) match { - case t: tileentity.Adapter if id == GuiType.Adapter.id => - new container.Adapter(ContainerTypes.ADAPTER, containerId, player.inventory, t) - case t: tileentity.Assembler if id == GuiType.Assembler.id => - new container.Assembler(ContainerTypes.ASSEMBLER, containerId, player.inventory, t) - case t: tileentity.Charger if id == GuiType.Charger.id => - new container.Charger(ContainerTypes.CHARGER, containerId, player.inventory, t) - case t: tileentity.Case if id == GuiType.Case.id => - new container.Case(ContainerTypes.CASE, containerId, player.inventory, t, t.tier) - case t: tileentity.Disassembler if id == GuiType.Disassembler.id => - new container.Disassembler(ContainerTypes.DISASSEMBLER, containerId, player.inventory, t) - case t: tileentity.DiskDrive if id == GuiType.DiskDrive.id => - new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, t) - case t: tileentity.Printer if id == GuiType.Printer.id => - new container.Printer(ContainerTypes.PRINTER, containerId, player.inventory, t) - case t: tileentity.Raid if id == GuiType.Raid.id => - new container.Raid(ContainerTypes.RAID, containerId, player.inventory, t) - case t: tileentity.Relay if id == GuiType.Relay.id => - new container.Relay(ContainerTypes.RELAY, containerId, player.inventory, t) - case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new container.Robot(ContainerTypes.ROBOT, containerId, player.inventory, t.robot) - case t: tileentity.Rack if id == GuiType.Rack.id => - new container.Rack(ContainerTypes.RACK, containerId, player.inventory, t) - case t: tileentity.Rack if id == GuiType.ServerInRack.id => - val slot = GuiType.extractSlot(y) - val server = t.getMountable(slot).asInstanceOf[Server] - new container.Server(ContainerTypes.SERVER, containerId, player.inventory, server, slot) - case t: tileentity.Rack if id == GuiType.DiskDriveMountableInRack.id => - val slot = GuiType.extractSlot(y) - val drive = t.getMountable(slot).asInstanceOf[DiskDriveMountable] - new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, drive) - case _ => null - } - case Some(GuiType.Category.Entity) => - world.getEntity(x) match { - case drone: entity.Drone if id == GuiType.Drone.id => - new container.Drone(ContainerTypes.DRONE, containerId, player.inventory, drone.mainInventory) - case _ => null - } - case Some(GuiType.Category.Item) => { - val itemStackInUse = getItemStackInUse(id, player) - itemStackInUse.getItem match { - case database: item.UpgradeDatabase if id == GuiType.Database.id => - new container.Database(ContainerTypes.DATABASE, containerId, player.inventory, new DatabaseInventory { - override def container = itemStackInUse - - override def stillValid(player: PlayerEntity) = player == player - }) - case server: item.Server if id == GuiType.Server.id => - new container.Server(ContainerTypes.SERVER, containerId, player.inventory, new ServerInventory { - override def container = itemStackInUse - - override def stillValid(player: PlayerEntity) = player == player - }) - case tablet: item.Tablet if id == GuiType.TabletInner.id => - val stack = itemStackInUse - if (stack.hasTag) - new container.Tablet(ContainerTypes.TABLET, containerId, player.inventory, item.Tablet.get(stack, player)) - else - null - case drive: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => - new container.DiskDrive(ContainerTypes.DISK_DRIVE, containerId, player.inventory, new DiskDriveMountableInventory { - override def container: ItemStack = itemStackInUse - - override def stillValid(player: PlayerEntity) = player == player - }) - case _ => null - } - } - case _ => null - } - } - - def getClientGuiElement(id: Int, containerId: Int, player: PlayerEntity, world: World, x: Int, y: Int, z: Int): AnyRef = null - - def getItemStackInUse(id: Int, player: PlayerEntity): ItemStack = { - val mainItem: ItemStack = player.getItemInHand(Hand.MAIN_HAND) - mainItem.getItem match { - case drive: item.traits.FileSystemLike if id == GuiType.Drive.id => mainItem - case database: item.UpgradeDatabase if id == GuiType.Database.id => mainItem - case server: item.Server if id == GuiType.Server.id => mainItem - case tablet: item.Tablet if id == GuiType.Tablet.id => mainItem - case tablet: item.Tablet if id == GuiType.TabletInner.id => mainItem - case terminal: item.Terminal if id == GuiType.Terminal.id => mainItem - case drive: item.DiskDriveMountable if id == GuiType.DiskDriveMountable.id => mainItem - case _ => player.getItemInHand(Hand.OFF_HAND) - } - } -} diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala deleted file mode 100644 index ee001fc5b6..0000000000 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ /dev/null @@ -1,56 +0,0 @@ -package li.cil.oc.common - -import li.cil.oc.util.ScalaEnum - -import scala.collection.mutable - -object GuiType extends ScalaEnum { - val Categories = mutable.Map.empty[Int, Category.Value] - - sealed trait EnumVal extends Value { - def id = ordinal - def subType: GuiType.Category.Value - Categories += ordinal -> subType - } - - val Adapter = new EnumVal { def name = "Adapter"; def subType = GuiType.Category.Block } - val Assembler = new EnumVal { def name = "Assembler"; def subType = GuiType.Category.Block } - val Case = new EnumVal { def name = "Case"; def subType = GuiType.Category.Block } - val Charger = new EnumVal { def name = "Charger"; def subType = GuiType.Category.Block } - val Database = new EnumVal { def name = "Database"; def subType = GuiType.Category.Item } - val Disassembler = new EnumVal { def name = "Disassembler"; def subType = GuiType.Category.Block } - val DiskDrive = new EnumVal { def name = "DiskDrive"; def subType = GuiType.Category.Block } - val DiskDriveMountable = new EnumVal { def name = "DiskDriveMountable"; def subType = GuiType.Category.Item } - val DiskDriveMountableInRack = new EnumVal { def name = "DiskDriveMountableInRack"; def subType = GuiType.Category.Block } - val Drive = new EnumVal { def name = "Drive"; def subType = GuiType.Category.Item } - val Drone = new EnumVal { def name = "Drone"; def subType = GuiType.Category.Entity } - val Manual = new EnumVal { def name = "Manual"; def subType = GuiType.Category.None } - val Printer = new EnumVal { def name = "Printer"; def subType = GuiType.Category.Block } - val Rack = new EnumVal { def name = "Rack"; def subType = GuiType.Category.Block } - val Raid = new EnumVal { def name = "Raid"; def subType = GuiType.Category.Block } - val Relay = new EnumVal { def name = "Relay"; def subType = GuiType.Category.Block } - val Robot = new EnumVal { def name = "Robot"; def subType = GuiType.Category.Block } - val Screen = new EnumVal { def name = "Screen"; def subType = GuiType.Category.Block } - val Server = new EnumVal { def name = "Server"; def subType = GuiType.Category.Item } - val ServerInRack = new EnumVal { def name = "ServerInRack"; def subType = GuiType.Category.Block } - val Switch = new EnumVal { def name = "Switch"; def subType = GuiType.Category.Block } - val Tablet = new EnumVal { def name = "Tablet"; def subType = GuiType.Category.Item } - val TabletInner = new EnumVal { def name = "TabletInner"; def subType = GuiType.Category.Item } - val Terminal = new EnumVal { def name = "Terminal"; def subType = GuiType.Category.Item } - val Waypoint = new EnumVal { def name = "Waypoint"; def subType = GuiType.Category.Block } - - object Category extends ScalaEnum { - sealed trait EnumVal extends Value - - val None = new EnumVal { def name = "None" } - val Block = new EnumVal { def name = "Block" } - val Entity = new EnumVal { def name = "Entity" } - val Item = new EnumVal { def name = "Item" } - } - - def embedSlot(y: Int, slot: Int) = (y & 0x00FFFFFF) | (slot << 24) - - def extractY(value: Int) = value & 0x00FFFFFF - - def extractSlot(value: Int) = (value >>> 24) & 0xFF -} diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 44b0841bea..4363c66376 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -126,24 +126,6 @@ class Proxy { driver.Registry.locked = true } - def getGuiHandler(): common.GuiHandler = server.GuiHandler - - @Deprecated - def openGui(player: PlayerEntity, guiId: Int, world: World, x: Int, y: Int, z: Int): Unit = { - player match { - case _: FakePlayer => {} // Ignore fake players. - case _: ServerPlayerEntity => { - player.openMenu(new INamedContainerProvider { - override def createMenu(id: Int, plrInv: PlayerInventory, plr: PlayerEntity): Container = - getGuiHandler.getServerGuiElement(guiId, id, plr, plr.level, x, y, z).asInstanceOf[Container] - - override def getDisplayName(): ITextComponent = StringTextComponent.EMPTY - }) - } - case _ => OpenComputers.log.error(s"Unsupported entity for openGui: ${player.getClass.getName}") - } - } - def registerModel(instance: Item, id: String): Unit = {} def registerModel(instance: Block, id: String): Unit = {} diff --git a/src/main/scala/li/cil/oc/common/block/Adapter.scala b/src/main/scala/li/cil/oc/common/block/Adapter.scala index f7c2098ec5..e1b66f5c8f 100644 --- a/src/main/scala/li/cil/oc/common/block/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/block/Adapter.scala @@ -1,11 +1,12 @@ package li.cil.oc.common.block -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -15,7 +16,10 @@ import net.minecraft.world.IWorldReader import net.minecraft.world.World class Adapter extends SimpleBlock with traits.GUI { - override def guiType = GuiType.Adapter + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Adapter => ContainerTypes.openAdapterGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Adapter(tileentity.TileEntityTypes.ADAPTER) diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index 9b2896556e..689a194d4b 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -1,9 +1,10 @@ package li.cil.oc.common.block import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import net.minecraft.block.BlockState +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader @@ -12,7 +13,10 @@ import net.minecraft.world.World class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { override def energyThroughput = Settings.get.assemblerRate - override def guiType = GuiType.Assembler + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Assembler => ContainerTypes.openAssemblerGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Assembler(tileentity.TileEntityTypes.ASSEMBLER) } diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 0d495060f8..eb7205dea2 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.block import java.util import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.util.Rarity @@ -12,6 +12,7 @@ import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.fluid.FluidState import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer @@ -50,7 +51,10 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override def energyThroughput = Settings.get.caseRate(tier) - override def guiType = GuiType.Case + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Case => ContainerTypes.openCaseGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Case(tileentity.TileEntityTypes.CASE, tier) diff --git a/src/main/scala/li/cil/oc/common/block/Charger.scala b/src/main/scala/li/cil/oc/common/block/Charger.scala index 57bd9d6b24..fd831b15c7 100644 --- a/src/main/scala/li/cil/oc/common/block/Charger.scala +++ b/src/main/scala/li/cil/oc/common/block/Charger.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.block import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench @@ -9,6 +9,7 @@ import li.cil.oc.server.PacketSender import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer import net.minecraft.util.Direction @@ -25,7 +26,10 @@ class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateA override def energyThroughput = Settings.get.chargerRate - override def guiType = GuiType.Charger + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Charger => ContainerTypes.openChargerGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Charger(tileentity.TileEntityTypes.CHARGER) diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index a558a29bf9..56a925812f 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -3,13 +3,15 @@ package li.cil.oc.common.block import java.util import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import li.cil.oc.util.Tooltip import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader @@ -28,7 +30,10 @@ class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.Sta override def energyThroughput = Settings.get.disassemblerRate - override def guiType = GuiType.Disassembler + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Disassembler => ContainerTypes.openDisassemblerGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Disassembler(tileentity.TileEntityTypes.DISASSEMBLER) } diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index 88189e68df..b06442df45 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -2,7 +2,7 @@ package li.cil.oc.common.block import java.util -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods @@ -11,6 +11,7 @@ import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer import net.minecraft.util.Direction @@ -38,7 +39,10 @@ class DiskDrive extends SimpleBlock with traits.GUI { // ----------------------------------------------------------------------- // - override def guiType = GuiType.DiskDrive + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.DiskDrive => ContainerTypes.openDiskDriveGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.DiskDrive(tileentity.TileEntityTypes.DISK_DRIVE) diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala index d9fadf0260..2f6a8e22f0 100644 --- a/src/main/scala/li/cil/oc/common/block/Printer.scala +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -1,15 +1,19 @@ package li.cil.oc.common.block -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import net.minecraft.block.BlockState +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World class Printer extends SimpleBlock with traits.StateAware with traits.GUI { - override def guiType = GuiType.Printer + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Printer => ContainerTypes.openPrinterGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Printer(tileentity.TileEntityTypes.PRINTER) } diff --git a/src/main/scala/li/cil/oc/common/block/Rack.scala b/src/main/scala/li/cil/oc/common/block/Rack.scala index 5b5aeee45a..38bf729140 100644 --- a/src/main/scala/li/cil/oc/common/block/Rack.scala +++ b/src/main/scala/li/cil/oc/common/block/Rack.scala @@ -2,12 +2,13 @@ package li.cil.oc.common.block import li.cil.oc.Settings import li.cil.oc.api.component.RackMountable -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer import net.minecraft.util.Direction @@ -29,7 +30,10 @@ class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAwar override def energyThroughput = Settings.get.serverRackRate - override def guiType = GuiType.Rack + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Rack => ContainerTypes.openRackGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Rack(tileentity.TileEntityTypes.RACK) diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index 53bb326e7d..0cb93f3480 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -3,7 +3,7 @@ package li.cil.oc.common.block import java.util import li.cil.oc.client.KeyBindings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity @@ -13,6 +13,7 @@ import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer import net.minecraft.util.Direction @@ -41,7 +42,10 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si // ----------------------------------------------------------------------- // - override def guiType = GuiType.Raid + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Raid => ContainerTypes.openRaidGui(player, te) + case _ => + } override def newBlockEntity(world: IBlockReader) = new tileentity.Raid(tileentity.TileEntityTypes.RAID) diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala index 1dcada932a..a701d53f71 100644 --- a/src/main/scala/li/cil/oc/common/block/Relay.scala +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -1,13 +1,18 @@ package li.cil.oc.common.block import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { - override def guiType = GuiType.Relay + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { + case te: tileentity.Relay => ContainerTypes.openRelayGui(player, te) + case _ => + } override def energyThroughput = Settings.get.accessPointRate diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 58be81bbff..833c127933 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -7,7 +7,7 @@ import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.KeyBindings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist @@ -24,6 +24,7 @@ import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.fluid.FluidState import net.minecraft.item import net.minecraft.item.ItemStack @@ -193,10 +194,10 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 // We only send slot changes to nearby players, so if there was no slot // change since this player got into range he might have the wrong one, // so we send him the current one just in case. - world.getBlockEntity(pos) match { - case proxy: tileentity.RobotProxy if proxy.robot.node.network != null => + (player, world.getBlockEntity(pos)) match { + case (srvPlr: ServerPlayerEntity, proxy: tileentity.RobotProxy) if proxy.robot.node.network != null => PacketSender.sendRobotSelectedSlotChange(proxy.robot) - OpenComputers.openGui(player, GuiType.Robot.id, world, pos.getX, pos.getY, pos.getZ) + ContainerTypes.openRobotGui(srvPlr, proxy.robot) case _ => } } diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 0a7ec7acd7..d1b49b90e3 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -6,7 +6,7 @@ import li.cil.oc.Constants import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api -import li.cil.oc.common.GuiType +import li.cil.oc.client.gui import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench @@ -72,11 +72,11 @@ class Screen(val tier: Int) extends RedstoneAware { else if (api.Items.get(heldItem) == api.Items.get(Constants.ItemName.Analyzer)) false else world.getBlockEntity(pos) match { case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isCrouching == screen.origin.invertTouchMode) => - // Yep, this GUI is actually purely client side. We could skip this - // if, but it is clearer this way (to trigger it from the server we - // would have to give screens a "container", which we do not want). + // Yep, this GUI is actually purely client side (to trigger it from + // the server we would have to give screens a "container", which we + // do not want). if (world.isClientSide) { - OpenComputers.openGui(player, GuiType.Screen.id, world, pos.getX, pos.getY, pos.getZ) + Minecraft.getInstance.pushGuiLayer(new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.hasKeyboard, () => screen.origin.buffer.isRenderingEnabled)) } true case screen: tileentity.Screen if screen.tier > 0 && side == screen.facing => diff --git a/src/main/scala/li/cil/oc/common/block/Waypoint.scala b/src/main/scala/li/cil/oc/common/block/Waypoint.scala index 383d204c22..b8f3a784d7 100644 --- a/src/main/scala/li/cil/oc/common/block/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/block/Waypoint.scala @@ -1,12 +1,13 @@ package li.cil.oc.common.block import li.cil.oc.OpenComputers -import li.cil.oc.common.GuiType +import li.cil.oc.client.gui import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.util.RotationHelper import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.client.Minecraft import net.minecraft.entity.player.PlayerEntity import net.minecraft.state.StateContainer import net.minecraft.util.ActionResultType @@ -28,8 +29,9 @@ class Waypoint extends RedstoneAware { override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { if (!player.isCrouching) { - if (world.isClientSide) { - OpenComputers.openGui(player, GuiType.Waypoint.id, world, pos.getX, pos.getY, pos.getZ) + if (world.isClientSide) world.getBlockEntity(pos) match { + case t: tileentity.Waypoint => Minecraft.getInstance.pushGuiLayer(new gui.Waypoint(t)) + case _ => } ActionResultType.sidedSuccess(world.isClientSide) } diff --git a/src/main/scala/li/cil/oc/common/block/traits/GUI.scala b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala index f78cd73cda..e50324764e 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/GUI.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/GUI.scala @@ -1,9 +1,11 @@ package li.cil.oc.common.block.traits import li.cil.oc.OpenComputers -import li.cil.oc.common.GuiType import li.cil.oc.common.block.SimpleBlock +import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -11,12 +13,16 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.World trait GUI extends SimpleBlock { - def guiType: GuiType.EnumVal + def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos) + + // This gets forwarded to the vanilla PlayerEntity.openMenu call which doesn't support extra data. + override def getMenuProvider(state: BlockState, world: World, pos: BlockPos): INamedContainerProvider = null override def localOnBlockActivated(world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, heldItem: ItemStack, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { if (!player.isCrouching) { - if (!world.isClientSide) { - OpenComputers.openGui(player, guiType.id, world, pos.getX, pos.getY, pos.getZ) + player match { + case srvPlr: ServerPlayerEntity if !world.isClientSide => openGui(srvPlr, world, pos) + case _ => } true } diff --git a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java index bc25baede9..f85fe7cc20 100644 --- a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java +++ b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java @@ -4,6 +4,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.ContainerType; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.event.RegistryEvent; @@ -11,6 +12,7 @@ import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.network.IContainerFactory; +import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ObjectHolder; @@ -85,6 +87,95 @@ private static void register(IForgeRegistry> registry, String n registry.register(type); } + public static void openAdapterGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Adapter adapter) { + NetworkHooks.openGui(player, adapter); + } + + public static void openAssemblerGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Assembler assembler) { + NetworkHooks.openGui(player, assembler); + } + + public static void openCaseGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Case computer) { + NetworkHooks.openGui(player, computer, buff -> { + buff.writeVarInt(computer.getContainerSize()); + buff.writeVarInt(computer.tier()); + }); + } + + public static void openChargerGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Charger charger) { + NetworkHooks.openGui(player, charger); + } + + public static void openDatabaseGui(ServerPlayerEntity player, li.cil.oc.common.inventory.DatabaseInventory database) { + NetworkHooks.openGui(player, database, buff -> { + buff.writeItem(database.container()); + buff.writeVarInt(database.getContainerSize()); + buff.writeVarInt(database.tier()); + }); + } + + public static void openDisassemblerGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Disassembler disassembler) { + NetworkHooks.openGui(player, disassembler); + } + + public static void openDiskDriveGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.DiskDrive diskDrive) { + NetworkHooks.openGui(player, diskDrive); + } + + public static void openDiskDriveGui(ServerPlayerEntity player, li.cil.oc.server.component.DiskDriveMountable diskDrive) { + NetworkHooks.openGui(player, diskDrive); + } + + public static void openDiskDriveGui(ServerPlayerEntity player, li.cil.oc.common.inventory.DiskDriveMountableInventory diskDrive) { + NetworkHooks.openGui(player, diskDrive); + } + + public static void openDroneGui(ServerPlayerEntity player, li.cil.oc.common.entity.Drone drone) { + NetworkHooks.openGui(player, drone.containerProvider(), buff -> { + buff.writeVarInt(drone.mainInventory().getContainerSize()); + }); + } + + public static void openPrinterGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Printer printer) { + NetworkHooks.openGui(player, printer); + } + + public static void openRackGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Rack rack) { + NetworkHooks.openGui(player, rack); + } + + public static void openRaidGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Raid raid) { + NetworkHooks.openGui(player, raid); + } + + public static void openRelayGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Relay relay) { + NetworkHooks.openGui(player, relay); + } + + public static void openRobotGui(ServerPlayerEntity player, li.cil.oc.common.tileentity.Robot robot) { + NetworkHooks.openGui(player, robot, buff -> { + RobotInfo$.MODULE$.writeRobotInfo(buff, new RobotInfo(robot)); + }); + } + + public static void openServerGui(ServerPlayerEntity player, li.cil.oc.common.inventory.ServerInventory server, int rackSlot) { + NetworkHooks.openGui(player, server, buff -> { + buff.writeItem(server.container()); + buff.writeVarInt(server.getContainerSize()); + buff.writeVarInt(server.tier()); + buff.writeVarInt(rackSlot + 1); + }); + } + + public static void openTabletGui(ServerPlayerEntity player, li.cil.oc.common.item.TabletWrapper tablet) { + NetworkHooks.openGui(player, tablet, buff -> { + buff.writeItem(tablet.stack()); + buff.writeVarInt(tablet.getContainerSize()); + buff.writeUtf(tablet.containerSlotType(), 32); + buff.writeVarInt(tablet.containerSlotTier()); + }); + } + private ContainerTypes() { throw new Error(); } diff --git a/src/main/scala/li/cil/oc/common/container/Database.scala b/src/main/scala/li/cil/oc/common/container/Database.scala index 3d1b0f33b8..e7d7bd24ef 100644 --- a/src/main/scala/li/cil/oc/common/container/Database.scala +++ b/src/main/scala/li/cil/oc/common/container/Database.scala @@ -12,9 +12,6 @@ import net.minecraft.item.ItemStack class Database(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, val container: ItemStack, databaseInventory: IInventory, val tier: Int) extends Player(selfType, id, playerInventory, databaseInventory) { - def this(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, databaseInventory: DatabaseInventory) = - this(selfType, id, playerInventory, databaseInventory.container, databaseInventory, databaseInventory.tier) - val rows = math.sqrt(databaseInventory.getContainerSize).ceil.toInt val offset = 8 + Array(3, 2, 0)(tier) * slotSize diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 708d838e5a..1fae7ef6aa 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -72,9 +72,6 @@ class RobotInfo(val mainInvSize: Int, val slot1: String, val tier1: Int, class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: IInventory, val info: RobotInfo) extends Player(selfType, id, playerInventory, robot) { - def this(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: PlayerInventory, robot: tileentity.Robot) = - this(selfType, id, playerInventory, robot, new RobotInfo(robot)) - private val withScreenHeight = 256 private val noScreenHeight = 108 val deltaY: Int = if (info.screenBuffer.isDefined) 0 else withScreenHeight - noScreenHeight diff --git a/src/main/scala/li/cil/oc/common/container/Server.scala b/src/main/scala/li/cil/oc/common/container/Server.scala index 9acafeeaec..6a1b6b4042 100644 --- a/src/main/scala/li/cil/oc/common/container/Server.scala +++ b/src/main/scala/li/cil/oc/common/container/Server.scala @@ -13,9 +13,6 @@ import net.minecraft.nbt.CompoundNBT class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, serverInventory: IInventory, tier: Int, val rackSlot: Int) extends Player(selfType, id, playerInventory, serverInventory) { - def this(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, serverInventory: ServerInventory, rackSlot: Int = -1) = - this(selfType, id, playerInventory, serverInventory.container, serverInventory, serverInventory.tier, rackSlot) - for (i <- 0 to 1) { val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) diff --git a/src/main/scala/li/cil/oc/common/container/Tablet.scala b/src/main/scala/li/cil/oc/common/container/Tablet.scala index fe2ed7a0ba..cb865da578 100644 --- a/src/main/scala/li/cil/oc/common/container/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/container/Tablet.scala @@ -10,9 +10,6 @@ import net.minecraft.item.ItemStack class Tablet(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, tablet: IInventory, slot1: String, tier1: Int) extends Player(selfType, id, playerInventory, tablet) { - def this(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, tablet: TabletWrapper) = - this(selfType, id, playerInventory, tablet.stack, tablet, tablet.containerSlotType, tablet.containerSlotTier) - addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, slot1, tier1)) addPlayerInventorySlots(8, 84) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 02dcccb9f8..263441faa8 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -19,7 +19,8 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.MachineHost import li.cil.oc.api.network._ import li.cil.oc.common.EventHandler -import li.cil.oc.common.GuiType +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.inventory.Inventory import li.cil.oc.common.item.data.DroneData @@ -39,6 +40,9 @@ import net.minecraft.entity.MoverType import net.minecraft.entity.Pose import net.minecraft.entity.item.ItemEntity import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.network.datasync.DataParameter @@ -51,6 +55,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist @@ -487,6 +492,14 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, super.skipAttackInteraction(entity) } + // Not implemented in Drone itself because spectators would open this via vanilla PlayerEntity.openMenu (without extra data). + val containerProvider = new INamedContainerProvider { + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Drone(ContainerTypes.DRONE, id, playerInventory, mainInventory) + } + override def interact(player: PlayerEntity, hand: Hand): ActionResultType = { if (!isAlive) return ActionResultType.PASS if (player.isCrouching) { @@ -499,8 +512,9 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, start() } } - else if (!world.isClientSide) { - OpenComputers.openGui(player, GuiType.Drone.id, world, getId, 0, 0) + else player match { + case srvPlr: ServerPlayerEntity if !world.isClientSide => ContainerTypes.openDroneGui(srvPlr, this) + case _ => } ActionResultType.sidedSuccess(world.isClientSide) } diff --git a/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala b/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala index ecb94770fc..a1fcba76b4 100644 --- a/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/DatabaseInventory.scala @@ -1,10 +1,16 @@ package li.cil.oc.common.inventory import li.cil.oc.Settings +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.container.{Database => DatabaseContainer} import li.cil.oc.integration.opencomputers.DriverUpgradeDatabase +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack +import net.minecraft.util.text.StringTextComponent -trait DatabaseInventory extends ItemStackInventory { +trait DatabaseInventory extends ItemStackInventory with INamedContainerProvider { def tier: Int = DriverUpgradeDatabase.tier(container) override def getContainerSize = Settings.get.databaseEntriesPerTier(tier) @@ -16,4 +22,9 @@ trait DatabaseInventory extends ItemStackInventory { override def getInventoryStackRequired = 1 override def canPlaceItem(slot: Int, stack: ItemStack) = stack != container + + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new DatabaseContainer(ContainerTypes.DATABASE, id, playerInventory, container, this, tier) } diff --git a/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala b/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala index e448316100..3bbd9abeac 100644 --- a/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/DiskDriveMountableInventory.scala @@ -1,11 +1,17 @@ package li.cil.oc.common.inventory import li.cil.oc.api.Driver -import li.cil.oc.common.tileentity import li.cil.oc.common.Slot +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.container.{DiskDrive => DiskDriveContainer} +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack +import net.minecraft.util.text.StringTextComponent -trait DiskDriveMountableInventory extends ItemStackInventory { +trait DiskDriveMountableInventory extends ItemStackInventory with INamedContainerProvider { def tier: Int = 1 override def getContainerSize = 1 @@ -18,4 +24,9 @@ trait DiskDriveMountableInventory extends ItemStackInventory { case (0, Some(driver)) => driver.slot(stack) == Slot.Floppy case _ => false } + + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new DiskDriveContainer(ContainerTypes.DISK_DRIVE, id, playerInventory, this) } diff --git a/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala b/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala index 775cfd4130..b026ebc8c5 100644 --- a/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/ServerInventory.scala @@ -3,11 +3,17 @@ package li.cil.oc.common.inventory import li.cil.oc.api.Driver import li.cil.oc.api.internal import li.cil.oc.common.InventorySlots +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.container.{Server => ServerContainer} import li.cil.oc.util.ItemUtils import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack -trait ServerInventory extends ItemStackInventory { +trait ServerInventory extends ItemStackInventory with INamedContainerProvider { + def rackSlot: Int + def tier: Int = ItemUtils.caseTier(container) max 0 override def getContainerSize = InventorySlots.server(tier).length @@ -23,4 +29,7 @@ trait ServerInventory extends ItemStackInventory { val provided = InventorySlots.server(tier)(slot) driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier }) + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new ServerContainer(ContainerTypes.SERVER, id, playerInventory, container, this, tier, rackSlot) } diff --git a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala index 83e51d3ad7..a81509449f 100644 --- a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala @@ -2,8 +2,10 @@ package li.cil.oc.common.item import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.inventory.DiskDriveMountableInventory import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -15,10 +17,14 @@ class DiskDriveMountable(props: Properties = new Properties().tab(CreativeTab)) override def maxStackSize = 1 override def use(stack: ItemStack, world: World, player: PlayerEntity) = { - // Open the GUI immediately on the client, too, to avoid the player - // changing the current slot before it actually opens, which can lead to - // desynchronization of the player inventory. - OpenComputers.openGui(player, GuiType.DiskDriveMountable.id, world, 0, 0, 0) + if (!world.isClientSide) player match { + case srvPlr: ServerPlayerEntity => ContainerTypes.openDiskDriveGui(srvPlr, new DiskDriveMountableInventory { + override def container: ItemStack = stack + + override def stillValid(player: PlayerEntity) = player == srvPlr + }) + case _ => + } player.swing(Hand.MAIN_HAND) new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index f10b2bf691..557f02fd7a 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -5,11 +5,12 @@ import java.util import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.client.KeyBindings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item // Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties @@ -38,6 +39,8 @@ class Server(val tier: Int, props: Properties = new Properties().tab(CreativeTab private object HelperInventory extends ServerInventory { var container = ItemStack.EMPTY + + override def rackSlot = -1 } override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]) { @@ -63,10 +66,16 @@ class Server(val tier: Int, props: Properties = new Properties().tab(CreativeTab override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching) { - // Open the GUI immediately on the client, too, to avoid the player - // changing the current slot before it actually opens, which can lead to - // desynchronization of the player inventory. - OpenComputers.openGui(player, GuiType.Server.id, world, 0, 0, 0) + if (!world.isClientSide) player match { + case srvPlr: ServerPlayerEntity => ContainerTypes.openServerGui(srvPlr, new ServerInventory { + override def container = stack + + override def rackSlot = -1 + + override def stillValid(player: PlayerEntity) = player == srvPlr + }, -1) + case _ => + } player.swing(Hand.MAIN_HAND) } new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index 0e95507831..056d417681 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -24,9 +24,11 @@ import li.cil.oc.api.network.Message import li.cil.oc.api.network.Node import li.cil.oc.{client, server} import li.cil.oc.client.KeyBindings -import li.cil.oc.common.GuiType +import li.cil.oc.client.gui import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.item.data.TabletData import li.cil.oc.integration.opencomputers.DriverScreen @@ -42,7 +44,8 @@ import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity -import net.minecraft.entity.player.{PlayerEntity, ServerPlayerEntity} +import net.minecraft.entity.player.{PlayerEntity, PlayerInventory, ServerPlayerEntity} +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item // Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties @@ -216,8 +219,9 @@ class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item if (!world.isClientSide) { val tablet = Tablet.Server.get(stack, player) tablet.machine.stop() - if (tablet.data.tier > Tier.One) { - OpenComputers.openGui(player, GuiType.TabletInner.id, world, 0, 0, 0) + if (tablet.data.tier > Tier.One) player match { + case srvPlr: ServerPlayerEntity => ContainerTypes.openTabletGui(srvPlr, Tablet.get(stack, player)) + case _ => } } } @@ -231,7 +235,12 @@ class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item } } else { - OpenComputers.openGui(player, GuiType.Tablet.id, world, 0, 0, 0) + Tablet.get(stack, player).components.collect { + case Some(buffer: api.internal.TextBuffer) => buffer + }.headOption match { + case Some(buffer: api.internal.TextBuffer) => Minecraft.getInstance.pushGuiLayer(new gui.Screen(buffer, true, () => true, () => buffer.isRenderingEnabled)) + case _ => + } } } } @@ -250,7 +259,7 @@ class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item } } -class TabletWrapper(var stack: ItemStack, var player: PlayerEntity) extends ComponentInventory with MachineHost with internal.Tablet { +class TabletWrapper(var stack: ItemStack, var player: PlayerEntity) extends ComponentInventory with MachineHost with internal.Tablet with INamedContainerProvider { // Remember our *original* world, so we know which tablets to clear on dimension // changes of players holding tablets - since the player entity instance may be // kept the same and components are not required to properly handle world changes. @@ -327,6 +336,13 @@ class TabletWrapper(var stack: ItemStack, var player: PlayerEntity) extends Comp // ----------------------------------------------------------------------- // + override def getDisplayName = getName + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Tablet(ContainerTypes.TABLET, id, playerInventory, stack, this, containerSlotType, containerSlotTier) + + // ----------------------------------------------------------------------- // + override def onConnect(node: Node) { if (node == this.node) { connectComponents() diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 7a3ff90144..130d713143 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -2,12 +2,18 @@ package li.cil.oc.common.item import java.util +import com.google.common.base.Strings import li.cil.oc.Constants import li.cil.oc.CreativeTab +import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.api +import li.cil.oc.client.gui +import li.cil.oc.common.component +import li.cil.oc.common.tileentity.traits.TileEntity import li.cil.oc.util.Tooltip +import net.minecraft.client.Minecraft import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.util.ITooltipFlag @@ -63,7 +69,32 @@ class Terminal(props: Properties = new Properties().tab(CreativeTab)) extends It val server = stack.getTag.getString(Settings.namespace + "server") if (key != null && !key.isEmpty && server != null && !server.isEmpty) { if (world.isClientSide) { - OpenComputers.openGui(player, GuiType.Terminal.id, world, 0, 0, 0) + if (stack.hasTag) { + val address = stack.getTag.getString(Settings.namespace + "server") + val key = stack.getTag.getString(Settings.namespace + "key") + if (!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(address)) { + component.TerminalServer.loaded.find(address) match { + case Some(term) if term != null && term.rack != null => term.rack match { + case rack: TileEntity with api.internal.Rack => { + def inRange = player.isAlive && !rack.isRemoved && player.distanceToSqr(rack.x + 0.5, rack.y + 0.5, rack.z + 0.5) < term.range * term.range + if (inRange) { + if (term.sidedKeys.contains(key)) Minecraft.getInstance.pushGuiLayer(new gui.Screen(term.buffer, true, () => true, () => { + // Check if someone else bound a term to our server. + if (stack.getTag.getString(Settings.namespace + "key") != key) Minecraft.getInstance.popGuiLayer + // Check whether we're still in range. + if (!inRange) Minecraft.getInstance.popGuiLayer + true + })) + else player.displayClientMessage(Localization.Terminal.InvalidKey, true) + } + else player.displayClientMessage(Localization.Terminal.OutOfRange, true) + } + case _ => // Eh? + } + case _ => player.displayClientMessage(Localization.Terminal.OutOfRange, true) + } + } + } } player.swing(Hand.MAIN_HAND) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala index 19934f1abe..9dd035f72b 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala @@ -3,9 +3,11 @@ package li.cil.oc.common.item import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.inventory.DatabaseInventory import li.cil.oc.util.Rarity import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -28,7 +30,14 @@ class UpgradeDatabase(val tier: Int, props: Properties = new Properties().tab(Cr override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching) { - OpenComputers.openGui(player, GuiType.Database.id, world, 0, 0, 0) + if (!world.isClientSide) player match { + case srvPlr: ServerPlayerEntity => ContainerTypes.openDatabaseGui(srvPlr, new DatabaseInventory { + override def container = stack + + override def stillValid(player: PlayerEntity) = player == srvPlr + }) + case _ => + } player.swing(Hand.MAIN_HAND) } else { diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala index 5968659d5e..20a654f6e1 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -5,11 +5,13 @@ import java.util import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings -import li.cil.oc.common.GuiType +import li.cil.oc.client.gui import li.cil.oc.common.item.data.DriveData import li.cil.oc.util.Tooltip +import net.minecraft.client.Minecraft import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType @@ -51,7 +53,10 @@ trait FileSystemLike extends SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching && (!stack.hasTag || !stack.getTag.contains(Settings.namespace + "lootFactory"))) { - OpenComputers.openGui(player, GuiType.Drive.id, world, 0, 0, 0) + if (!world.isClientSide) player match { + case srvPlr: ServerPlayerEntity => Minecraft.getInstance.pushGuiLayer(new gui.Drive(player.inventory, () => stack)) + case _ => + } player.swing(Hand.MAIN_HAND) } new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) diff --git a/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala b/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala index 0dd6549125..ad3a9031b5 100644 --- a/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/ServerTemplate.scala @@ -17,6 +17,8 @@ object ServerTemplate { def disassemble(stack: ItemStack, ingredients: Array[ItemStack]) = { val info = new ServerInventory { override def container = stack + + override def rackSlot = -1 } Array(ingredients, (0 until info.getContainerSize).map(info.getItem).filter(null !=).toArray) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala index 1639dd4bf5..9c5e6fc246 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala @@ -14,9 +14,12 @@ import li.cil.oc.api.internal import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network._ import li.cil.oc.common.Slot +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.entity.player.PlayerEntity -import net.minecraft.util.SoundEvents +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.ListNBT @@ -24,12 +27,15 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.SoundCategory +import net.minecraft.util.SoundEvents import net.minecraftforge.common.util.Constants.NBT import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable -class Adapter(selfType: TileEntityType[_ <: Adapter]) extends TileEntity(selfType) with traits.Environment with traits.ComponentInventory with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo { +class Adapter(selfType: TileEntityType[_ <: Adapter]) extends TileEntity(selfType) with traits.Environment with traits.ComponentInventory + with traits.Tickable with traits.OpenSides with Analyzable with internal.Adapter with DeviceInfo with INamedContainerProvider { + val node = api.Network.newNode(this, Visibility.Network).create() private val blocks = Array.fill[Option[(ManagedEnvironment, DriverBlock)]](6)(None) @@ -187,6 +193,11 @@ class Adapter(selfType: TileEntityType[_ <: Adapter]) extends TileEntity(selfTyp // ----------------------------------------------------------------------- // + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Adapter(ContainerTypes.ADAPTER, id, playerInventory, this) + + // ----------------------------------------------------------------------- // + private final val BlocksTag = Settings.namespace + "adapter.blocks" private final val BlockNameTag = "name" private final val BlockDataTag = "data" diff --git a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala index 3297d7d91b..85e81df00f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala @@ -12,22 +12,30 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction +import net.minecraft.util.text.StringTextComponent import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Assembler(selfType: TileEntityType[_ <: Assembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo { +class Assembler(selfType: TileEntityType[_ <: Assembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor + with traits.Inventory with SidedEnvironment with traits.StateAware with traits.Tickable with DeviceInfo with INamedContainerProvider { + val node = api.Network.newNode(this, Visibility.Network). withComponent("assembler"). withConnector(Settings.get.bufferConverter). @@ -197,4 +205,11 @@ class Assembler(selfType: TileEntityType[_ <: Assembler]) extends TileEntity(sel tplSlot.validate(this, slot, stack) case _ => false } + + // ----------------------------------------------------------------------- // + + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Assembler(ContainerTypes.ASSEMBLER, id, playerInventory, this) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala index 2b00556142..58e1a8ec5a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala @@ -15,8 +15,12 @@ import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.block.property.PropertyRunning +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.util.Color import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -27,7 +31,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Case(selfType: TileEntityType[_ <: Case], var tier: Int) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo { +class Case(selfType: TileEntityType[_ <: Case], var tier: Int) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Computer with traits.Colored with internal.Case with DeviceInfo with INamedContainerProvider { def this(selfType: TileEntityType[_ <: Case]) = { this(selfType, 0) // If no tier was defined when constructing this case, then we don't yet know the inventory size @@ -142,4 +146,9 @@ class Case(selfType: TileEntityType[_ <: Case], var tier: Int) extends TileEntit val provided = InventorySlots.computer(tier)(slot) driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier }) + + // ----------------------------------------------------------------------- // + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Case(ContainerTypes.CASE, id, playerInventory, this, tier) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index e45ea842cb..1d8545beb3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -14,12 +14,16 @@ import li.cil.oc.api.nanomachines.Controller import li.cil.oc.api.network._ import li.cil.oc.api.util.StateAware import li.cil.oc.common.Slot +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.entity.Drone import li.cil.oc.integration.util.ItemCharge import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.particles.ParticleTypes @@ -35,7 +39,9 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable -class Charger(selfType: TileEntityType[_ <: Charger]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo { +class Charger(selfType: TileEntityType[_ <: Charger]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.RedstoneAware + with traits.Rotatable with traits.ComponentInventory with traits.Tickable with Analyzable with traits.StateAware with DeviceInfo with INamedContainerProvider { + val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -225,6 +231,11 @@ class Charger(selfType: TileEntityType[_ <: Charger]) extends TileEntity(selfTyp // ----------------------------------------------------------------------- // + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Charger(ContainerTypes.CHARGER, id, playerInventory, this) + + // ----------------------------------------------------------------------- // + override def updateRedstoneInput(side: Direction) { super.updateRedstoneInput(side) val signal = getInput.max min 15 diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index 62aa2d66c5..69770cb83d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -11,6 +11,8 @@ import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.network.Connector import li.cil.oc.api.network.Visibility import li.cil.oc.api.util.StateAware +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.template.DisassemblerTemplates import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.BlockPosition @@ -18,6 +20,8 @@ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ItemUtils import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -31,7 +35,9 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -class Disassembler(selfType: TileEntityType[_ <: Disassembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo { +class Disassembler(selfType: TileEntityType[_ <: Disassembler]) extends TileEntity(selfType) with traits.Environment with traits.PowerAcceptor + with traits.Inventory with traits.StateAware with traits.PlayerInputAware with traits.Tickable with DeviceInfo with INamedContainerProvider { + val node: Connector = api.Network.newNode(this, Visibility.None). withConnector(Settings.get.bufferConverter). create() @@ -201,4 +207,9 @@ class Disassembler(selfType: TileEntityType[_ <: Disassembler]) extends TileEnti disassembleNextInstantly = !stack.isEmpty && slot == 0 && player.isCreative } } + + // ----------------------------------------------------------------------- // + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Disassembler(ContainerTypes.DISASSEMBLER, id, playerInventory, this) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala index feb4434339..6f512f93ca 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala @@ -18,10 +18,14 @@ import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility import li.cil.oc.common.Slot import li.cil.oc.common.Sound +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -32,7 +36,9 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class DiskDrive(selfType: TileEntityType[_ <: DiskDrive]) extends TileEntity(selfType) with traits.Environment with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo { +class DiskDrive(selfType: TileEntityType[_ <: DiskDrive]) extends TileEntity(selfType) with traits.Environment + with traits.ComponentInventory with traits.Rotatable with Analyzable with DeviceInfo with INamedContainerProvider { + // Used on client side to check whether to render disk activity indicators. var lastAccess = 0L @@ -102,6 +108,12 @@ class DiskDrive(selfType: TileEntityType[_ <: DiskDrive]) extends TileEntity(sel case _ => false } + // ----------------------------------------------------------------------- // + // INamedContainerProvider + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.DiskDrive(ContainerTypes.DISK_DRIVE, id, playerInventory, this) + // ----------------------------------------------------------------------- // // ComponentInventory diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index 2402f00f11..f102625cb8 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -13,12 +13,17 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.api.util.StateAware +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.item.data.PrintData import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.StackOption import li.cil.oc.util.StackOption._ +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.ISidedInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -30,7 +35,9 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToJava._ -class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo { +class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable + with SidedEnvironment with traits.StateAware with traits.Tickable with ISidedInventory with DeviceInfo with INamedContainerProvider { + val node: ComponentConnector = api.Network.newNode(this, Visibility.Network). withComponent("printer3d"). withConnector(Settings.get.bufferConverter). @@ -370,6 +377,11 @@ class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfTyp // ----------------------------------------------------------------------- // + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Printer(ContainerTypes.PRINTER, id, playerInventory, this) + + // ----------------------------------------------------------------------- // + override def getSlotsForFace(side: Direction): Array[Int] = Array(slotMaterial, slotInk, slotOutput) override def canTakeItemThroughFace(slot: Int, stack: ItemStack, side: Direction): Boolean = !canPlaceItem(slot, stack) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index 4b084e1518..db290aad6e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -16,6 +16,8 @@ import li.cil.oc.api.network.Packet import li.cil.oc.api.network.Visibility import li.cil.oc.api.util.StateAware import li.cil.oc.common.Slot +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.server.{PacketSender => ServerPacketSender} @@ -23,7 +25,9 @@ import li.cil.oc.util.ExtendedInventory._ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.RotationHelper import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.nbt.IntArrayNBT @@ -35,7 +39,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn class Rack(selfType: TileEntityType[_ <: Rack]) extends TileEntity(selfType) with traits.PowerAcceptor with traits.Hub with traits.PowerBalancer - with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware { + with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware with INamedContainerProvider { var isRelayEnabled = false val lastData = new Array[CompoundNBT](getContainerSize) @@ -339,6 +343,12 @@ class Rack(selfType: TileEntityType[_ <: Rack]) extends TileEntity(selfType) wit } } + // ----------------------------------------------------------------------- // + // INamedContainerProvider + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Rack(ContainerTypes.RACK, id, playerInventory, this) + // ----------------------------------------------------------------------- // // ComponentInventory diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 1e46b81641..b748b68c77 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -8,13 +8,17 @@ import li.cil.oc.api.Driver import li.cil.oc.api.fs.Label import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network.Visibility -import li.cil.oc.common.item.data.DriveData import li.cil.oc.common.Slot +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.item.data.DriveData import li.cil.oc.common.item.data.NodeData import li.cil.oc.server.component.FileSystem import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -23,7 +27,7 @@ import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable with Analyzable { +class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) with traits.Environment with traits.Inventory with traits.Rotatable with Analyzable with INamedContainerProvider { val node = api.Network.newNode(this, Visibility.None).create() var filesystem: Option[FileSystem] = None @@ -122,6 +126,11 @@ class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) wit // ----------------------------------------------------------------------- // + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Raid(ContainerTypes.RAID, id, playerInventory, this) + + // ----------------------------------------------------------------------- // + private final val FileSystemTag = Settings.namespace + "fs" private final val PresenceTag = Settings.namespace + "presence" private final val LabelTag = Settings.namespace + "label" diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index de5479f9b8..983d0b9ca6 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -24,12 +24,16 @@ import li.cil.oc.api.network.WirelessEndpoint import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.item import li.cil.oc.integration.Mods import li.cil.oc.integration.opencomputers.DriverLinkedCard import li.cil.oc.server.network.QuantumNetwork import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -40,7 +44,9 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) with traits.Hub with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { +class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) with traits.Hub with traits.ComponentInventory + with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode with INamedContainerProvider { + lazy final val WirelessNetworkCardTier1: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier1) lazy final val WirelessNetworkCardTier2: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier2) lazy final val LinkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) @@ -278,6 +284,11 @@ class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) w // ----------------------------------------------------------------------- // + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Relay(ContainerTypes.RELAY, id, playerInventory, this) + + // ----------------------------------------------------------------------- // + private final val StrengthTag = Settings.namespace + "strength" private final val IsRepeaterTag = Settings.namespace + "isRepeater" private final val ComponentNodesTag = Settings.namespace + "componentNodes" diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 004d1c254e..953f7e427d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -14,6 +14,8 @@ import li.cil.oc.client.gui import li.cil.oc.common.EventHandler import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.container +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.InventoryProxy import li.cil.oc.common.inventory.InventorySelection import li.cil.oc.common.inventory.TankSelection @@ -36,8 +38,10 @@ import net.minecraft.block.Blocks import net.minecraft.block.FlowingFluidBlock import net.minecraft.client.Minecraft import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.fluid.Fluid import net.minecraft.inventory.EquipmentSlotType +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.tileentity.TileEntity @@ -47,6 +51,7 @@ import net.minecraft.util.SoundEvents import net.minecraft.util.Util import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.StringTextComponent import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.util.LazyOptional @@ -66,7 +71,9 @@ import scala.collection.mutable // robot moves we only create a new proxy tile entity, hook the instance of this // class that was held by the old proxy to it and can then safely forget the // old proxy, which will be cleaned up by Minecraft like any other tile entity. -class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with traits.PowerInformation with traits.RotatableTile with IFluidHandler with internal.Robot with InventorySelection with TankSelection { +class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with traits.PowerInformation with traits.RotatableTile + with IFluidHandler with internal.Robot with InventorySelection with TankSelection with INamedContainerProvider { + var proxy: RobotProxy = _ val info = new RobotData() @@ -765,6 +772,13 @@ class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with // ----------------------------------------------------------------------- // + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new container.Robot(ContainerTypes.ROBOT, id, playerInventory, this, new container.RobotInfo(this)) + + // ----------------------------------------------------------------------- // + override def dropSlot(slot: Int, count: Int, direction: Option[Direction]): Boolean = InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), mainInventory, slot, count, direction) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala index 46bac1b1b8..d8836e3be7 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala @@ -11,5 +11,7 @@ object InventoryProviderServer extends InventoryProvider { override def getInventory(stack: ItemStack, player: PlayerEntity): IInventory = new ServerInventory { override def container: ItemStack = stack + + override def rackSlot = -1 } } diff --git a/src/main/scala/li/cil/oc/server/GuiHandler.scala b/src/main/scala/li/cil/oc/server/GuiHandler.scala deleted file mode 100644 index 12aa244a91..0000000000 --- a/src/main/scala/li/cil/oc/server/GuiHandler.scala +++ /dev/null @@ -1,8 +0,0 @@ -package li.cil.oc.server - -import li.cil.oc.common.{GuiHandler => CommonGuiHandler} -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.world.World - -object GuiHandler extends CommonGuiHandler { -} diff --git a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala index 67e7aed668..400d54a14d 100644 --- a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala @@ -19,21 +19,27 @@ import li.cil.oc.api.network.Node import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.api.prefab.AbstractManagedEnvironment -import li.cil.oc.common.{GuiType, Slot, Sound} +import li.cil.oc.common.{Slot, Sound} +import li.cil.oc.common.container.ContainerTypes +import li.cil.oc.common.container.{DiskDrive => DiskDriveContainer} import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.inventory.ItemStackInventory import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.entity.player.ServerPlayerEntity +import net.minecraft.inventory.container.INamedContainerProvider import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.util.Direction import net.minecraft.util.Hand +import net.minecraft.util.text.StringTextComponent import scala.collection.convert.ImplicitConversionsToJava._ -class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends AbstractManagedEnvironment with ItemStackInventory with ComponentInventory with RackMountable with Analyzable with DeviceInfo { +class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends AbstractManagedEnvironment with ItemStackInventory with ComponentInventory with RackMountable with Analyzable with DeviceInfo with INamedContainerProvider { // Stored for filling data packet when queried. var lastAccess = 0L @@ -180,13 +186,23 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs } isDiskInDrive || isHoldingDisk } - else { - val position = BlockPosition(rack) - OpenComputers.openGui(player, GuiType.DiskDriveMountableInRack.id, rack.world, position.x, GuiType.embedSlot(position.y, slot), position.z) - true + else player match { + case srvPlr: ServerPlayerEntity => { + srvPlr.openMenu(this) + true + } + case _ => false } } + // ----------------------------------------------------------------------- // + // INamedContainerProvider + + override def getDisplayName = StringTextComponent.EMPTY + + override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = + new DiskDriveContainer(ContainerTypes.DISK_DRIVE, id, playerInventory, this) + // ----------------------------------------------------------------------- // // StateAware diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index 4ccc9a31dd..279f164eaa 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -19,10 +19,10 @@ import li.cil.oc.api.network.Analyzable import li.cil.oc.api.network.Environment import li.cil.oc.api.network.Message import li.cil.oc.api.network.Node -import li.cil.oc.common.GuiType import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.ComponentInventory import li.cil.oc.common.inventory.ServerInventory import li.cil.oc.common.item @@ -30,6 +30,7 @@ import li.cil.oc.server.network.Connector import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.util.Direction @@ -124,6 +125,8 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit // ----------------------------------------------------------------------- // // ServerInventory + override def rackSlot = slot + override def tier: Int = container.getItem match { case server: item.Server => server.tier case _ => 0 @@ -189,8 +192,10 @@ class Server(val rack: api.internal.Rack, val slot: Int) extends Environment wit } } else { - val position = BlockPosition(rack) - OpenComputers.openGui(player, GuiType.ServerInRack.id, world, position.x, GuiType.embedSlot(position.y, slot), position.z) + player match { + case srvPlr: ServerPlayerEntity => ContainerTypes.openServerGui(srvPlr, this, slot) + case _ => + } } } true From 71c215e52063c080ea1697000320527a382d2671 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 3 Sep 2022 00:53:33 +0200 Subject: [PATCH 048/159] Fix regular GUIs not being closeable --- src/main/scala/li/cil/oc/client/gui/Drive.scala | 3 +++ src/main/scala/li/cil/oc/client/gui/Manual.scala | 5 ++++- src/main/scala/li/cil/oc/client/gui/Screen.scala | 7 +++++++ src/main/scala/li/cil/oc/client/gui/Waypoint.scala | 7 +++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Drive.scala b/src/main/scala/li/cil/oc/client/gui/Drive.scala index b2de3d31b0..ab990274ab 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drive.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drive.scala @@ -7,6 +7,7 @@ import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.item.data.DriveData import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.gui.screen +import net.minecraft.client.settings.KeyBinding import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack import net.minecraft.util.text.StringTextComponent @@ -30,6 +31,8 @@ class Drive(playerInventory: PlayerInventory, val driveStack: () => ItemStack) e override protected def init(): Unit = { super.init() + minecraft.mouseHandler.releaseMouse() + KeyBinding.releaseAll() managedButton = new ImageButton(leftPos + 11, topPos + 11, 74, 18, new Button.IPressable { override def onPress(b: Button) = { ClientPacketSender.sendDriveMode(unmanaged = false) diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index 82c86c5ef3..d89e3a2c2f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -13,6 +13,7 @@ import net.minecraft.client.Minecraft import net.minecraft.client.gui.screen import net.minecraft.client.gui.widget.button.Button import net.minecraft.client.util.InputMappings +import net.minecraft.client.settings.KeyBinding import net.minecraft.util.text.ITextProperties import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW @@ -79,12 +80,14 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window refreshPage() } else { - minecraft.player.closeContainer() + onClose() } } override protected def init(): Unit = { super.init() + minecraft.mouseHandler.releaseMouse() + KeyBinding.releaseAll() for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { val x = leftPos + tabPosX diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index dc6e994558..4a8adab78b 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -8,6 +8,7 @@ import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.util.RenderState import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.screen +import net.minecraft.client.settings.KeyBinding import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW @@ -98,6 +99,12 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has else None } + override protected def init(): Unit = { + super.init() + minecraft.mouseHandler.releaseMouse() + KeyBinding.releaseAll() + } + override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { super.render(stack, mouseX, mouseY, dt) drawBufferLayer(stack) diff --git a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala index a9f0ea06d9..9a7d838a06 100644 --- a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala +++ b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala @@ -7,6 +7,7 @@ import li.cil.oc.client.Textures import li.cil.oc.common.tileentity import net.minecraft.client.gui.screen import net.minecraft.client.gui.widget.TextFieldWidget +import net.minecraft.client.settings.KeyBinding import net.minecraft.util.text.StringTextComponent import org.lwjgl.glfw.GLFW @@ -23,7 +24,7 @@ class Waypoint(val waypoint: tileentity.Waypoint) extends screen.Screen(StringTe super.tick() textField.tick() if (minecraft.player.distanceToSqr(waypoint.x + 0.5, waypoint.y + 0.5, waypoint.z + 0.5) > 64) { - minecraft.player.closeContainer() + onClose() } } @@ -31,6 +32,8 @@ class Waypoint(val waypoint: tileentity.Waypoint) extends screen.Screen(StringTe override protected def init(): Unit = { super.init() + minecraft.mouseHandler.releaseMouse() + KeyBinding.releaseAll() leftPos = (width - imageWidth) / 2 topPos = (height - imageHeight) / 2 @@ -61,8 +64,8 @@ class Waypoint(val waypoint: tileentity.Waypoint) extends screen.Screen(StringTe } override def removed(): Unit = { - super.removed() minecraft.keyboardHandler.setSendRepeatsToGui(false) + super.removed() } override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { From edc2b51b95b421fc24e671c976117d0e6dddecb0 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 3 Sep 2022 19:26:42 +0200 Subject: [PATCH 049/159] Fix Waypoint gui not being editable --- src/main/scala/li/cil/oc/client/gui/Waypoint.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala index 9a7d838a06..e979cf3702 100644 --- a/src/main/scala/li/cil/oc/client/gui/Waypoint.scala +++ b/src/main/scala/li/cil/oc/client/gui/Waypoint.scala @@ -54,7 +54,6 @@ class Waypoint(val waypoint: tileentity.Waypoint) extends screen.Screen(StringTe textField.setMaxLength(32) textField.setBordered(false) textField.setCanLoseFocus(false) - textField.setFocus(true) textField.setTextColor(0xFFFFFF) textField.setValue(waypoint.label) addWidget(textField) From 9127bfec753e9bb123b373b82094a2f33ba492d9 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 3 Sep 2022 20:29:21 +0200 Subject: [PATCH 050/159] Fix Manual tab icon rendering --- .../java/li/cil/oc/api/manual/TabIconRenderer.java | 3 ++- .../cil/oc/api/prefab/ItemStackTabIconRenderer.java | 12 +++++++----- .../cil/oc/api/prefab/TextureTabIconRenderer.java | 13 +++++++------ src/main/scala/li/cil/oc/client/gui/Manual.scala | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java b/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java index ad07891d9c..d8e68167c9 100644 --- a/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/manual/TabIconRenderer.java @@ -1,5 +1,6 @@ package li.cil.oc.api.manual; +import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -22,5 +23,5 @@ public interface TabIconRenderer { * adjusted so that drawing starts at (0,0,0), and should go to (16,16,0). */ @OnlyIn(Dist.CLIENT) - void render(); + void render(MatrixStack stack); } diff --git a/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java b/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java index d20fcd4dfc..2267a68968 100644 --- a/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/prefab/ItemStackTabIconRenderer.java @@ -1,9 +1,9 @@ package li.cil.oc.api.prefab; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; import li.cil.oc.api.manual.TabIconRenderer; import net.minecraft.client.Minecraft; -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.renderer.RenderHelper; import net.minecraft.item.ItemStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -22,11 +22,13 @@ public ItemStackTabIconRenderer(ItemStack stack) { @OnlyIn(Dist.CLIENT) @Override - public void render() { + public void render(MatrixStack matrix) { + // Translate manually because ItemRenderer generally can't take a MatrixStack. + RenderSystem.pushMatrix(); + RenderSystem.multMatrix(matrix.last().pose()); RenderSystem.enableRescaleNormal(); - RenderHelper.turnOff(); RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240, 240); Minecraft.getInstance().getItemRenderer().renderAndDecorateItem(stack, 0, 0); - RenderHelper.turnOff(); + RenderSystem.popMatrix(); } } diff --git a/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java b/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java index 202f236f89..db63f084c8 100644 --- a/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java +++ b/src/main/java/li/cil/oc/api/prefab/TextureTabIconRenderer.java @@ -1,8 +1,9 @@ package li.cil.oc.api.prefab; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; import li.cil.oc.api.manual.TabIconRenderer; import net.minecraft.client.Minecraft; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; @@ -24,15 +25,15 @@ public TextureTabIconRenderer(ResourceLocation location) { @Override @OnlyIn(Dist.CLIENT) - public void render() { + public void render(MatrixStack stack) { Minecraft.getInstance().getTextureManager().bind(location); final Tessellator t = Tessellator.getInstance(); final BufferBuilder r = t.getBuilder(); r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.vertex(0, 16, 0).uv(0, 1).endVertex(); - r.vertex(16, 16, 0).uv(1, 1).endVertex(); - r.vertex(16, 0, 0).uv(1, 0).endVertex(); - r.vertex(0, 0, 0).uv(0, 0).endVertex(); + r.vertex(stack.last().pose(), 0, 16, 0).uv(0, 1).endVertex(); + r.vertex(stack.last().pose(), 16, 16, 0).uv(1, 1).endVertex(); + r.vertex(stack.last().pose(), 16, 0, 0).uv(1, 0).endVertex(); + r.vertex(stack.last().pose(), 0, 0, 0).uv(0, 0).endVertex(); t.end(); } } diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index d89e3a2c2f..09860395e3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -115,7 +115,7 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window val button = buttons.get(i).asInstanceOf[ImageButton] stack.pushPose() stack.translate(button.x + 5, button.y + 5, getBlitOffset) - tab.renderer.render() + tab.renderer.render(stack) stack.popPose() } From 3458c1ab8332d064638a16a047d44ae86716dc37 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 3 Sep 2022 22:12:02 +0200 Subject: [PATCH 051/159] Fix illegal ResourceLocation paths in Manual --- src/main/java/li/cil/oc/api/detail/ManualAPI.java | 2 +- src/main/java/li/cil/oc/api/manual/PathProvider.java | 2 +- .../java/li/cil/oc/api/prefab/ResourceContentProvider.java | 3 ++- src/main/scala/li/cil/oc/client/Manual.scala | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/li/cil/oc/api/detail/ManualAPI.java b/src/main/java/li/cil/oc/api/detail/ManualAPI.java index e8bc944494..0eb9718bcc 100644 --- a/src/main/java/li/cil/oc/api/detail/ManualAPI.java +++ b/src/main/java/li/cil/oc/api/detail/ManualAPI.java @@ -90,7 +90,7 @@ public interface ManualAPI { *

    * The provided path may contain the special variable %LANGUAGE%, * which will be resolved to the currently set language, falling back to - * en_US. + * en_us. * * @param path the path of the page to get the content of. * @return the content of the page, or null if none exists. diff --git a/src/main/java/li/cil/oc/api/manual/PathProvider.java b/src/main/java/li/cil/oc/api/manual/PathProvider.java index 4f796f2b2f..dc9701d87e 100644 --- a/src/main/java/li/cil/oc/api/manual/PathProvider.java +++ b/src/main/java/li/cil/oc/api/manual/PathProvider.java @@ -16,7 +16,7 @@ *

    * Note that you can use the special variable %LANGUAGE% in your * paths, for language agnostic paths. These will be resolved to the currently - * set language, falling back to en_US, during actual content lookup. + * set language, falling back to en_us, during actual content lookup. */ public interface PathProvider { /** diff --git a/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java b/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java index 7d24042b24..2113b8f4fb 100644 --- a/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java +++ b/src/main/java/li/cil/oc/api/prefab/ResourceContentProvider.java @@ -40,7 +40,8 @@ public ResourceContentProvider(String resourceDomain) { @Override public Iterable getContent(String path) { - final ResourceLocation location = new ResourceLocation(resourceDomain, basePath + (path.startsWith("/") ? path.substring(1) : path)); + final String resourcePath = basePath + (path.startsWith("/") ? path.substring(1) : path); + final ResourceLocation location = new ResourceLocation(resourceDomain, resourcePath.toLowerCase()); InputStream is = null; try { is = Minecraft.getInstance().getResourceManager().getResource(location).getInputStream(); diff --git a/src/main/scala/li/cil/oc/client/Manual.scala b/src/main/scala/li/cil/oc/client/Manual.scala index 7b0ec8132d..38644b5ec0 100644 --- a/src/main/scala/li/cil/oc/client/Manual.scala +++ b/src/main/scala/li/cil/oc/client/Manual.scala @@ -23,7 +23,7 @@ import scala.collection.mutable object Manual extends ManualAPI { final val LanguageKey = "%LANGUAGE%" - final val FallbackLanguage = "en_US" + final val FallbackLanguage = "en_us" class History(val path: String, var offset: Int = 0) From cdb10bd3365f7e0cefb74a69a3f93937ab972b9d Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 4 Sep 2022 00:05:11 +0200 Subject: [PATCH 052/159] Fix Manual rendering causing texture errors --- .../segment/render/BlockImageProvider.scala | 4 ++-- .../markdown/segment/render/ItemImageProvider.scala | 4 ++-- .../segment/render/OreDictImageProvider.scala | 4 ++-- .../segment/render/TextureImageProvider.scala | 9 +++++++-- .../segment/render/TextureImageRenderer.scala | 13 ++++++++----- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala index 59eb8554fa..6c632acba4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala @@ -14,7 +14,7 @@ import net.minecraftforge.registries.ForgeRegistries object BlockImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { val splitIndex = data.lastIndexOf('@') - val (name, optMeta) = if (splitIndex > 0) data.splitAt(splitIndex) else (data, "") + val (name, optMeta) = if (splitIndex > 0) data.toLowerCase.splitAt(splitIndex) else (data.toLowerCase, "") val meta = if (Strings.isNullOrEmpty(optMeta)) 0 else Integer.parseInt(optMeta.drop(1)) ForgeRegistries.BLOCKS.getValue(new ResourceLocation(name)) match { case block: Block if block.asItem() != null => { @@ -22,7 +22,7 @@ object BlockImageProvider extends ImageProvider { if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) new ItemStackImageRenderer(Array(stack)) } - case _ => new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { + case _ => new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.BlockMissing" override def onMouseClick(mouseX: Int, mouseY: Int): Boolean = false diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala index 611028639c..d909855149 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala @@ -13,14 +13,14 @@ import net.minecraftforge.registries.ForgeRegistries object ItemImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { val splitIndex = data.lastIndexOf('@') - val (name, optMeta) = if (splitIndex > 0) data.splitAt(splitIndex) else (data, "") + val (name, optMeta) = if (splitIndex > 0) data.toLowerCase.splitAt(splitIndex) else (data.toLowerCase, "") ForgeRegistries.ITEMS.getValue(new ResourceLocation(name)) match { case item: Item => { val stack = new ItemStack(item, 1) if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) new ItemStackImageRenderer(Array(stack)) } - case _ => new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { + case _ => new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.ItemMissing" override def onMouseClick(mouseX: Int, mouseY: Int): Boolean = false diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala index bafc9f30fe..82f08afc5b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala @@ -13,7 +13,7 @@ import scala.collection.convert.ImplicitConversionsToScala._ object OreDictImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { - val desired = new ResourceLocation(data) + val desired = new ResourceLocation(data.toLowerCase) val stacks = mutable.ArrayBuffer.empty[ItemStack] ItemTags.getWrappers.find(t => desired.equals(t.getName)).foreach { stacks ++= _.getValues.map(new ItemStack(_)) @@ -22,7 +22,7 @@ object OreDictImageProvider extends ImageProvider { stacks ++= _.getValues.map(new ItemStack(_)) } if (stacks.nonEmpty) new ItemStackImageRenderer(stacks.toArray) - else new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { + else new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.OreDictMissing" override def onMouseClick(mouseX: Int, mouseY: Int): Boolean = false diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageProvider.scala index 588c81bd76..9491f9f3c2 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageProvider.scala @@ -7,9 +7,14 @@ import li.cil.oc.client.Textures import net.minecraft.util.ResourceLocation object TextureImageProvider extends ImageProvider { + val ManualMissingItem = { + val tex = Textures.GUI.ManualMissingItem + new ResourceLocation(tex.getNamespace, s"textures/${tex.getPath}.png") + } + override def getImage(data: String): ImageRenderer = { - try new TextureImageRenderer(new ResourceLocation(data)) catch { - case t: Throwable => new TextureImageRenderer(Textures.GUI.ManualMissingItem) with InteractiveImageRenderer { + try new TextureImageRenderer(new ResourceLocation(data.toLowerCase)) catch { + case t: Throwable => new TextureImageRenderer(ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.ImageMissing" override def onMouseClick(mouseX: Int, mouseY: Int): Boolean = false diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala index 7c32bac6e0..77b1e9a58d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/TextureImageRenderer.scala @@ -23,7 +23,7 @@ class TextureImageRenderer(val location: ResourceLocation) extends ImageRenderer manager.getTexture(location) match { case image: ImageTexture => image case other => - other.releaseId() + if (other != null) other.releaseId() val image = new ImageTexture(location) manager.register(location, image) image @@ -70,14 +70,17 @@ class TextureImageRenderer(val location: ResourceLocation) extends ImageRenderer val resource = manager.getResource(location) is = resource.getInputStream val bi = ImageIO.read(is) - bind() val data = MemoryUtil.memAllocInt(bi.getWidth * bi.getHeight) val tempArr = Array.ofDim[Int]((1024 * 1024) min data.capacity) val dy = tempArr.length / bi.getWidth - for (y0 <- 0 until data.capacity by dy) { - bi.getRGB(0, y0, bi.getWidth, dy max (bi.getHeight - y0), tempArr, 0, bi.getWidth) - data.put(tempArr, 0, bi.getWidth * dy) + for (y0 <- 0 until bi.getHeight by dy) { + val currH = dy min (bi.getHeight - y0 - 1) + bi.getRGB(0, y0, bi.getWidth, currH, tempArr, 0, bi.getWidth) + data.put(tempArr, 0, bi.getWidth * currH) } + + bind() + data.flip() TextureUtil.initTexture(data, bi.getWidth, bi.getHeight) width = bi.getWidth height = bi.getHeight From 559db44cf6477830ebda360a2303af0cc44d5b52 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 4 Sep 2022 23:47:12 +0200 Subject: [PATCH 053/159] Fix Manual rendering of ore dictionary entries --- .../opencomputers/doc/de_de/block/adapter.md | 2 +- .../doc/de_de/block/assembler.md | 2 +- .../opencomputers/doc/de_de/block/cable.md | 2 +- .../doc/de_de/block/capacitor.md | 2 +- .../opencomputers/doc/de_de/block/case1.md | 2 +- .../doc/de_de/block/chameliumblock.md | 2 +- .../opencomputers/doc/de_de/block/charger.md | 2 +- .../doc/de_de/block/disassembler.md | 2 +- .../doc/de_de/block/diskdrive.md | 2 +- .../opencomputers/doc/de_de/block/geolyzer.md | 2 +- .../doc/de_de/block/hologram1.md | 2 +- .../opencomputers/doc/de_de/block/keyboard.md | 2 +- .../doc/de_de/block/motionsensor.md | 2 +- .../doc/de_de/block/netsplitter.md | 2 +- .../doc/de_de/block/powerconverter.md | 2 +- .../doc/de_de/block/powerdistributor.md | 2 +- .../opencomputers/doc/de_de/block/printer.md | 2 +- .../opencomputers/doc/de_de/block/raid.md | 2 +- .../opencomputers/doc/de_de/block/redstone.md | 2 +- .../opencomputers/doc/de_de/block/relay.md | 2 +- .../opencomputers/doc/de_de/block/screen1.md | 2 +- .../doc/de_de/block/serverrack.md | 2 +- .../opencomputers/doc/de_de/block/waypoint.md | 2 +- .../doc/de_de/general/quickstart.md | 4 ++-- .../doc/de_de/item/abstractbuscard.md | 2 +- .../opencomputers/doc/de_de/item/acid.md | 2 +- .../opencomputers/doc/de_de/item/alu.md | 2 +- .../opencomputers/doc/de_de/item/analyzer.md | 2 +- .../doc/de_de/item/angelupgrade.md | 2 +- .../opencomputers/doc/de_de/item/apu1.md | 2 +- .../opencomputers/doc/de_de/item/arrowkeys.md | 2 +- .../doc/de_de/item/batteryupgrade1.md | 2 +- .../doc/de_de/item/buttongroup.md | 2 +- .../opencomputers/doc/de_de/item/card.md | 2 +- .../doc/de_de/item/cardcontainer1.md | 2 +- .../opencomputers/doc/de_de/item/chamelium.md | 2 +- .../opencomputers/doc/de_de/item/chip1.md | 2 +- .../doc/de_de/item/chunkloaderupgrade.md | 2 +- .../doc/de_de/item/circuitboard.md | 2 +- .../doc/de_de/item/componentbus1.md | 2 +- .../doc/de_de/item/controlunit.md | 2 +- .../opencomputers/doc/de_de/item/cpu1.md | 2 +- .../doc/de_de/item/craftingupgrade.md | 2 +- .../doc/de_de/item/cuttingwire.md | 2 +- .../doc/de_de/item/databaseupgrade1.md | 2 +- .../opencomputers/doc/de_de/item/datacard1.md | 2 +- .../opencomputers/doc/de_de/item/disk.md | 2 +- .../doc/de_de/item/dronecase1.md | 2 +- .../opencomputers/doc/de_de/item/eeprom.md | 2 +- .../doc/de_de/item/experienceupgrade.md | 2 +- .../opencomputers/doc/de_de/item/floppy.md | 2 +- .../doc/de_de/item/generatorupgrade.md | 2 +- .../doc/de_de/item/graphicscard1.md | 2 +- .../opencomputers/doc/de_de/item/hdd1.md | 2 +- .../doc/de_de/item/hoverboots.md | 2 +- .../doc/de_de/item/hoverupgrade1.md | 2 +- .../doc/de_de/item/inkcartridge.md | 2 +- .../doc/de_de/item/internetcard.md | 2 +- .../opencomputers/doc/de_de/item/interweb.md | 2 +- .../de_de/item/inventorycontrollerupgrade.md | 2 +- .../doc/de_de/item/inventoryupgrade.md | 2 +- .../opencomputers/doc/de_de/item/lancard.md | 2 +- .../doc/de_de/item/leashupgrade.md | 2 +- .../doc/de_de/item/linkedcard.md | 2 +- .../opencomputers/doc/de_de/item/manual.md | 2 +- .../doc/de_de/item/microcontrollercase1.md | 2 +- .../doc/de_de/item/navigationupgrade.md | 2 +- .../opencomputers/doc/de_de/item/numpad.md | 2 +- .../doc/de_de/item/pistonupgrade.md | 2 +- .../doc/de_de/item/printedcircuitboard.md | 2 +- .../opencomputers/doc/de_de/item/ram1.md | 2 +- .../doc/de_de/item/rawcircuitboard.md | 2 +- .../doc/de_de/item/redstonecard1.md | 2 +- .../opencomputers/doc/de_de/item/server1.md | 2 +- .../doc/de_de/item/signupgrade.md | 2 +- .../doc/de_de/item/solargeneratorupgrade.md | 2 +- .../doc/de_de/item/tabletcase1.md | 2 +- .../doc/de_de/item/tankcontrollerupgrade.md | 2 +- .../doc/de_de/item/tankupgrade.md | 2 +- .../opencomputers/doc/de_de/item/terminal.md | 2 +- .../doc/de_de/item/texturepicker.md | 2 +- .../doc/de_de/item/tractorbeamupgrade.md | 2 +- .../doc/de_de/item/transistor.md | 2 +- .../doc/de_de/item/upgradecontainer1.md | 2 +- .../opencomputers/doc/de_de/item/wlanCard1.md | 2 +- .../doc/de_de/item/worldsensorcard.md | 2 +- .../opencomputers/doc/de_de/item/wrench.md | 2 +- .../doc/en_us/block/accesspoint.md | 2 +- .../opencomputers/doc/en_us/block/adapter.md | 2 +- .../doc/en_us/block/assembler.md | 2 +- .../opencomputers/doc/en_us/block/cable.md | 2 +- .../doc/en_us/block/capacitor.md | 2 +- .../opencomputers/doc/en_us/block/case1.md | 2 +- .../doc/en_us/block/chameliumblock.md | 2 +- .../opencomputers/doc/en_us/block/charger.md | 2 +- .../doc/en_us/block/disassembler.md | 2 +- .../doc/en_us/block/diskdrive.md | 2 +- .../opencomputers/doc/en_us/block/geolyzer.md | 2 +- .../doc/en_us/block/hologram1.md | 2 +- .../opencomputers/doc/en_us/block/keyboard.md | 2 +- .../doc/en_us/block/motionsensor.md | 2 +- .../doc/en_us/block/netsplitter.md | 2 +- .../doc/en_us/block/powerconverter.md | 2 +- .../doc/en_us/block/powerdistributor.md | 2 +- .../opencomputers/doc/en_us/block/printer.md | 2 +- .../opencomputers/doc/en_us/block/rack.md | 2 +- .../opencomputers/doc/en_us/block/raid.md | 2 +- .../opencomputers/doc/en_us/block/redstone.md | 2 +- .../opencomputers/doc/en_us/block/relay.md | 2 +- .../opencomputers/doc/en_us/block/screen1.md | 2 +- .../opencomputers/doc/en_us/block/switch.md | 2 +- .../doc/en_us/block/transposer.md | 2 +- .../opencomputers/doc/en_us/block/waypoint.md | 2 +- .../doc/en_us/general/example.md | 20 +++++++++---------- .../doc/en_us/general/quickstart.md | 4 ++-- .../doc/en_us/item/abstractbuscard.md | 2 +- .../opencomputers/doc/en_us/item/acid.md | 2 +- .../opencomputers/doc/en_us/item/alu.md | 2 +- .../opencomputers/doc/en_us/item/analyzer.md | 2 +- .../doc/en_us/item/angelupgrade.md | 2 +- .../opencomputers/doc/en_us/item/apu1.md | 2 +- .../opencomputers/doc/en_us/item/arrowkeys.md | 2 +- .../doc/en_us/item/batteryupgrade1.md | 2 +- .../doc/en_us/item/buttongroup.md | 2 +- .../opencomputers/doc/en_us/item/card.md | 2 +- .../doc/en_us/item/cardcontainer1.md | 2 +- .../opencomputers/doc/en_us/item/chamelium.md | 2 +- .../opencomputers/doc/en_us/item/chip1.md | 2 +- .../doc/en_us/item/chunkloaderupgrade.md | 2 +- .../doc/en_us/item/circuitboard.md | 2 +- .../doc/en_us/item/componentbus1.md | 2 +- .../doc/en_us/item/controlunit.md | 2 +- .../opencomputers/doc/en_us/item/cpu1.md | 2 +- .../doc/en_us/item/craftingupgrade.md | 2 +- .../doc/en_us/item/cuttingwire.md | 2 +- .../doc/en_us/item/databaseupgrade1.md | 2 +- .../opencomputers/doc/en_us/item/datacard1.md | 2 +- .../opencomputers/doc/en_us/item/disk.md | 2 +- .../doc/en_us/item/diskdrivemountable.md | 2 +- .../doc/en_us/item/dronecase1.md | 2 +- .../opencomputers/doc/en_us/item/eeprom.md | 2 +- .../doc/en_us/item/experienceupgrade.md | 2 +- .../opencomputers/doc/en_us/item/floppy.md | 2 +- .../doc/en_us/item/generatorupgrade.md | 2 +- .../doc/en_us/item/graphicscard1.md | 2 +- .../opencomputers/doc/en_us/item/hdd1.md | 2 +- .../doc/en_us/item/hoverboots.md | 2 +- .../doc/en_us/item/hoverupgrade1.md | 2 +- .../doc/en_us/item/inkcartridge.md | 2 +- .../doc/en_us/item/internetcard.md | 2 +- .../opencomputers/doc/en_us/item/interweb.md | 2 +- .../en_us/item/inventorycontrollerupgrade.md | 2 +- .../doc/en_us/item/inventoryupgrade.md | 2 +- .../opencomputers/doc/en_us/item/lancard.md | 2 +- .../doc/en_us/item/leashupgrade.md | 2 +- .../doc/en_us/item/linkedcard.md | 2 +- .../opencomputers/doc/en_us/item/manual.md | 2 +- .../opencomputers/doc/en_us/item/mfu.md | 2 +- .../doc/en_us/item/microcontrollercase1.md | 2 +- .../doc/en_us/item/nanomachines.md | 2 +- .../doc/en_us/item/navigationupgrade.md | 2 +- .../opencomputers/doc/en_us/item/numpad.md | 2 +- .../doc/en_us/item/pistonupgrade.md | 2 +- .../doc/en_us/item/printedcircuitboard.md | 2 +- .../opencomputers/doc/en_us/item/ram1.md | 2 +- .../doc/en_us/item/rawcircuitboard.md | 2 +- .../doc/en_us/item/redstonecard1.md | 2 +- .../opencomputers/doc/en_us/item/server1.md | 2 +- .../doc/en_us/item/signupgrade.md | 2 +- .../doc/en_us/item/solargeneratorupgrade.md | 2 +- .../doc/en_us/item/stickypistonupgrade.md | 2 +- .../doc/en_us/item/tabletcase1.md | 2 +- .../doc/en_us/item/tankcontrollerupgrade.md | 2 +- .../doc/en_us/item/tankupgrade.md | 2 +- .../opencomputers/doc/en_us/item/terminal.md | 2 +- .../doc/en_us/item/terminalserver.md | 2 +- .../doc/en_us/item/texturepicker.md | 2 +- .../doc/en_us/item/tractorbeamupgrade.md | 2 +- .../doc/en_us/item/tradingupgrade.md | 2 +- .../doc/en_us/item/transistor.md | 2 +- .../doc/en_us/item/upgradecontainer1.md | 2 +- .../opencomputers/doc/en_us/item/wlancard1.md | 2 +- .../doc/en_us/item/worldsensorcard.md | 2 +- .../opencomputers/doc/en_us/item/wrench.md | 2 +- .../doc/fr_fr/block/accessPoint.md | 2 +- .../opencomputers/doc/fr_fr/block/adapter.md | 2 +- .../doc/fr_fr/block/assembler.md | 2 +- .../opencomputers/doc/fr_fr/block/cable.md | 2 +- .../doc/fr_fr/block/capacitor.md | 2 +- .../opencomputers/doc/fr_fr/block/case1.md | 2 +- .../doc/fr_fr/block/chameliumBlock.md | 2 +- .../opencomputers/doc/fr_fr/block/charger.md | 2 +- .../doc/fr_fr/block/disassembler.md | 2 +- .../doc/fr_fr/block/diskDrive.md | 2 +- .../opencomputers/doc/fr_fr/block/geolyzer.md | 2 +- .../doc/fr_fr/block/hologram1.md | 2 +- .../opencomputers/doc/fr_fr/block/keyboard.md | 2 +- .../doc/fr_fr/block/motionSensor.md | 2 +- .../doc/fr_fr/block/netSplitter.md | 2 +- .../doc/fr_fr/block/powerConverter.md | 2 +- .../doc/fr_fr/block/powerDistributor.md | 2 +- .../opencomputers/doc/fr_fr/block/printer.md | 2 +- .../opencomputers/doc/fr_fr/block/raid.md | 2 +- .../opencomputers/doc/fr_fr/block/redstone.md | 2 +- .../opencomputers/doc/fr_fr/block/relay.md | 2 +- .../opencomputers/doc/fr_fr/block/screen1.md | 2 +- .../doc/fr_fr/block/serverRack.md | 2 +- .../opencomputers/doc/fr_fr/block/switch.md | 2 +- .../doc/fr_fr/block/transposer.md | 2 +- .../opencomputers/doc/fr_fr/block/waypoint.md | 2 +- .../doc/fr_fr/general/example.md | 20 +++++++++---------- .../doc/fr_fr/general/quickstart.md | 4 ++-- .../doc/fr_fr/item/abstractBusCard.md | 2 +- .../opencomputers/doc/fr_fr/item/acid.md | 2 +- .../opencomputers/doc/fr_fr/item/alu.md | 2 +- .../opencomputers/doc/fr_fr/item/analyzer.md | 2 +- .../doc/fr_fr/item/angelUpgrade.md | 2 +- .../opencomputers/doc/fr_fr/item/apu1.md | 2 +- .../opencomputers/doc/fr_fr/item/arrowKeys.md | 2 +- .../doc/fr_fr/item/batteryUpgrade1.md | 2 +- .../doc/fr_fr/item/buttonGroup.md | 2 +- .../opencomputers/doc/fr_fr/item/card.md | 2 +- .../doc/fr_fr/item/cardContainer1.md | 2 +- .../opencomputers/doc/fr_fr/item/chamelium.md | 2 +- .../opencomputers/doc/fr_fr/item/chip1.md | 2 +- .../doc/fr_fr/item/chunkloaderUpgrade.md | 2 +- .../doc/fr_fr/item/circuitBoard.md | 2 +- .../doc/fr_fr/item/componentBus1.md | 2 +- .../doc/fr_fr/item/controlUnit.md | 2 +- .../opencomputers/doc/fr_fr/item/cpu1.md | 2 +- .../doc/fr_fr/item/craftingUpgrade.md | 2 +- .../doc/fr_fr/item/cuttingWire.md | 2 +- .../opencomputers/doc/fr_fr/item/dataCard1.md | 2 +- .../doc/fr_fr/item/databaseUpgrade1.md | 2 +- .../opencomputers/doc/fr_fr/item/disk.md | 2 +- .../doc/fr_fr/item/droneCase1.md | 2 +- .../opencomputers/doc/fr_fr/item/eeprom.md | 2 +- .../doc/fr_fr/item/experienceUpgrade.md | 2 +- .../opencomputers/doc/fr_fr/item/floppy.md | 2 +- .../doc/fr_fr/item/generatorUpgrade.md | 2 +- .../doc/fr_fr/item/graphicsCard1.md | 2 +- .../opencomputers/doc/fr_fr/item/hdd1.md | 2 +- .../doc/fr_fr/item/hoverBoots.md | 2 +- .../doc/fr_fr/item/hoverUpgrade1.md | 2 +- .../doc/fr_fr/item/inkCartridge.md | 2 +- .../doc/fr_fr/item/internetCard.md | 2 +- .../opencomputers/doc/fr_fr/item/interweb.md | 2 +- .../fr_fr/item/inventoryControllerUpgrade.md | 2 +- .../doc/fr_fr/item/inventoryUpgrade.md | 2 +- .../opencomputers/doc/fr_fr/item/lanCard.md | 2 +- .../doc/fr_fr/item/leashUpgrade.md | 2 +- .../doc/fr_fr/item/linkedCard.md | 2 +- .../opencomputers/doc/fr_fr/item/manual.md | 2 +- .../doc/fr_fr/item/microcontrollerCase1.md | 2 +- .../doc/fr_fr/item/nanomachines.md | 2 +- .../doc/fr_fr/item/navigationUpgrade.md | 2 +- .../opencomputers/doc/fr_fr/item/numPad.md | 2 +- .../doc/fr_fr/item/pistonUpgrade.md | 2 +- .../doc/fr_fr/item/printedCircuitBoard.md | 2 +- .../opencomputers/doc/fr_fr/item/ram1.md | 2 +- .../doc/fr_fr/item/rawCircuitBoard.md | 2 +- .../doc/fr_fr/item/redstoneCard1.md | 2 +- .../opencomputers/doc/fr_fr/item/server1.md | 2 +- .../doc/fr_fr/item/signUpgrade.md | 2 +- .../doc/fr_fr/item/solarGeneratorUpgrade.md | 2 +- .../doc/fr_fr/item/tabletCase1.md | 2 +- .../doc/fr_fr/item/tankControllerUpgrade.md | 2 +- .../doc/fr_fr/item/tankUpgrade.md | 2 +- .../opencomputers/doc/fr_fr/item/terminal.md | 2 +- .../doc/fr_fr/item/texturePicker.md | 2 +- .../doc/fr_fr/item/tractorBeamUpgrade.md | 2 +- .../doc/fr_fr/item/transistor.md | 2 +- .../doc/fr_fr/item/upgradeContainer1.md | 2 +- .../opencomputers/doc/fr_fr/item/wlanCard1.md | 2 +- .../doc/fr_fr/item/worldSensorCard.md | 2 +- .../opencomputers/doc/fr_fr/item/wrench.md | 2 +- .../doc/ru_ru/block/accessPoint.md | 2 +- .../opencomputers/doc/ru_ru/block/adapter.md | 2 +- .../doc/ru_ru/block/assembler.md | 2 +- .../opencomputers/doc/ru_ru/block/cable.md | 2 +- .../doc/ru_ru/block/capacitor.md | 2 +- .../opencomputers/doc/ru_ru/block/case1.md | 2 +- .../doc/ru_ru/block/chameliumBlock.md | 2 +- .../opencomputers/doc/ru_ru/block/charger.md | 2 +- .../doc/ru_ru/block/disassembler.md | 2 +- .../doc/ru_ru/block/diskDrive.md | 2 +- .../opencomputers/doc/ru_ru/block/geolyzer.md | 2 +- .../doc/ru_ru/block/hologram1.md | 2 +- .../opencomputers/doc/ru_ru/block/keyboard.md | 2 +- .../doc/ru_ru/block/motionSensor.md | 2 +- .../doc/ru_ru/block/netSplitter.md | 2 +- .../doc/ru_ru/block/powerConverter.md | 2 +- .../doc/ru_ru/block/powerDistributor.md | 2 +- .../opencomputers/doc/ru_ru/block/printer.md | 2 +- .../opencomputers/doc/ru_ru/block/rack.md | 2 +- .../opencomputers/doc/ru_ru/block/raid.md | 2 +- .../opencomputers/doc/ru_ru/block/redstone.md | 2 +- .../opencomputers/doc/ru_ru/block/relay.md | 2 +- .../opencomputers/doc/ru_ru/block/screen1.md | 2 +- .../opencomputers/doc/ru_ru/block/switch.md | 2 +- .../doc/ru_ru/block/transposer.md | 2 +- .../opencomputers/doc/ru_ru/block/waypoint.md | 2 +- .../doc/ru_ru/general/example.md | 20 +++++++++---------- .../doc/ru_ru/general/quickstart.md | 4 ++-- .../doc/ru_ru/item/abstractBusCard.md | 2 +- .../opencomputers/doc/ru_ru/item/acid.md | 2 +- .../opencomputers/doc/ru_ru/item/alu.md | 2 +- .../opencomputers/doc/ru_ru/item/analyzer.md | 2 +- .../doc/ru_ru/item/angelUpgrade.md | 2 +- .../opencomputers/doc/ru_ru/item/apu1.md | 2 +- .../opencomputers/doc/ru_ru/item/arrowKeys.md | 2 +- .../doc/ru_ru/item/batteryUpgrade1.md | 2 +- .../doc/ru_ru/item/buttonGroup.md | 2 +- .../opencomputers/doc/ru_ru/item/card.md | 2 +- .../doc/ru_ru/item/cardContainer1.md | 2 +- .../opencomputers/doc/ru_ru/item/chamelium.md | 2 +- .../opencomputers/doc/ru_ru/item/chip1.md | 2 +- .../doc/ru_ru/item/chunkloaderUpgrade.md | 2 +- .../doc/ru_ru/item/circuitBoard.md | 2 +- .../doc/ru_ru/item/componentBus1.md | 2 +- .../doc/ru_ru/item/controlUnit.md | 2 +- .../opencomputers/doc/ru_ru/item/cpu1.md | 2 +- .../doc/ru_ru/item/craftingUpgrade.md | 2 +- .../doc/ru_ru/item/cuttingWire.md | 2 +- .../opencomputers/doc/ru_ru/item/dataCard1.md | 2 +- .../doc/ru_ru/item/databaseUpgrade1.md | 2 +- .../opencomputers/doc/ru_ru/item/disk.md | 2 +- .../doc/ru_ru/item/diskDriveMountable.md | 2 +- .../doc/ru_ru/item/droneCase1.md | 2 +- .../opencomputers/doc/ru_ru/item/eeprom.md | 2 +- .../doc/ru_ru/item/experienceUpgrade.md | 2 +- .../opencomputers/doc/ru_ru/item/floppy.md | 2 +- .../doc/ru_ru/item/generatorUpgrade.md | 2 +- .../doc/ru_ru/item/graphicsCard1.md | 2 +- .../opencomputers/doc/ru_ru/item/hdd1.md | 2 +- .../doc/ru_ru/item/hoverBoots.md | 2 +- .../doc/ru_ru/item/hoverUpgrade1.md | 2 +- .../doc/ru_ru/item/inkCartridge.md | 2 +- .../doc/ru_ru/item/internetCard.md | 2 +- .../opencomputers/doc/ru_ru/item/interweb.md | 2 +- .../ru_ru/item/inventoryControllerUpgrade.md | 2 +- .../doc/ru_ru/item/inventoryUpgrade.md | 2 +- .../opencomputers/doc/ru_ru/item/lanCard.md | 2 +- .../doc/ru_ru/item/leashUpgrade.md | 2 +- .../doc/ru_ru/item/linkedCard.md | 2 +- .../opencomputers/doc/ru_ru/item/manual.md | 2 +- .../opencomputers/doc/ru_ru/item/mfu.md | 2 +- .../doc/ru_ru/item/microcontrollerCase1.md | 2 +- .../doc/ru_ru/item/nanomachines.md | 2 +- .../doc/ru_ru/item/navigationUpgrade.md | 2 +- .../opencomputers/doc/ru_ru/item/numPad.md | 2 +- .../doc/ru_ru/item/pistonUpgrade.md | 2 +- .../doc/ru_ru/item/printedCircuitBoard.md | 2 +- .../opencomputers/doc/ru_ru/item/ram1.md | 2 +- .../doc/ru_ru/item/rawCircuitBoard.md | 2 +- .../doc/ru_ru/item/redstoneCard1.md | 2 +- .../opencomputers/doc/ru_ru/item/server1.md | 2 +- .../doc/ru_ru/item/signUpgrade.md | 2 +- .../doc/ru_ru/item/solarGeneratorUpgrade.md | 2 +- .../doc/ru_ru/item/tabletCase1.md | 2 +- .../doc/ru_ru/item/tankControllerUpgrade.md | 2 +- .../doc/ru_ru/item/tankUpgrade.md | 2 +- .../opencomputers/doc/ru_ru/item/terminal.md | 2 +- .../doc/ru_ru/item/terminalServer.md | 2 +- .../doc/ru_ru/item/texturePicker.md | 2 +- .../doc/ru_ru/item/tractorBeamUpgrade.md | 2 +- .../doc/ru_ru/item/tradingUpgrade.md | 2 +- .../doc/ru_ru/item/transistor.md | 2 +- .../doc/ru_ru/item/upgradeContainer1.md | 2 +- .../opencomputers/doc/ru_ru/item/wlanCard1.md | 2 +- .../doc/ru_ru/item/worldSensorCard.md | 2 +- .../opencomputers/doc/ru_ru/item/wrench.md | 2 +- .../doc/zh_cn/block/accesspoint.md | 2 +- .../opencomputers/doc/zh_cn/block/adapter.md | 2 +- .../doc/zh_cn/block/assembler.md | 2 +- .../opencomputers/doc/zh_cn/block/cable.md | 2 +- .../doc/zh_cn/block/capacitor.md | 2 +- .../opencomputers/doc/zh_cn/block/case1.md | 2 +- .../doc/zh_cn/block/chameliumblock.md | 2 +- .../opencomputers/doc/zh_cn/block/charger.md | 2 +- .../doc/zh_cn/block/disassembler.md | 2 +- .../doc/zh_cn/block/diskdrive.md | 2 +- .../opencomputers/doc/zh_cn/block/geolyzer.md | 2 +- .../doc/zh_cn/block/hologram1.md | 2 +- .../opencomputers/doc/zh_cn/block/keyboard.md | 2 +- .../doc/zh_cn/block/motionsensor.md | 2 +- .../doc/zh_cn/block/netsplitter.md | 2 +- .../doc/zh_cn/block/powerconverter.md | 2 +- .../doc/zh_cn/block/powerdistributor.md | 2 +- .../opencomputers/doc/zh_cn/block/printer.md | 2 +- .../opencomputers/doc/zh_cn/block/rack.md | 2 +- .../opencomputers/doc/zh_cn/block/raid.md | 2 +- .../opencomputers/doc/zh_cn/block/redstone.md | 2 +- .../opencomputers/doc/zh_cn/block/relay.md | 2 +- .../opencomputers/doc/zh_cn/block/screen1.md | 2 +- .../opencomputers/doc/zh_cn/block/switch.md | 2 +- .../doc/zh_cn/block/transposer.md | 2 +- .../opencomputers/doc/zh_cn/block/waypoint.md | 2 +- .../doc/zh_cn/general/example.md | 20 +++++++++---------- .../doc/zh_cn/general/quickstart.md | 4 ++-- .../doc/zh_cn/item/abstractbuscard.md | 2 +- .../opencomputers/doc/zh_cn/item/acid.md | 2 +- .../opencomputers/doc/zh_cn/item/alu.md | 2 +- .../opencomputers/doc/zh_cn/item/analyzer.md | 2 +- .../doc/zh_cn/item/angelupgrade.md | 2 +- .../opencomputers/doc/zh_cn/item/apu1.md | 2 +- .../opencomputers/doc/zh_cn/item/arrowkeys.md | 2 +- .../doc/zh_cn/item/batteryupgrade1.md | 2 +- .../doc/zh_cn/item/buttongroup.md | 2 +- .../opencomputers/doc/zh_cn/item/card.md | 2 +- .../doc/zh_cn/item/cardcontainer1.md | 2 +- .../opencomputers/doc/zh_cn/item/chamelium.md | 2 +- .../opencomputers/doc/zh_cn/item/chip1.md | 2 +- .../doc/zh_cn/item/chunkloaderupgrade.md | 2 +- .../doc/zh_cn/item/circuitboard.md | 2 +- .../doc/zh_cn/item/componentbus1.md | 2 +- .../doc/zh_cn/item/controlunit.md | 2 +- .../opencomputers/doc/zh_cn/item/cpu1.md | 2 +- .../doc/zh_cn/item/craftingupgrade.md | 2 +- .../doc/zh_cn/item/cuttingwire.md | 2 +- .../doc/zh_cn/item/databaseupgrade1.md | 2 +- .../opencomputers/doc/zh_cn/item/datacard1.md | 2 +- .../opencomputers/doc/zh_cn/item/disk.md | 2 +- .../doc/zh_cn/item/diskdrivemountable.md | 2 +- .../doc/zh_cn/item/dronecase1.md | 2 +- .../opencomputers/doc/zh_cn/item/eeprom.md | 2 +- .../doc/zh_cn/item/experienceupgrade.md | 2 +- .../opencomputers/doc/zh_cn/item/floppy.md | 2 +- .../doc/zh_cn/item/generatorupgrade.md | 2 +- .../doc/zh_cn/item/graphicscard1.md | 2 +- .../opencomputers/doc/zh_cn/item/hdd1.md | 2 +- .../doc/zh_cn/item/hoverboots.md | 2 +- .../doc/zh_cn/item/hoverupgrade1.md | 2 +- .../doc/zh_cn/item/inkcartridge.md | 2 +- .../doc/zh_cn/item/internetcard.md | 2 +- .../opencomputers/doc/zh_cn/item/interweb.md | 2 +- .../zh_cn/item/inventorycontrollerupgrade.md | 2 +- .../doc/zh_cn/item/inventoryupgrade.md | 2 +- .../opencomputers/doc/zh_cn/item/lancard.md | 2 +- .../doc/zh_cn/item/leashupgrade.md | 2 +- .../doc/zh_cn/item/linkedcard.md | 2 +- .../opencomputers/doc/zh_cn/item/manual.md | 2 +- .../opencomputers/doc/zh_cn/item/mfu.md | 2 +- .../doc/zh_cn/item/microcontrollercase1.md | 2 +- .../doc/zh_cn/item/nanomachines.md | 2 +- .../doc/zh_cn/item/navigationupgrade.md | 2 +- .../opencomputers/doc/zh_cn/item/numpad.md | 2 +- .../doc/zh_cn/item/pistonupgrade.md | 2 +- .../doc/zh_cn/item/printedcircuitboard.md | 2 +- .../opencomputers/doc/zh_cn/item/ram1.md | 2 +- .../doc/zh_cn/item/rawCircuitBoard.md | 2 +- .../doc/zh_cn/item/redstonecard1.md | 2 +- .../opencomputers/doc/zh_cn/item/server1.md | 2 +- .../doc/zh_cn/item/signupgrade.md | 2 +- .../doc/zh_cn/item/solargeneratorupgrade.md | 2 +- .../doc/zh_cn/item/tabletCase1.md | 2 +- .../doc/zh_cn/item/tankcontrollerupgrade.md | 2 +- .../doc/zh_cn/item/tankupgrade.md | 2 +- .../opencomputers/doc/zh_cn/item/terminal.md | 2 +- .../doc/zh_cn/item/terminalserver.md | 2 +- .../doc/zh_cn/item/texturepicker.md | 2 +- .../doc/zh_cn/item/tractorbeamupgrade.md | 2 +- .../doc/zh_cn/item/tradingupgrade.md | 2 +- .../doc/zh_cn/item/transistor.md | 2 +- .../doc/zh_cn/item/upgradecontainer1.md | 2 +- .../opencomputers/doc/zh_cn/item/wlancard1.md | 2 +- .../doc/zh_cn/item/worldsensorcard.md | 2 +- .../opencomputers/doc/zh_cn/item/wrench.md | 2 +- .../data/forge/tags/blocks/pistons.json | 7 +++++++ .../data/forge/tags/items/pistons.json | 7 +++++++ .../opencomputers/tags/blocks/adapter.json | 6 ++++++ .../opencomputers/tags/blocks/assembler.json | 6 ++++++ .../data/opencomputers/tags/blocks/cable.json | 6 ++++++ .../opencomputers/tags/blocks/capacitor.json | 6 ++++++ .../tags/blocks/carpetedcapacitor.json | 6 ++++++ .../data/opencomputers/tags/blocks/case1.json | 6 ++++++ .../data/opencomputers/tags/blocks/case2.json | 6 ++++++ .../data/opencomputers/tags/blocks/case3.json | 6 ++++++ .../tags/blocks/chameliumblock.json | 6 ++++++ .../opencomputers/tags/blocks/charger.json | 6 ++++++ .../tags/blocks/disassembler.json | 6 ++++++ .../opencomputers/tags/blocks/diskdrive.json | 6 ++++++ .../opencomputers/tags/blocks/geolyzer.json | 6 ++++++ .../opencomputers/tags/blocks/hologram1.json | 6 ++++++ .../opencomputers/tags/blocks/hologram2.json | 6 ++++++ .../opencomputers/tags/blocks/keyboard.json | 6 ++++++ .../tags/blocks/motionsensor.json | 6 ++++++ .../tags/blocks/netsplitter.json | 6 ++++++ .../tags/blocks/powerconverter.json | 6 ++++++ .../tags/blocks/powerdistributor.json | 6 ++++++ .../opencomputers/tags/blocks/printer.json | 6 ++++++ .../data/opencomputers/tags/blocks/rack.json | 6 ++++++ .../data/opencomputers/tags/blocks/raid.json | 6 ++++++ .../opencomputers/tags/blocks/redstone.json | 6 ++++++ .../data/opencomputers/tags/blocks/relay.json | 6 ++++++ .../opencomputers/tags/blocks/screen1.json | 6 ++++++ .../opencomputers/tags/blocks/screen2.json | 6 ++++++ .../opencomputers/tags/blocks/screen3.json | 6 ++++++ .../tags/blocks/stoneendstone.json | 6 ++++++ .../opencomputers/tags/blocks/transposer.json | 6 ++++++ .../opencomputers/tags/blocks/waypoint.json | 6 ++++++ .../opencomputers/tags/items/analyzer.json | 6 ++++++ .../tags/items/angelupgrade.json | 6 ++++++ .../data/opencomputers/tags/items/apu1.json | 6 ++++++ .../data/opencomputers/tags/items/apu2.json | 6 ++++++ .../tags/items/batteryupgrade1.json | 6 ++++++ .../tags/items/batteryupgrade2.json | 6 ++++++ .../tags/items/batteryupgrade3.json | 6 ++++++ .../tags/items/cardcontainer1.json | 6 ++++++ .../tags/items/cardcontainer2.json | 6 ++++++ .../tags/items/cardcontainer3.json | 6 ++++++ .../opencomputers/tags/items/chamelium.json | 6 ++++++ .../opencomputers/tags/items/chipdiamond.json | 6 ++++++ .../tags/items/chunkloaderupgrade.json | 6 ++++++ .../tags/items/circuitchip1.json | 6 ++++++ .../tags/items/circuitchip2.json | 6 ++++++ .../tags/items/circuitchip3.json | 6 ++++++ .../tags/items/componentbus1.json | 6 ++++++ .../tags/items/componentbus2.json | 6 ++++++ .../tags/items/componentbus3.json | 6 ++++++ .../data/opencomputers/tags/items/cpu1.json | 6 ++++++ .../data/opencomputers/tags/items/cpu2.json | 6 ++++++ .../data/opencomputers/tags/items/cpu3.json | 6 ++++++ .../tags/items/craftingupgrade.json | 6 ++++++ .../tags/items/databaseupgrade1.json | 6 ++++++ .../tags/items/databaseupgrade2.json | 6 ++++++ .../tags/items/databaseupgrade3.json | 6 ++++++ .../opencomputers/tags/items/datacard1.json | 6 ++++++ .../opencomputers/tags/items/datacard2.json | 6 ++++++ .../opencomputers/tags/items/datacard3.json | 6 ++++++ .../tags/items/diskdrivemountable.json | 6 ++++++ .../opencomputers/tags/items/dronecase1.json | 6 ++++++ .../opencomputers/tags/items/dronecase2.json | 6 ++++++ .../data/opencomputers/tags/items/eeprom.json | 6 ++++++ .../tags/items/experienceupgrade.json | 6 ++++++ .../data/opencomputers/tags/items/floppy.json | 6 ++++++ .../tags/items/generatorupgrade.json | 6 ++++++ .../tags/items/graphicscard1.json | 6 ++++++ .../tags/items/graphicscard2.json | 6 ++++++ .../tags/items/graphicscard3.json | 6 ++++++ .../data/opencomputers/tags/items/hdd1.json | 6 ++++++ .../data/opencomputers/tags/items/hdd2.json | 6 ++++++ .../data/opencomputers/tags/items/hdd3.json | 6 ++++++ .../opencomputers/tags/items/hoverboots.json | 6 ++++++ .../tags/items/hoverupgrade1.json | 6 ++++++ .../tags/items/hoverupgrade2.json | 6 ++++++ .../tags/items/inkcartridge.json | 6 ++++++ .../tags/items/inkcartridgeempty.json | 6 ++++++ .../tags/items/internetcard.json | 6 ++++++ .../items/inventorycontrollerupgrade.json | 6 ++++++ .../tags/items/inventoryupgrade.json | 6 ++++++ .../opencomputers/tags/items/lancard.json | 6 ++++++ .../tags/items/leashupgrade.json | 6 ++++++ .../opencomputers/tags/items/linkedcard.json | 6 ++++++ .../data/opencomputers/tags/items/manual.json | 6 ++++++ .../tags/items/materialacid.json | 6 ++++++ .../opencomputers/tags/items/materialalu.json | 6 ++++++ .../tags/items/materialarrowkey.json | 6 ++++++ .../tags/items/materialbuttongroup.json | 6 ++++++ .../tags/items/materialcard.json | 6 ++++++ .../tags/items/materialcircuitboard.json | 6 ++++++ .../items/materialcircuitboardprinted.json | 6 ++++++ .../tags/items/materialcircuitboardraw.json | 6 ++++++ .../opencomputers/tags/items/materialcu.json | 6 ++++++ .../tags/items/materialcuttingwire.json | 6 ++++++ .../tags/items/materialdisk.json | 6 ++++++ .../tags/items/materialinterweb.json | 6 ++++++ .../tags/items/materialnumpad.json | 6 ++++++ .../tags/items/materialtransistor.json | 6 ++++++ .../data/opencomputers/tags/items/mfu.json | 6 ++++++ .../tags/items/microcontrollercase1.json | 6 ++++++ .../tags/items/microcontrollercase2.json | 6 ++++++ .../tags/items/nanomachines.json | 6 ++++++ .../tags/items/navigationupgrade.json | 6 ++++++ .../tags/items/pistonupgrade.json | 6 ++++++ .../data/opencomputers/tags/items/ram1.json | 6 ++++++ .../data/opencomputers/tags/items/ram2.json | 6 ++++++ .../data/opencomputers/tags/items/ram3.json | 6 ++++++ .../data/opencomputers/tags/items/ram4.json | 6 ++++++ .../data/opencomputers/tags/items/ram5.json | 6 ++++++ .../data/opencomputers/tags/items/ram6.json | 6 ++++++ .../tags/items/redstonecard1.json | 6 ++++++ .../tags/items/redstonecard2.json | 6 ++++++ .../opencomputers/tags/items/server1.json | 6 ++++++ .../opencomputers/tags/items/server2.json | 6 ++++++ .../opencomputers/tags/items/server3.json | 6 ++++++ .../opencomputers/tags/items/signupgrade.json | 6 ++++++ .../tags/items/solargeneratorupgrade.json | 6 ++++++ .../tags/items/stickypistonupgrade.json | 6 ++++++ .../opencomputers/tags/items/tabletcase1.json | 6 ++++++ .../opencomputers/tags/items/tabletcase2.json | 6 ++++++ .../tags/items/tankcontrollerupgrade.json | 6 ++++++ .../opencomputers/tags/items/tankupgrade.json | 6 ++++++ .../opencomputers/tags/items/terminal.json | 6 ++++++ .../tags/items/terminalserver.json | 6 ++++++ .../tags/items/texturepicker.json | 6 ++++++ .../tags/items/tractorbeamupgrade.json | 6 ++++++ .../tags/items/tradingupgrade.json | 6 ++++++ .../tags/items/upgradecontainer1.json | 6 ++++++ .../tags/items/upgradecontainer2.json | 6 ++++++ .../tags/items/upgradecontainer3.json | 6 ++++++ .../opencomputers/tags/items/wlancard1.json | 6 ++++++ .../opencomputers/tags/items/wlancard2.json | 6 ++++++ .../data/opencomputers/tags/items/wrench.json | 6 ++++++ .../render/ItemStackImageRenderer.scala | 4 ++++ .../segment/render/OreDictImageProvider.scala | 14 +++++++++---- 606 files changed, 1341 insertions(+), 513 deletions(-) create mode 100644 src/main/resources/data/forge/tags/blocks/pistons.json create mode 100644 src/main/resources/data/forge/tags/items/pistons.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/adapter.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/assembler.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/cable.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/capacitor.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/carpetedcapacitor.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/case1.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/case2.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/case3.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/chameliumblock.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/charger.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/disassembler.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/diskdrive.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/geolyzer.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/hologram1.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/hologram2.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/keyboard.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/motionsensor.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/netsplitter.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/powerconverter.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/powerdistributor.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/printer.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/rack.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/raid.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/redstone.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/relay.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/screen1.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/screen2.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/screen3.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/transposer.json create mode 100644 src/main/resources/data/opencomputers/tags/blocks/waypoint.json create mode 100644 src/main/resources/data/opencomputers/tags/items/analyzer.json create mode 100644 src/main/resources/data/opencomputers/tags/items/angelupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/apu1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/apu2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/batteryupgrade1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/batteryupgrade2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/batteryupgrade3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cardcontainer1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cardcontainer2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cardcontainer3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/chamelium.json create mode 100644 src/main/resources/data/opencomputers/tags/items/chipdiamond.json create mode 100644 src/main/resources/data/opencomputers/tags/items/chunkloaderupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/circuitchip1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/circuitchip2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/circuitchip3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/componentbus1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/componentbus2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/componentbus3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cpu1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cpu2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cpu3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/craftingupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/databaseupgrade1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/databaseupgrade2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/databaseupgrade3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/datacard1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/datacard2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/datacard3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/diskdrivemountable.json create mode 100644 src/main/resources/data/opencomputers/tags/items/dronecase1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/dronecase2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/eeprom.json create mode 100644 src/main/resources/data/opencomputers/tags/items/experienceupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/floppy.json create mode 100644 src/main/resources/data/opencomputers/tags/items/generatorupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/graphicscard1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/graphicscard2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/graphicscard3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hdd1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hdd2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hdd3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hoverboots.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hoverupgrade1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hoverupgrade2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/inkcartridge.json create mode 100644 src/main/resources/data/opencomputers/tags/items/inkcartridgeempty.json create mode 100644 src/main/resources/data/opencomputers/tags/items/internetcard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/inventorycontrollerupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/inventoryupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/lancard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/leashupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/linkedcard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/manual.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialacid.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialalu.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialarrowkey.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialbuttongroup.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcircuitboard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcircuitboardprinted.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcircuitboardraw.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcu.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialcuttingwire.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialdisk.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialinterweb.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialnumpad.json create mode 100644 src/main/resources/data/opencomputers/tags/items/materialtransistor.json create mode 100644 src/main/resources/data/opencomputers/tags/items/mfu.json create mode 100644 src/main/resources/data/opencomputers/tags/items/microcontrollercase1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/microcontrollercase2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/nanomachines.json create mode 100644 src/main/resources/data/opencomputers/tags/items/navigationupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/pistonupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram4.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram5.json create mode 100644 src/main/resources/data/opencomputers/tags/items/ram6.json create mode 100644 src/main/resources/data/opencomputers/tags/items/redstonecard1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/redstonecard2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/server1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/server2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/server3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/signupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/solargeneratorupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/stickypistonupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tabletcase1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tabletcase2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tankcontrollerupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tankupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/terminal.json create mode 100644 src/main/resources/data/opencomputers/tags/items/terminalserver.json create mode 100644 src/main/resources/data/opencomputers/tags/items/texturepicker.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tractorbeamupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/tradingupgrade.json create mode 100644 src/main/resources/data/opencomputers/tags/items/upgradecontainer1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/upgradecontainer2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/upgradecontainer3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/wlancard1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/wlancard2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/wrench.json diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/adapter.md b/src/main/resources/assets/opencomputers/doc/de_de/block/adapter.md index e588bc8030..6fe53a4124 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/adapter.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/adapter.md @@ -1,6 +1,6 @@ # Adapter -![Nun mit 100% mehr Alles!](oredict:oc:adapter) +![Nun mit 100% mehr Alles!](oredict:opencomputers:adapter) Der Adapter ermöglicht es [Computern](../general/computer.md) mit Vanilla-Minecraft-Blöcken oder Blöcken von anderen Mods zu interagieren. Unterstützte Blöcke neben dem Adapter werden als Komponenten des Computers aufgelistet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/assembler.md b/src/main/resources/assets/opencomputers/doc/de_de/block/assembler.md index eb6f6d08a1..a845f24fdb 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/assembler.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/assembler.md @@ -1,6 +1,6 @@ # Elektronik-Werkbank -![Harder, better, faster, stronger.](oredict:oc:assembler) +![Harder, better, faster, stronger.](oredict:opencomputers:assembler) Die Elektronik-Werkbank ist eine fortgeschrittene Maschine welche verwendet werden kann, um komplexere elektronische Geräte wie [Roboter](robot.md), [Drohnen](../item/drone.md) oder [Tablets](../item/tablet.md) zu bauen. Sie benötigen eine große Menge an Energie um Geräte zu montieren, daher wird empfohlen, sie mittels einer [Kondensatorbank (capacitor bank)](capacitor.md) mit Energie zu versorgen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/cable.md b/src/main/resources/assets/opencomputers/doc/de_de/block/cable.md index 0316ae0e15..6d0044b160 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/cable.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/cable.md @@ -1,6 +1,6 @@ # Kabel -![Salat.](oredict:oc:cable) +![Salat.](oredict:opencomputers:cable) Das Kabel ist eine Möglichkeit, [Computer](../general/computer.md) und Maschinen, die weit auseinander stehen zu verbinden. Kompakte Bauten, bei denen sich alle Komponenten direkt oder indirekt berühren (die meisten Blöcke verhalten sich für Kabel) benötigen in der Regel keine Kabel. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/capacitor.md b/src/main/resources/assets/opencomputers/doc/de_de/block/capacitor.md index 54f8a3b4e8..1d968d0000 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/capacitor.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/capacitor.md @@ -1,6 +1,6 @@ # Kondensator -![It's over 9000.](oredict:oc:capacitor) +![It's over 9000.](oredict:opencomputers:capacitor) Der Kondensator speichert Energie welche vom Netzwerk genutzt werden kann. Dadurch funktioniert es als Energiepuffer wenn es benötigt wird. Im Gegensatz zum Konvertieren von Energietypen anderer Mods (mittels eines [Energiekonverters](powerConverter.md)) erfolgt die Umwandlung von Energie innerhalb eines Subnetzwerkes ohne Verzögerung. Es ist nützlich, einen internen Energiepuffer für energieintensive Aufgaben (wie [Montage](assembler.md) oder das [Aufladen](charger.md) von Geräten wie [Robotern](robot.md) oder [Drohnen](../item/drone.md) zu haben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/case1.md b/src/main/resources/assets/opencomputers/doc/de_de/block/case1.md index de68ea0afe..7c74e5eb53 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/case1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/case1.md @@ -1,6 +1,6 @@ # Computergehäuse -![Just in case.](oredict:oc:case1) +![Just in case.](oredict:opencomputers:case1) Computergehäuse gibt es in drei verschiedenen Stufen. Die Stufe bestimmt die maximale Anzahl an Komponenten, die eingesetzt werden können. Es gibt eine zusätzliche Stufe für den Kreativ-Modus. Gehäuse können ebenfalls in einer [Elektronik-Werkbank](assembler.md) platziert werden, um [Roboter](robot.md) zu bauen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/chameliumblock.md b/src/main/resources/assets/opencomputers/doc/de_de/block/chameliumblock.md index 92f1167937..8bd97f886d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/chameliumblock.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/chameliumblock.md @@ -1,6 +1,6 @@ # Chamälium-Block -![So... nichtssagend...](oredict:oc:chameliumBlock) +![So... nichtssagend...](oredict:opencomputers:chameliumBlock) Einige Stücke [Chamälium](../item/chamelium.md) können kombiniert werden, um einen dekorativen Monochrom-Block zu schaffen. Chamäliumblöcke können mit einer der 16 Minecraft-Farben gefärbt werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/charger.md b/src/main/resources/assets/opencomputers/doc/de_de/block/charger.md index 747a5b1d44..755f87ddc7 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/charger.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/charger.md @@ -1,6 +1,6 @@ # Ladegerät -![Gehen wir es an!](oredict:oc:charger) +![Gehen wir es an!](oredict:opencomputers:charger) Das Ladegerät wird verwendet, um Geräte wie den [Roboter](robot.md), [Drohnen](../item/drone.md) und [Tablets](../item/tablet.md) zu laden. Ein Ladegerät muss mit einem Redstonesignal aktiviert werden. Die Ladegeschwindigkeit hängt von der Stärke des Redstonesignals ab. Eine Stärke von 15 bedeutet 100% Ladegeschwindigkeit. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/disassembler.md b/src/main/resources/assets/opencomputers/doc/de_de/block/disassembler.md index 10bcece267..b5accf6b0f 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/disassembler.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/disassembler.md @@ -1,6 +1,6 @@ # Recycler -![Bau' es auf, reiß' es ab.](oredict:oc:disassembler) +![Bau' es auf, reiß' es ab.](oredict:opencomputers:disassembler) Der Recycler kann verwendet werden, um die meisten Items in ihre Bestandteile aufzuteilen. Dies ist besonders nützlich um Materialien von unnütz gewordenen Bauteilen zurückzubekommen, oder um Geräte zu demontieren, die nicht länger nützlich sind oder inkorrekt gebaut wurden (z.B. [Roboter](robot.md) ohne [Betriebssystem](../general/openOS.md)). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/diskdrive.md b/src/main/resources/assets/opencomputers/doc/de_de/block/diskdrive.md index 1317702848..8715aefd84 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/diskdrive.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/diskdrive.md @@ -1,6 +1,6 @@ # Diskettenlaufwerk -![Und das Rad dreht sich rundherum...](oredict:oc:diskDrive) +![Und das Rad dreht sich rundherum...](oredict:opencomputers:diskDrive) Das Diskettenlaufwerk kann verwendet werden, um [Disketten](../item/floppy.md) mittels eines Computers auszulesen. Dies ist am Anfang nützlich, da niedrigstufige [Computergehäuse](case1.md) keinen eingebauten Diskettenslot haben. Dennoch benötigst du ein Betriebssystem um den Computer hochzufahren. Eine [OpenOS](../general/openOS.md)-Diskette kann mit einer leeren [Diskette](../item/floppy.md) und dem [Handbuch](../item/manual.md) gefertigt werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/geolyzer.md b/src/main/resources/assets/opencomputers/doc/de_de/block/geolyzer.md index 7d3a6582ad..d48cacfce3 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/geolyzer.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/geolyzer.md @@ -1,6 +1,6 @@ # Geolyzer -![Er rockt!](oredict:oc:geolyzer) +![Er rockt!](oredict:opencomputers:geolyzer) Der Geolyzer kann von [Computern](../general/computer.md) verwendet werden, um das umgebende Terrain auf die ungefähre Härte von Blöcken zu analysieren. Dies ist nützlich um Karten der Gegend zu generieren, die auf einem [Hologrammprojektor](hologram1.md) angezeigt werden. Erze sind üblicherweise härter als Erde und Stein, daher kann man mit dem Geolyzer potenziell wertvolle Blöcke finden. Die Ergebnisse sind in der Regel etwas ungenau, theoretisch können mehrere Scans zu genaueren Ergebnissen führen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/hologram1.md b/src/main/resources/assets/opencomputers/doc/de_de/block/hologram1.md index 990192f466..48ad70c42e 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/hologram1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/hologram1.md @@ -1,6 +1,6 @@ # Hologrammprojektor -![Is this the real life? Is this just fantasy?](oredict:oc:hologram1) +![Is this the real life? Is this just fantasy?](oredict:opencomputers:hologram1) Der Hologrammprojektor ist ein 3D-Display, das bedeutet es stellt ein dreidimensionales Array von "Voxeln" bereit, die von einem Computer einzeln aktiviert oder deaktiviert können. Der Stufe-2-Projektor hat dieselbe Auflösung, unterstützt allerdings die Darstellung in drei verschiedenen Farben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/keyboard.md b/src/main/resources/assets/opencomputers/doc/de_de/block/keyboard.md index 27046aa4a9..dbf54146b7 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/keyboard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/keyboard.md @@ -1,6 +1,6 @@ # Tastatur -![QWERTY](oredict:oc:keyboard) +![QWERTY](oredict:opencomputers:keyboard) Eine Tastatur wird benötigt um Text auf [Bildschirme](screen1.md) zu schreiben, egal ob in der Welt platziert oder in Geräte eingebaut (wie bei [Robotern](robot.md) oder [Tablets](../item/tablet.md)). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/motionsensor.md b/src/main/resources/assets/opencomputers/doc/de_de/block/motionsensor.md index 0da1a476f3..3a2073c73e 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/motionsensor.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/motionsensor.md @@ -1,6 +1,6 @@ # Bewegungssensor -![Nicht. Blinzeln.](oredict:oc:motionSensor) +![Nicht. Blinzeln.](oredict:opencomputers:motionSensor) Der Bewegungssensor erlaubt es [Computern](../general/computer.md) Bewegungen von lebenden Entities zu erkennen. Wenn die Geschwindigkeit eines Lebewesens einen Schwellenwert überschreitet wird ein Signal zum angeschlossenen Computer gesendet. Der Schwellwert kann mittels der Komponenten-API welche der Bewegungssensor bereitstellt verändert werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/netsplitter.md b/src/main/resources/assets/opencomputers/doc/de_de/block/netsplitter.md index d9b2dbb477..d06dc6c550 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/netsplitter.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/netsplitter.md @@ -1,6 +1,6 @@ # Net Splitter -![*.net *.split](oredict:oc:netSplitter) +![*.net *.split](oredict:opencomputers:netSplitter) Der Net Splitter ist ein Gerät das eine Kontrolle der Verbindungen zwischen Subnetzwerken ermöglicht. Im Gegensatz zum [Switch](switch.md) oder dem [Energiekonverter](powerConverter.md) verbindet es benachbarte Subnetzwerke direkt, das bedeutet, dass auf Komponenten zugegriffen werden kann. Die Verbindungen zu jeder Seite kann mit einem Schraubenschlüssel eingestellt werden (zum Beispiel den [srench](../item/wrench.md)). Wenn ein Redstonesignal an den Netzwerksplitter gesendet wird, werden alle Seiten invertiert. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/powerconverter.md b/src/main/resources/assets/opencomputers/doc/de_de/block/powerconverter.md index 5de9f9e6d1..c92f4d3836 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/powerconverter.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/powerconverter.md @@ -1,5 +1,5 @@ # Leistungswandler -![Einer von uns? Einer von uns!](oredict:oc:powerConverter) +![Einer von uns? Einer von uns!](oredict:opencomputers:powerConverter) Der Leistungswandler stellt den schnellsten Weg dar, um Energie von anderen Mods zu OpenComputers eigenem Energiesystem zu konvertieren. Wenn nur ein Computer verwendet wird, wird dieser nicht nötig sein. Bei einer großen Kondensatorbank, die nur dann und wann geleert wird, ist er ebenfalls nicht nötig. Wenn hingegen eine [Elektronik-Werkbank](assembler.md) oder eine [Ladestation](charger.md) direkt mit Energie versorgt werden soll, ist es eine gute Idee, diesen Konverter zu verwenden, anstatt sie direkt zu einer externen Energiequelle anzuschließen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/powerdistributor.md b/src/main/resources/assets/opencomputers/doc/de_de/block/powerdistributor.md index 192d90725c..796a1e6757 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/powerdistributor.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/powerdistributor.md @@ -1,5 +1,5 @@ # Stromverteiler -![Power to the masses.](oredict:oc:powerDistributor) +![Power to the masses.](oredict:opencomputers:powerDistributor) Der Stromverteiler verteilt Energie in einem geteilten Energiespeicher (wie beispielsweise in einem [Kondensator](capacitor.md). Dadurch können verschiedene Subnetzwerke dieselbe Energiequelle verwenden ohne Komponenten sichtbar zu machen. Es balanciert die Energie in allen Subnetzwerken aus, sodass sie alle die selbe relative Menge an Energie haben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/printer.md b/src/main/resources/assets/opencomputers/doc/de_de/block/printer.md index 5bcb842475..bfec00fa32 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/printer.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/printer.md @@ -1,6 +1,6 @@ # 3D-Drucker -![2D printing is so yesteryear.](oredict:oc:printer) +![2D printing is so yesteryear.](oredict:opencomputers:printer) 3D-Drucker erlauben es, Blöcke von jeder Form mit jeder Art von Textur zu drucken. Um mit 3D-Druckern anzufangen wird ein 3D-Drucker und ein Computer benötigt. Dadurch erhält man Zugriff auf die `printer3d`-Komponenten-API. Hiermit können [Modelle](print.md) erstellt und gedruckt werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/raid.md b/src/main/resources/assets/opencomputers/doc/de_de/block/raid.md index 586b067c1d..ead8ba3b34 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/raid.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/raid.md @@ -1,6 +1,6 @@ # RAID -![40 man instance.](oredict:oc:raid) +![40 man instance.](oredict:opencomputers:raid) Der RAID-Block enthält drei [Festplatten](../item/hdd1.md), welche zu einem einzelnen Dateisystem kombiniert werden. Dieses einzelne Dateisystem hat als Kapazität die Summe der einzelnen Kapazitäten. Das Dateisystem kann mit allen angeschlossenen [Computern](../general/computer.md) verwendet werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/redstone.md b/src/main/resources/assets/opencomputers/doc/de_de/block/redstone.md index eafa6bc0fd..3bb9812fe1 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/redstone.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/redstone.md @@ -1,6 +1,6 @@ # Redstone-I/O -![Hi Red.](oredict:oc:redstone) +![Hi Red.](oredict:opencomputers:redstone) Der Redstone-I/O-Block kann verwendet werden, um ferngesteuert Redstonesignale auszugeben und einzulesen. Es verhält sich wie ein Hybrid einer Stufe-1- und einer Stufe-2-[Redstonekarte](../item/redstoneCard1.md). Es kann analoge und gebündelte Signale lesen wie schreiben, aber kann keine kabellosen Redstonesignale ausgeben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/relay.md b/src/main/resources/assets/opencomputers/doc/de_de/block/relay.md index e2842edd65..3650db3194 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/relay.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/relay.md @@ -1,6 +1,6 @@ # Relay -![Baut Brücken.](oredict:oc:relay) +![Baut Brücken.](oredict:opencomputers:relay) Das Relay kann verwendet werden um verschiedene Subnetzwerken das Senden von Nachrichten zueinander zu ermöglichen, ohne Komponenten Computern in anderen Netzen zugänglich zu machen. Grundsätzlich ist es eine gute Idee Komponenten lokal zu behalten, damit [Computer](../general/computer.md) nicht die falschen Komponenten ansprechen oder Komponenten-Overflows zu verursachen (welche dazu führen, dass Computer crashen und nicht hochfahren.) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/screen1.md b/src/main/resources/assets/opencomputers/doc/de_de/block/screen1.md index 4ad4be4fef..e7dab91e4c 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/screen1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/screen1.md @@ -1,6 +1,6 @@ # Bildschirme -![Kannst du das sehen?](oredict:oc:screen1) +![Kannst du das sehen?](oredict:opencomputers:screen1) Ein Bildschirm wird in Kombinationen mit [Grafikkarten](../item/graphicsCard1.md) verwendet, um mit [Computern](../general/computer.md) Text darzustellen. Verschiedene Stufen haben unterschiedliche Fähigkeiten, wie die Unterstützung verschiedener Auflösungen und Farbtiefen. Sie reichen von geringauflösenden Monochromdisplays zu hochauflösenden Displays mit bis zu 256 Farben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/serverrack.md b/src/main/resources/assets/opencomputers/doc/de_de/block/serverrack.md index b7125eb7e6..575cfd1da6 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/serverrack.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/serverrack.md @@ -1,6 +1,6 @@ # Serverschrank -![Free housing.](oredict:oc:serverRack) +![Free housing.](oredict:opencomputers:serverRack) Ein Serverschrank kann bis zu vier [Server](../item/server1.md) enthalten. Ein Server ist ein höherstufiger [Computer](../general/computer.md) welcher nur in einem Serverschrank laufen kann. Server können mit einer [Fernbedienung](../item/terminal.md) ferngesteuert werden. Die Anzahl der Terminals die gleichzeitig mit einem Server verbunden werden können wird von der Stufe des Servers begrenzt. Der Abstand zum Server, bis zu dem das Terminal funktioniert kann in der GUI des Serverschranks konfiguriert werden. Mehr Reichweite bedeutet mehr Energieverbrauch. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/de_de/block/waypoint.md index 26a4e24306..b8b23003fa 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/block/waypoint.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/block/waypoint.md @@ -1,6 +1,6 @@ # Wegpunkt -!["Da lang!" - "Nein, ganz falsch! Dort entlang!"](oredict:oc:waypoint) +!["Da lang!" - "Nein, ganz falsch! Dort entlang!"](oredict:opencomputers:waypoint) Der Wegpunkt kann mit Hilfe des [Navigations-Upgrades](../item/navigationUpgrade.md) erkannt werden. So können Geräte mit diesem Upgrade Wegpunkte verwenden um durch die Welt zu navigieren. Dies ist besonders nützlich zum Schreiben einfach wiederverwendbarer Programme für Geräte wie [Roboter](robot.md) und [Drohnen](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/general/quickstart.md b/src/main/resources/assets/opencomputers/doc/de_de/general/quickstart.md index e3e9766a0f..88225bed43 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/general/quickstart.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/general/quickstart.md @@ -6,7 +6,7 @@ Auch bekannt als "Wie man seinen ersten Computer baut". Um deinen ersten [Comput Zuerst wirst du ein [Computergehäuse](../block/case1.md) benötigen. Dies sit der Block der alle Komponenten benötigt und das Verhalten des gebauten Computers bestimmen. -![Ein Stufe-2-Computergehäuse.](oredict:oc:case2) +![Ein Stufe-2-Computergehäuse.](oredict:opencomputers:case2) Zum Beispiel musst du bestimmen, welche Stufe die [Grafikkarte](../item/graphicsCard1.md) du benötigst, ob eine [Netzwerkkarte](../item/lanCard.md) oder eine [Redstonekarte](../item/redstoneCard1.md) benötigt wird oder, wenn du im Creative-Mode spielst, eine [Debugkarte](../item/debugCard.md) gebraucht wird. @@ -38,7 +38,7 @@ Wenn du, wie in den Screenshots oben, ein Stufe-2-Gehäuse verwendet hast gibt e Es lebt! Zumindest sollte es das. Wenn es das nicht tut lief etwas falsch, und das [Analysegerät](../item/analyzer.md) wird bei der Fehlersuche helfen. Wenn der Computer jetzt läuft bist zu weitgehend fertig und der schwerste Teil ist vorbei. Du musst es nur noch mit einem Um die Ausgabe des Computers zu lesen wird ein [Bildschirm](../block/screen1.md) und eine Grafikkarte benötigt. -![Nein. Es ist kein Flachbildschirm](oredict:oc:screen2) +![Nein. Es ist kein Flachbildschirm](oredict:opencomputers:screen2) Platziere den Bildschirm entweder direkt neben den Computer oder verbinde sie mit einem Kabel. Setze eine Grafikkarte deiner Wahl in den Computer ein. Nun sollte ein blinkender Cursor auf dem Bildschirm sichtbar sein. Jetzt fehlt nur noch eine [Tastatur](../block/keyboard.md) entweder direkt auf dem Bildschirm oder neben dem Bildschirm (mit Ausrichtung auf diesen). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/abstractbuscard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/abstractbuscard.md index 5807e0ae59..e47a712465 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/abstractbuscard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/abstractbuscard.md @@ -1,5 +1,5 @@ # Abstrakter-Bus-Karte -![Mehr Netzwerktechnik!](oredict:oc:abstractBusCard) +![Mehr Netzwerktechnik!](oredict:opencomputers:abstractBusCard) Diese Karte erlaubt es [Computern](../general/computer.md), [Servern](server1.md) und [Robotern](../block/robot.md) mit Abstract Busses von StargateTech2 zu interagieren. Wenn die Karte installiert ist, werden sich die entsprechenden Blöcke mit dem abstrakten Bus verbinden und eine Komponente wird für die Maschine sichtbar. Damit können Nachrichten über den Bus gesendet werden. Hereinkommende Nachrichten werden in Signale konvertiert und zur Maschine gesendet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/acid.md b/src/main/resources/assets/opencomputers/doc/de_de/item/acid.md index f9af47bc2f..f037d67c67 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/acid.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/acid.md @@ -1,5 +1,5 @@ # Grog -![Reflux?](oredict:oc:materialAcid) +![Reflux?](oredict:opencomputers:materialAcid) Wird nur bei Hard-Mode-Rezepten verwenden. Es ist verwendet um [Leiterplatten](circuitBoard.md) zu ätzen, bevor sie zu [gedruckten Leiterplatten](printedCircuitBoard.md) werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/alu.md b/src/main/resources/assets/opencomputers/doc/de_de/item/alu.md index 485b93f6eb..39c6d115f6 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/alu.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/alu.md @@ -1,5 +1,5 @@ # Arithmetic Logic Unit -![Ich of logiks !](oredict:oc:materialALU) +![Ich of logiks !](oredict:opencomputers:materialALU) Wird für rechnende Komponenten wie [CPUs](cpu1.md) und [Grafikkarten](graphicsCard1.md) benötigt. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/analyzer.md b/src/main/resources/assets/opencomputers/doc/de_de/item/analyzer.md index ac436f7bf3..9b16a5c3a5 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/analyzer.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/analyzer.md @@ -1,6 +1,6 @@ # Messgerät -![Hat nichts mit Kirche zu tun.](oredict:oc:analyzer) +![Hat nichts mit Kirche zu tun.](oredict:opencomputers:analyzer) Das Messgerät ist ein nützliches Werkzeug um OpenComputers-Geräte auszulesen. Mittels eines Rechtsklickes (ggf. auch beim Schleichen) werden die Informationen in den Chatlog geschrieben. Darunter fallen grundlegende Dinge wie die Adresse von Komponenten, den Energiemassen im Subnetzwerk bis zu Informationen über einen Crash. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/angelupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/angelupgrade.md index 6fff45b40a..b1d03bf383 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/angelupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/angelupgrade.md @@ -1,5 +1,5 @@ # Engels-Upgrade -![Hallelujah.](oredict:oc:angelUpgrade) +![Hallelujah.](oredict:opencomputers:angelUpgrade) Dieses Upgrade ermöglicht es [Robotern](../block/robot.md) Blöcke mitten in die Luft zu platzieren. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/apu1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/apu1.md index e473727a61..de11a79b34 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/apu1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/apu1.md @@ -1,6 +1,6 @@ # Beschleunigter Prozessor (APU) -![Ausgereiftesteste Physik United.](oredict:oc:apu1) +![Ausgereiftesteste Physik United.](oredict:opencomputers:apu1) APUs sind eine Mischung aus [CPUs](cpu1.md) und [Grafikkarten](graphicsCard1.md). Diese zu nutzen ermöglicht es einen Kartenslot mehr zu verwenden. Wie eine normale CPU definiert es die Architektur des [Computers](../general/computer.md) und die Nummer der Komponenten die verwendet werden können. Es ermöglicht zudem auch grundlegende Grafikberechnungen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/arrowkeys.md b/src/main/resources/assets/opencomputers/doc/de_de/item/arrowkeys.md index ef3b92bf4d..31dbafbc09 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/arrowkeys.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/arrowkeys.md @@ -1,5 +1,5 @@ # Pfeiltasten -![Sei nur froh, dass die nicht aus echten Pfeilen hergestellt werden..](oredict:oc:materialArrowKey) +![Sei nur froh, dass die nicht aus echten Pfeilen hergestellt werden..](oredict:opencomputers:materialArrowKey) Pfeiltasten sind nötig um [Tastaturen](../block/keyboard.md) zu bauen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/batteryupgrade1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/batteryupgrade1.md index 432d48289f..23c549b56b 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/batteryupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/batteryupgrade1.md @@ -1,5 +1,5 @@ # Batterie-Upgrade -![Aus Metall angefertigt.](oredict:oc:batteryUpgrade1) +![Aus Metall angefertigt.](oredict:opencomputers:batteryUpgrade1) Dieses Upgrade erhöht den internen Energiespeicher von Geräten wie [Robotern](../block/robot.md) und [Tablets](tablet.md). Sie können damit länger verwendet werden, bevor sie mit einem [Ladegerät](../block/charger.md) geladen werden müssen. Hochstufige Upgrades haben mehr Batteriekapazität. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/buttongroup.md b/src/main/resources/assets/opencomputers/doc/de_de/item/buttongroup.md index d6320600d0..348e9b7c21 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/buttongroup.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/buttongroup.md @@ -1,5 +1,5 @@ # Tastengruppe -![MOAR KNÖPFE.](oredict:oc:materialButtonGroup) +![MOAR KNÖPFE.](oredict:opencomputers:materialButtonGroup) Weil du *immer* zu viele Knöpfe hast. Lüg nicht! Wir Shift-Klicken das Tastenrezept immer und immer wieder. Die Gruppen werden um Bauen von [Tastaturen](../block/keyboard.md) verwendet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/card.md b/src/main/resources/assets/opencomputers/doc/de_de/item/card.md index b7fa57fcf9..31e29df1f7 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/card.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/card.md @@ -1,5 +1,5 @@ # Karten -![Können nicht gelesen werden.](oredict:oc:materialCard) +![Können nicht gelesen werden.](oredict:opencomputers:materialCard) Übliches Ausgangsmaterial für kartenförmige Komponenten in OpenComputers (wie [Grafikkarten](graphicsCard1.md), [Netzwerkkarten](lanCard.md) usw.) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/cardcontainer1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/cardcontainer1.md index 68791181be..143b3e36d0 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/cardcontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/cardcontainer1.md @@ -1,5 +1,5 @@ # Kartenbehälter -![Ich kann Karten haben!!](oredict:oc:cardContainer1) +![Ich kann Karten haben!!](oredict:opencomputers:cardContainer1) Der Kartenbehälter ist ein Behälter-Upgrade für [Roboter](../block/robot.md) das es erlaubt, Karten in [Roboter](../block/robot.md) nach Bedarf einzusetzen oder zu entfernen. Die Stufe die eine Karte maximal haben darf gleicht der des Containers. Im Gegensatz zu normalen Upgrades ist die Komplexität das Doppelte seiner Stufe. Siehe [Komplexität](../block/robot.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/chamelium.md b/src/main/resources/assets/opencomputers/doc/de_de/item/chamelium.md index e0d5a9fdbc..3055969d0b 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/chamelium.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/chamelium.md @@ -1,6 +1,6 @@ # Chamälium -![Kommt vom Wort "Chamäleon".](oredict:oc:chamelium) +![Kommt vom Wort "Chamäleon".](oredict:opencomputers:chamelium) Chamälium ist ein formbares Material das für [3D-Drucke](../block/print.md) in [3D-Druckern](../block/printer.md) verwendet wird. Sonst ist es nutzlos und damit sehr nützlich um monochrome Bereiche zu erstellen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/chip1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/chip1.md index 5f8f5f20a8..6139510a17 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/chip1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/chip1.md @@ -1,5 +1,5 @@ # Microchips -![Nicht essbar.](oredict:oc:circuitChip1) +![Nicht essbar.](oredict:opencomputers:circuitChip1) Microchips sind die Grundlage für das Bauen von elektronischen Komponenten. Sie kommen in verschiedenen Stufen und ermöglichen unterschiedliche Komponentenstufen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/chunkloaderupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/chunkloaderupgrade.md index 634948efff..d1c700c453 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/chunkloaderupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/chunkloaderupgrade.md @@ -1,6 +1,6 @@ # Chunkloader-Upgrade -![Geht nirgendwo hin.](oredict:oc:chunkloaderUpgrade) +![Geht nirgendwo hin.](oredict:opencomputers:chunkloaderUpgrade) Das Chunkloader-Upgrade kann in Geräten wie [Robotern](../block/robot.md) oder [Mikrocontrollern](../block/microcontroller.md) installiert werden, um den Chunk in dem er sich befindet sowie umliegende Chunks geladen zu halten. Dies verbraucht jedoch Energie. Der Chunkloader kann mit der Komponenten-API ein- oder ausgeschaltet werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/circuitboard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/circuitboard.md index d0a3154a29..24d7116594 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/circuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/circuitboard.md @@ -1,5 +1,5 @@ # Leiterplatte -![Braucht mehr Gold.](oredict:oc:materialCircuitBoard) +![Braucht mehr Gold.](oredict:opencomputers:materialCircuitBoard) Zwischenitem das beim Herstellen von [bedruckten Leiterplatten](printedCircuitBoard.md) aus [rohen Leiterplatten](rawCircuitBoard.md) entsteht. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/componentbus1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/componentbus1.md index e60791f68e..b1d9bc22de 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/componentbus1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/componentbus1.md @@ -1,6 +1,6 @@ # Komponentenschnittstelle -![Fährt nirgendwo hin](oredict:oc:componentBus1) +![Fährt nirgendwo hin](oredict:opencomputers:componentBus1) Eine Komponentenschnittstelle ist ein [Server](server1.md)spezifisches Upgrade das es dem Server erlaubt, mit mehr Komponenten auf einmal zu kommunizieren ohne sich abzuschalten. Wie bei [CPUs](cpu1.md) auch ermöglichen höhere Stufen mehr Komponenten und höhere Stufen für Server ermöglichen mehr Slots für Komponentenschnittstellen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/controlunit.md b/src/main/resources/assets/opencomputers/doc/de_de/item/controlunit.md index 1b270e19a2..7a0af554f6 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/controlunit.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/controlunit.md @@ -1,5 +1,5 @@ # Kontrolleinheit -![Mit eingebauter Cruising-Funktion.](oredict:oc:materialCU) +![Mit eingebauter Cruising-Funktion.](oredict:opencomputers:materialCU) Hochstufiges Craftingitem in weiter entwickelten Schaltkreisen wie [CPUs](cpu1.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/cpu1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/cpu1.md index 3fb63ace8e..b73671cc2d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/cpu1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/cpu1.md @@ -1,6 +1,6 @@ # CPU -![Gehirrrrrn.](oredict:oc:cpu1) +![Gehirrrrrn.](oredict:opencomputers:cpu1) Die zentrale Recheneinheit ist das Herz eines jeden [Computers](../general/computer.md) oder [Servers](server1.md). Sie definiert die Architektur des Gerätes und die Anzahl der Komponenten die maximal mit dem Gerät verbunden werden können bevor er zu funktionieren aufhört. Hochstufige CPUs ermöglich außerdem eine schnellere Ausführung. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/craftingupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/craftingupgrade.md index 806130c2a3..b270bba85d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/craftingupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/craftingupgrade.md @@ -1,5 +1,5 @@ # Crafting-Upgrade -![Kräftig.](oredict:oc:craftingUpgrade) +![Kräftig.](oredict:opencomputers:craftingUpgrade) Das Crafting-Upgrade erlaubt [Robotern](../block/robot.md) jede Art von Rezepten zu fertigen. Dabei werden Items aus dem [Inventar](../item/inventoryUpgrade.md) verwendet. Das 3x3-Netz im Inventar des Roboters wird als Werkbank verwendet. Items müssen entsprechend dem Rezept angeordnet sein. Ergebnisse werden im gewählten Slot im Inventar oder im nächsten freien Slot abgelegt, oder in die Welt geworfen, wenn kein Platz mehr übrig ist. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/cuttingwire.md b/src/main/resources/assets/opencomputers/doc/de_de/item/cuttingwire.md index 88e76beffa..d8cdd040e9 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/cuttingwire.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/cuttingwire.md @@ -1,5 +1,5 @@ # Schneidedraht -![Kein Würgedraht. Wirklich.](oredict:oc:materialCuttingWire) +![Kein Würgedraht. Wirklich.](oredict:opencomputers:materialCuttingWire) Dieses Item wird nur im Hard-Mode-Rezept für [Leiterplattenrohlinge](rawCircuitBoard.md) verwendet. Ineffektiv. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/databaseupgrade1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/databaseupgrade1.md index 6cc5e5d852..867280cb4e 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/databaseupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/databaseupgrade1.md @@ -1,6 +1,6 @@ # Datenbank-Upgrade -![Living in the database.](oredict:oc:databaseUpgrade1) +![Living in the database.](oredict:opencomputers:databaseUpgrade1) Das Datenbank-Upgrade kann konfiguriert werden um eine Liste von Itemstack-Repräsentationen zu speichern. Die können von anderen Komponenten verwendet werden. Das ist besonders nützlich für Items die mit ihren NBT-Daten sortiert werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/datacard1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/datacard1.md index 6812b2cbb5..e1a4f70215 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/datacard1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/datacard1.md @@ -1,6 +1,6 @@ # Datenkarte -![Im Gegensatz zum allgemeinen Glauben speichert das keine Daten.](oredict:oc:dataCard1) +![Im Gegensatz zum allgemeinen Glauben speichert das keine Daten.](oredict:opencomputers:dataCard1) Die Datenkarte ist eine Werkzeugkarte die einige Algorithmen zur Verfügung stellen die nur sehr schlecht oder sehr langsam implementierbar wären. Möglich ist Hashing und grundlegendes Inflating und Deflating.. Zudem enthält es ein angefügtes Dateisystem und stellt eine Vielzahl von Programmen zur Verfügung die die Funktionen der Karte verwenden, ähnlich der Internetkarte. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/disk.md b/src/main/resources/assets/opencomputers/doc/de_de/item/disk.md index e29167ac9a..4cf56b596d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/disk.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/disk.md @@ -1,5 +1,5 @@ # Speicherplatte -![World. RIP Terry Pratchett.](oredict:oc:materialDisk) +![World. RIP Terry Pratchett.](oredict:opencomputers:materialDisk) Grundlegende Komponente, die zum Bau von Speichermedien wie [Disketten](floppy.md) und [Festplatten](hdd1.md) benötigt wird. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/dronecase1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/dronecase1.md index 01c4d37793..d4911b1a59 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/dronecase1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/dronecase1.md @@ -1,6 +1,6 @@ # Drohnengehäuse -![Droning on.](oredict:oc:droneCase1) +![Droning on.](oredict:opencomputers:droneCase1) Das Drohnengehäuse wird verwendet um [Drohnen](drone.md) in der [Elektronik-Werkbank](../block/assembler.md) zu bauen. Drohnen sind leicht, schnell und sehr mobil, haben jedoch einen eingeschränkten Funktionenszeitraum (d.h. weniger Upgrade- und Komponentenslots sind verfügbar). Im Gegensatz zu [Robotern](../block/robot.md) können sie keine Werkzeuge verwenden und können nur indirekt mit der Welt interagieren. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/eeprom.md b/src/main/resources/assets/opencomputers/doc/de_de/item/eeprom.md index 29953d1084..d051f64030 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/eeprom.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/eeprom.md @@ -1,6 +1,6 @@ # EEPROM -![Let's get this party started.](oredict:oc:eeprom) +![Let's get this party started.](oredict:opencomputers:eeprom) Der EEPROM enthält den Code der verwendet wird um den Computer zu starten. Diese Daten sind als einfaches Bytearray gespeichert und können bei unterschiedlichen Architekturen andere Operationen auslösen. Das LUA-BIOS ist ein kleines Script das auf dem Dateisystem nach eine Datei namens `init.lua`. Auf anderen Architekturen kann es echter Maschinencode sein. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/experienceupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/experienceupgrade.md index 2228289a2e..fed97371fe 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/experienceupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/experienceupgrade.md @@ -1,6 +1,6 @@ # Erfahrungs-Upgrade -![Kaum sinnvoll, sehr cool.](oredict:oc:experienceUpgrade) +![Kaum sinnvoll, sehr cool.](oredict:opencomputers:experienceUpgrade) Das Erfahrungs-Upgrade ist ein sehr spezielles Upgrade, da es [Robotern](../block/robot.md) und [Drohnen](drone.md) ermöglicht, Erfahrung zu sammeln. Ein einziges Upgrade kann bis zu 30 Level speichern und ermöglicht damit kleinere Boni, wie schnellere Ausführung oder erhöhte Energiespeicherkapazität. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/floppy.md b/src/main/resources/assets/opencomputers/doc/de_de/item/floppy.md index 945ee14a22..94e5ad504e 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/floppy.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/floppy.md @@ -1,5 +1,5 @@ # Diskette -![Klobig](oredict:oc:floppy) +![Klobig](oredict:opencomputers:floppy) Die Diskette ist das günstigste und kleinste Speichermedium in OpenComputers. Es ist ein nützliches Item für das frühe Spiel um Daten zwischen den Geräten zu transferieren. Disketten mit nützlichen Programmen können in Dungeons gefunden werden (wie der OpenPrograms Package Manager, womit Programme von einem zentralen Github-Repository installiert werden können.) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/generatorupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/generatorupgrade.md index 3672b0a049..8958a14b5e 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/generatorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/generatorupgrade.md @@ -1,6 +1,6 @@ # Generator-Upgrade -![Generator X.](oredict:oc:generatorUpgrade) +![Generator X.](oredict:opencomputers:generatorUpgrade) Das Generator-Upgrade ermöglicht es Geräten sich im Betrieb neu aufzuladen. Zurzeit werden nur trockene Energiequellen (wie Kohle) unterstützt. Es hat ein internes Inventar welches ein Stack an Energiequellen halten kann. Überflüssige Energiequellen können über die Komponenten-API entfernt werden. Ein Upgrade von einem Roboter zu entfernen führt dazu, dass der Inhalt in die Welt geworfen wird. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/graphicscard1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/graphicscard1.md index 770922a49b..f49dff83af 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/graphicscard1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/graphicscard1.md @@ -1,6 +1,6 @@ # Grafikkarten -![Schicke Bilder.](oredict:oc:graphicsCard1) +![Schicke Bilder.](oredict:opencomputers:graphicsCard1) Die Grafikkarte ist essenziell für die meisten [Computer](../general/computer.md). Damit können sie Text auf einem verbundenen [Bildschirm](../block/screen1.md) darstellen. Grafikkarten kommen in verschiedenen Stufen, die zusammen mit dem Bildschirm unterschiedliche Auflösungen und Farbtiefen unterstützen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/hdd1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/hdd1.md index 97473bf80e..9791371a83 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/hdd1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/hdd1.md @@ -1,5 +1,5 @@ # Festplatte -![01010011 01110000 01100001 01100011 01100101.](oredict:oc:hdd1) +![01010011 01110000 01100001 01100011 01100101.](oredict:opencomputers:hdd1) Die Festplatten haben mehr Speicherplatz als andere OpenComputers-Speichermedien. Alle Medien arbeiten gleich schnell, haben allerdings unterschiedliche Speicherplatzgrößen. Es gibt auch Geräte die nur Disketten verwenden. Festplatten können in einem [RAID](../block/raid.md) platziert werden, um Geräten das Teilen des selben Dateisystems zu ermöglichen. Wenn eine Festplatte in einem RAID platziert werden, werden allerdings die Daten gelöscht. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/hoverboots.md b/src/main/resources/assets/opencomputers/doc/de_de/item/hoverboots.md index 091a40e357..4636704fd8 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/hoverboots.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/hoverboots.md @@ -1,6 +1,6 @@ # Schwebestiefel -![Tritt drauf!](oredict:oc:hoverBoots) +![Tritt drauf!](oredict:opencomputers:hoverBoots) Drohnen zu programmieren kann eine lange Zeit dauern. Es gibt allerdings eine Alternative dazu: Schwebestiefel. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/hoverupgrade1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/hoverupgrade1.md index f19dc925f6..a35ee0a35d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/hoverupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/hoverupgrade1.md @@ -1,6 +1,6 @@ # Schwebe-Upgrade -![Leicht wie eine Feder.](oredict:oc:hoverUpgrade1) +![Leicht wie eine Feder.](oredict:opencomputers:hoverUpgrade1) Das Schwebe-Upgrade erlaubt es [Robotern](../block/robot.md) viel höher über dem Boden zu fliegen als normal. Im Gegensatz zu [Drohnen](drone.md) können diese nämlich standardmäßig nur 8 Block hoch schweben. Das ist normal kein großes Problem, da sie sich trotzdem an Wänden entlang bewegen können. Sie können sich so bewegen: - Roboter können sich nur bewegen, wenn Start- und Zielposition gültig sind (z.B. um Brücken bauen zu können) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/inkcartridge.md b/src/main/resources/assets/opencomputers/doc/de_de/item/inkcartridge.md index d7655b3ab4..f17c0388eb 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/inkcartridge.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/inkcartridge.md @@ -1,5 +1,5 @@ # Tintenkartusche -![Regenbogenfarbig.](oredict:oc:inkCartridge) +![Regenbogenfarbig.](oredict:opencomputers:inkCartridge) Tintenkartuschen sind nützlich um den Farbbuffer von [3D-Druckern·](../block/printer.md) zu füllen. Es ist auch möglich sie mit Farbmitteln direkt zu färben, das ist aber ineffizient. Am besten kaufen Sie noch heute die echten OC Tintenfarben (TM) heute! (Disclaimer: Unter Umständen könnten sie mehr als der Drucker selbst kosten.) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/internetcard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/internetcard.md index 5d36d5c30e..defb260596 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/internetcard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/internetcard.md @@ -1,6 +1,6 @@ # Internetkarte -![Katzenvideos in 3, 2,....](oredict:oc:internetCard) +![Katzenvideos in 3, 2,....](oredict:opencomputers:internetCard) Internetkarten bieten [Computern](../general/computer.md) Internetzugriff. Einfache HTTP-Anfragen sind möglich, sowie einfache TCP-Client-Sockets die gelesen und beschrieben werden können. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/interweb.md b/src/main/resources/assets/opencomputers/doc/de_de/item/interweb.md index 558b45f43b..9dfadb8974 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/interweb.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/interweb.md @@ -1,5 +1,5 @@ # Internetz -![A website is a place where there's cobweb.](oredict:oc:materialInterweb) +![A website is a place where there's cobweb.](oredict:opencomputers:materialInterweb) Das Internetz ist eine grundlegende Komponente für alle Geräte die mit Hochdistanzkommunikation zusammenhängen. Es nutzt verrückte Mechaniken des Ends um Quantenlinienkommunikation zu ermöglichen. Wird vor allem in [Internetkarten](internetCard.md) und [verknüpften Karten](linkedCard.md) verwendet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/inventorycontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/inventorycontrollerupgrade.md index 90f1c1c663..2414f00291 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/inventorycontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/inventorycontrollerupgrade.md @@ -1,6 +1,6 @@ # Inventarbedienungs-Upgrade -![Ich bin unter Kontrolle.](oredict:oc:inventoryControllerUpgrade) +![Ich bin unter Kontrolle.](oredict:opencomputers:inventoryControllerUpgrade) Das Inventarbedienungs-Upgrade ermöglicht erweiterte Inventarinteraktionen für [Roboter](../block/robot.md) und [Drohnen](drone.md). Es erlaubt es dem Gerät explizit einzelne Slots in externen Inventaren zu verwenden. Es erlaubt es dem Gerät zudem detaillierte Informationen über seine Itemstacks zu lesen. Zuletzt stellt es Robotern eine Möglichkeit zur Verfügung, das Werkzeug ohne Hilfe zu wechseln. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/inventoryupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/inventoryupgrade.md index 94ca075c90..4954d5da89 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/inventoryupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/inventoryupgrade.md @@ -1,6 +1,6 @@ # Inventar-Upgrade -![Wo legt es diesen ganzen Kram hin...](oredict:oc:inventoryUpgrade) +![Wo legt es diesen ganzen Kram hin...](oredict:opencomputers:inventoryUpgrade) Das Inventar-Upgrade stellt Inventarslots für [Roboter](../block/robot.md) und [Drohnen](drone.md) bereit. Jedes Upgrade fügt 16 Inventarslots hinzu, bis zu einem Maximum von 64 Slots. Eine Drohne fügt 4 Slots pro Upgrade hinzu, bis zu einem Maximum von 8 Slots. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/lancard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/lancard.md index 968a1840cf..dbbe8dddbd 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/lancard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/lancard.md @@ -1,5 +1,5 @@ # Netzwerkkarte -![Tritt ins Netzwerk ein.](oredict:oc:lanCard) +![Tritt ins Netzwerk ein.](oredict:opencomputers:lanCard) Die Netzwerkkarte erlaubt es [Computern](../general/computer.md), Nachrichten vom Netzwerk zu empfangen und zum Netzwerk zu senden. Nachrichten (oder Pakete) können über das ganze Netzwerk oder zu spezifischen Knotenpunkten mit einer bestimmten Adresse gesendet werden. [Switche](../block/switch.md) und [Access Points](../block/accessPoint.md) können verwendet werden, um verschiedene Netzwerke miteinander zu verbinden. Nachrichten können dann zwischen verschiedenen Netzwerken hin- und hergesendet werden, auch zwischen unterschiedlichen Subnetzwerken. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/leashupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/leashupgrade.md index e7d4090a6b..499acdb7f8 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/leashupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/leashupgrade.md @@ -1,5 +1,5 @@ # Leinen-Upgrade -![-Angeleint- ~ Vexatos 2015](oredict:oc:leashUpgrade) +![-Angeleint- ~ Vexatos 2015](oredict:opencomputers:leashUpgrade) Das Leinen-Upgrade ermöglicht es, Leinen auf Tiere anzulegen. Das entsprechende Gerät (zum Beispiel eine [Drohne](drone.md)) kann dann Tiere führen, auch mehrere auf einmal. Es ist damit recht einfach Herden zu bewegen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/linkedcard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/linkedcard.md index 7a8d0f030e..f205000474 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/linkedcard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/linkedcard.md @@ -1,5 +1,5 @@ # Verlinkte Karte -![Ich fühle eine Verbindung](oredict:oc:linkedCard) +![Ich fühle eine Verbindung](oredict:opencomputers:linkedCard) Die verlinkte Karte ist eine spezialisierte und fortgeschrittene Version einer [Netzwerkkarte](lanCard.md). Gelinkte Karten treten nur im Paar auf und ermöglichen eine direkte Kommunikation zwischen den beiden Karten. Gelinkte Karten können über unbegrenzt weite Distanzen und über Dimensionen hinweg kommunizieren. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/manual.md b/src/main/resources/assets/opencomputers/doc/de_de/item/manual.md index 994f086141..47724f5207 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/manual.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/manual.md @@ -1,6 +1,6 @@ # Handbuch -![Ein gutes Buch.](oredict:oc:manual) +![Ein gutes Buch.](oredict:opencomputers:manual) Das Ding das du gerade liest! Das Handbuch enthält eine Vielfalt von Informationen über OpenComputers (und vielleicht mehr). Wenn du mehr Informatonen über ein Item oder einen Block im Mod benötigst, bist du hier genau richtig! Scrolle herunter für mehr Informationen (Mausrad oder Scrollbar auf der rechten Seite). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/microcontrollercase1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/microcontrollercase1.md index 3cf502fa1d..0ce27dfccd 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/microcontrollercase1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/microcontrollercase1.md @@ -1,6 +1,6 @@ # Mikrocontroller-Gehäuse -![So niedlich!](oredict:oc:microcontrollerCase1) +![So niedlich!](oredict:opencomputers:microcontrollerCase1) Das Mikrocontroller-Gehäuse ist das grundlegende Teil zum Bau von [Mikrocontrollern](../block/microcontroller.md) in der [Elektronik-Werkbank](../block/assembler.md). Mikrocontroller sind sehr primitive [Computer](../general/computer.md). Sie können nur eine bestimmte Anzahl an Komponenten enthalten und sind für sehr spezifische Anwendungsfälle gedacht, wie das Reagieren auf Redstonesignale, oder das Verarbeiten von Netzwerknachrichten. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/navigationupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/navigationupgrade.md index f10a49b737..c0e3fe53e4 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/navigationupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/navigationupgrade.md @@ -1,6 +1,6 @@ # Navigations-Upgrade -![Ich habe mich verirrt. Schon. Wieder.](oredict:oc:navigationUpgrade) +![Ich habe mich verirrt. Schon. Wieder.](oredict:opencomputers:navigationUpgrade) Das Navigation-Upgrade stellt Informationen über den Standort und zur Orientierung bereit. Die Koordinaten, die das Upgrade zur Verfügung stellt sind relativ zum Zentrum der Karte, die zum Anfertigen des Upgrades verwendet wird. Die Reichweite des Upgrades basiert auf die Größe dieser Karte. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/numpad.md b/src/main/resources/assets/opencomputers/doc/de_de/item/numpad.md index 013e2ad12d..bc42a57348 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/numpad.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/numpad.md @@ -1,5 +1,5 @@ # Ziffernblock -![Prüfe es auf Fingerabdrücke.](oredict:oc:materialNumPad) +![Prüfe es auf Fingerabdrücke.](oredict:opencomputers:materialNumPad) Der Ziffernblock ist Teil einer jeden [Tastatur](../block/keyboard.md). Es erlaubt es, Zahlen einzugeben. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/pistonupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/pistonupgrade.md index 7f1861216e..d3bf89146d 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/pistonupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/pistonupgrade.md @@ -1,6 +1,6 @@ # Kolben-Upgrade -![Push it.](oredict:oc:pistonUpgrade) +![Push it.](oredict:opencomputers:pistonUpgrade) Das Kolben-Upgrade erlaubt es einigen Geräten, ähnlich wie Vanilla-Kolben zu arbeiten. Wenn es installiert ist, wird eine Komponente mit einer einzigen Methode verfügbar, der `push()`-Methode. Wenn sie aufgerufen wird, das Gerät versuchen, den Block in der Richtung zu verschieben, in die der Block zeigt. Bei [Robotern](../block/robot.md) und [Mikrocontrollern](../block/microcontroller.md) ist das die Vorderseite, bei [Tablets](tablets.md) ist das der Block auf den der Spieler schaut. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/printedcircuitboard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/printedcircuitboard.md index 37d78454a2..9dd7a54d56 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/printedcircuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/printedcircuitboard.md @@ -1,5 +1,5 @@ # Gedruckte Leiterplatte (PCB) -![AKA PCB](oredict:oc:materialCircuitBoardPrinted) +![AKA PCB](oredict:opencomputers:materialCircuitBoardPrinted) Die bedruckte Leiterplatte ist, neben dem [Transistor](transistor.md) eine der grundlegendsten Werkstoffe in OpenComputers. Es wird als Basis für viele Komponenten verwendet, wie [Karten](card.md) und eine große Anzahl an [Blöcken](../block/index.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/ram1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/ram1.md index e5e05ab231..5786345dea 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/ram1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/ram1.md @@ -1,6 +1,6 @@ # Speicher (RAM) -![Do you remember, dancing in September~](oredict:oc:ram1) +![Do you remember, dancing in September~](oredict:opencomputers:ram1) Der Random Access Memory ist, wie die [CPU](cpu1.md) ein grundlegendes Teil in allen [Computern](../general/computer.md). Je nach der Architektur der CPU hat der RAM einen großen Effekt darauf, was ein Computer kann und was er nicht kann. In der standardmäßigen LUA-Architektur kontrolliert es die tatsächliche Menge von Memory die LUA-Scripts verwenden können. Um größere und speicherintensivere Programme zu schreiben wird mehr Speicher verwendet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/rawcircuitboard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/rawcircuitboard.md index ebdb1da2e1..017b799ff5 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/rawcircuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/rawcircuitboard.md @@ -1,5 +1,5 @@ # Leiterplattenrohling -![Nicht Sushi.](oredict:oc:materialCircuitBoardRaw) +![Nicht Sushi.](oredict:opencomputers:materialCircuitBoardRaw) Zwischenprodukt beim Fertigen. Wird verwendet, um [Leiterplatten](circuitBoard.md) (oder [gedruckte Leiterplatten](printedCircuitBoard.md), je nach dem welches Recipe-Set verwendet wird) zu fertigen. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/redstonecard1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/redstonecard1.md index b994efbbe0..b0a5bdd9a1 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/redstonecard1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/redstonecard1.md @@ -1,6 +1,6 @@ # Redstonekarte -![Sieht rot.](oredict:oc:redstoneCard1) +![Sieht rot.](oredict:opencomputers:redstoneCard1) Die Redstonekarte ermöglicht [Computern](../general/computer.md) das Lesen und Senden von analogen Redstonesignalen zu benachbarten Blöcken. Wenn sich ein eingehendes Signal ändert wird dies dem Computer gemeldet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/server1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/server1.md index 26245e30fe..a1153aa485 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/server1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/server1.md @@ -1,6 +1,6 @@ # Server -![Serviert alles korrekt.](oredict:oc:server1) +![Serviert alles korrekt.](oredict:opencomputers:server1) Server sind eine Form von hochstufigen [Computern](../general/computer.md). Sie können mit Rechtsklick konfiguriert werden, wenn sie in der Hand liegen. Nach dem Einsetzen von [CPU](cpu1.md), [RAM](ram1.md) und Erweiterungskarten muss der Server in einem [Serverschrank](../block/serverRack.md) platziert werden. Für Informationen siehe [Eintrag des Serverschranks](../block/serverRack.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/signupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/signupgrade.md index 95101e687f..7281ef6d38 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/signupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/signupgrade.md @@ -1,5 +1,5 @@ # Schilder-I/O -![Sagt mehr als tausend Worte.](oredict:oc:signUpgrade) +![Sagt mehr als tausend Worte.](oredict:opencomputers:signUpgrade) Dieses Upgrade ermöglicht es Geräten mit Schildern in der Welt zu interagieren. Es erlaubt das Lesen von Schildern sowie das Ändern des Schildes (wenn möglich). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/solargeneratorupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/solargeneratorupgrade.md index 49fd11ae08..7339361206 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/solargeneratorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/solargeneratorupgrade.md @@ -1,6 +1,6 @@ # Solargenerator-Upgrade -![Is walking on sunshine.](oredict:oc:solarGeneratorUpgrade) +![Is walking on sunshine.](oredict:opencomputers:solarGeneratorUpgrade) Das Solargenerator-Upgrade kann in Geräten wie [Robotern](../block/robot.md), [Drohnen](drone.md) und [Tablets](tablet.md) dazu verwendet werden, passiv Energie zu generieren. Es funktioniert nur, wenn das Gerät direktem Sonnenlicht ausgesetzt ist und keine Wetterereignisse stören. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/tabletcase1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/tabletcase1.md index 5c82c1b68d..4772f57fb2 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/tabletcase1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/tabletcase1.md @@ -1,6 +1,6 @@ # Tabletgehäuse -![Verbiegt sich nicht.](oredict:oc:tabletCase1) +![Verbiegt sich nicht.](oredict:opencomputers:tabletCase1) Das Tabletgehäuse ist das grundlegende Teil um [Tablets](tablet.md) in der [Elektronik-Werkbank](../block/assembler.md) zu bauen. Tablets sind sehr kompakte, tragbare [Computer](../general/computer.md). Sie können eine kleine Nummer von ausgewählten Upgrades halten und nicht mit der Welt interagieren (wie [Computergehäuse](../block/case1.md) mit [Netzwerkkarten](lanCard.md) oder [Redstonekarten](redstoneCard1.md) es können). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/tankcontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/tankcontrollerupgrade.md index f8bb5a9652..71dea2b6a8 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/tankcontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/tankcontrollerupgrade.md @@ -1,6 +1,6 @@ # Tankcontroller -![Flüssigrouting.](oredict:oc:tankControllerUpgrade) +![Flüssigrouting.](oredict:opencomputers:tankControllerUpgrade) Das Tankcontroller-Upgrade ist das [Inventarcontroller-Upgrade](inventoryControllerUpgrade.md) für Tanks. Es können detaillierte Informationen über Tanks abgefragt werden. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/tankupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/tankupgrade.md index 94dc204a3e..4c9fda0e25 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/tankupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/tankupgrade.md @@ -1,5 +1,5 @@ # Tank Upgrade -![Suck it.](oredict:oc:tankUpgrade) +![Suck it.](oredict:opencomputers:tankUpgrade) Das Tank-Upgrade ermöglicht es Geräten, Flüssigkeiten zu halten. Jeder Tank kann nur eine einzige Art von Flüssigkeit halten und stellt ein Volumen von 16 Eimern (16.000mB) zur Verfügung. [Roboter](../block/robot.md) und [Drohnen] können Flüssigkeiten aus der Welt aufnehmen und die Flüssigkeit dann in Tanks oder, wenn die Flüssigkeit das ermöglicht, wieder in der Welt platzieren. Es gibt kein Maximum an installierten Tanks in einem Gerät. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/terminal.md b/src/main/resources/assets/opencomputers/doc/de_de/item/terminal.md index 71f0712c33..407e5f1eee 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/terminal.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/terminal.md @@ -1,6 +1,6 @@ # Fernbedienungsterminal -![Fernzugriff.](oredict:oc:terminal) +![Fernzugriff.](oredict:opencomputers:terminal) Das Fernbedienungsterminal kann verwendet werden um [Server](server1.md) fernzusteuern. Um es zu verwenden reicht ein Rechtsklick (im Schleichen) auf einen Server in einem [Serverschrank](../block/serverRack.md). (Dabei muss auf den Server im Block gezeigt werden. Das Terminal wird dann auf den Server gebunden): diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/texturepicker.md b/src/main/resources/assets/opencomputers/doc/de_de/item/texturepicker.md index 881dbdf38b..aae7acfbc4 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/texturepicker.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/texturepicker.md @@ -1,6 +1,6 @@ # Texturleser -![Was soll das heißen, "das ist nur ein reskin"?](oredict:oc:texturePicker) +![Was soll das heißen, "das ist nur ein reskin"?](oredict:opencomputers:texturePicker) Der Texturleser ist sehr nützlich beim Anfertigen von Modellen für den [3D-Drucker](../block/printer.md). Es erlaubt das Abfragen der Texturnamen, die bei Blöcken in der Welt verwendet werden. Dies kann Probleme bei bestimmten Blöcken mit speziellem Rendering verursachen (wie z.B. bei Kisten) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/tractorbeamupgrade.md b/src/main/resources/assets/opencomputers/doc/de_de/item/tractorbeamupgrade.md index 975e72b638..c68b312e46 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/tractorbeamupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/tractorbeamupgrade.md @@ -1,5 +1,5 @@ # Traktorstrahl-Upgrade -![Beam me up.](oredict:oc:tractorBeamUpgrade) +![Beam me up.](oredict:opencomputers:tractorBeamUpgrade) Das Traktorstrahl-Upgrade erlaubt es Geräten Items in einem Drei-Block-Radius um sie herum aufzusammeln. Dies kann besonders nützlich sein, wenn [Roboter](../block/robot.md) in Baum- oder anderen Farmen eingesetzt werden, oder wenn sie verschiedene Werkzeuge um Abbau von unterschiedlichen Blöcken einsetzen. Jeder Einsatz saugt einen einzelnen Itemstack auf und verbraucht Energie. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/transistor.md b/src/main/resources/assets/opencomputers/doc/de_de/item/transistor.md index 93ca0c966e..636f4b3f98 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/transistor.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/transistor.md @@ -1,5 +1,5 @@ # Transistor -![Ich denke ich habe alle meine Referenzen aufgebraucht.](oredict:oc:materialTransistor) +![Ich denke ich habe alle meine Referenzen aufgebraucht.](oredict:opencomputers:materialTransistor) Der Transistor ist einer der grundlegendsten Crafting-Materialien in OpenComputers. Es wird hauptsächlich für [Microchips](chip1.md) und andere elektronische Kleinigkeiten verwendet. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/upgradecontainer1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/upgradecontainer1.md index 21abefdc7d..58ca6bb859 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/upgradecontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/upgradecontainer1.md @@ -1,5 +1,5 @@ # Upgrade-Container -![Kann Upgrade haben.](oredict:oc:upgradeContainer1) +![Kann Upgrade haben.](oredict:opencomputers:upgradeContainer1) Der Upgrade-Container ist ein Container-Upgrade für [Roboter](../block/robot.md). Es stellt einen Slot im Roboter zur Verfügung, in dem normale Upgrades platziert werden können. Die höchste Stufe das ein Upgrade haben darf gleicht der Stufe des Containers. Im Gegensatz zu normalen Upgrades ist die Komplexität des Containers doppelt so groß wie die Stufe. Für mehr Informationen über Komplexität siehe [Roboter](../block/robot.md) und [Elektronik-Werkbank](../block/assembler.md). diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/wlanCard1.md b/src/main/resources/assets/opencomputers/doc/de_de/item/wlanCard1.md index ca07529a34..4f64ad72d2 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/wlanCard1.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/wlanCard1.md @@ -1,6 +1,6 @@ # Drahtlosnetzwerkkarte -![Kann Krebs verursachen. Oder nicht.](oredict:oc:wlanCard2) +![Kann Krebs verursachen. Oder nicht.](oredict:opencomputers:wlanCard2) Die kabellose Netzwerkkarte ist eine aufgewertete [Netzwerkkarte](lanCard.md) die, zusätzlich zu verkabelten Nachrichten, zudem das Senden von Nachrichten über kabellose Netzwerke ermöglicht. Die Signalstärke kontrolliert die Distanz über die Nachrichten versendet werden können. Die Signalstärke gleicht der Distanz in Blöcken. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/worldsensorcard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/worldsensorcard.md index bf63e2330b..30768e009a 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/worldsensorcard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/worldsensorcard.md @@ -1,5 +1,5 @@ # Weltsensorkarte -![Um dort hin zu gehen...](oredict:oc:worldSensorCard) +![Um dort hin zu gehen...](oredict:opencomputers:worldSensorCard) Die Weltsensorkarte ermöglicht es Informationen über die Atmosphäre und Gravitation von verschiedenen Planeten in GalactiCraft zu lesen. Das kann für [Roboter](../block/robot.md) oder [Drohnen](drone.md) im Weltraum nützlich sein. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/wrench.md b/src/main/resources/assets/opencomputers/doc/de_de/item/wrench.md index 6312b9a64d..6b7e995050 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/wrench.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/wrench.md @@ -1,5 +1,5 @@ # Schraubenziehschlüssel -![In der Schweiz gemacht.](oredict:oc:wrench) +![In der Schweiz gemacht.](oredict:opencomputers:wrench) Wie fast jede andere Technologie-Modifikation hat OpenComputer seine eigene Version eines Schraubenschlüssels. In diesem Fall ist es ein Schraubendreher-Schraubenschlüssel-Hybdrid, das unglaublich merkwürdig aussieht. Damit können die meisten Blöcke gedreht werden und ist mit den meisten Mods kompatibel. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/accesspoint.md b/src/main/resources/assets/opencomputers/doc/en_us/block/accesspoint.md index 78bacc26c5..f6e5c1976a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/accesspoint.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/accesspoint.md @@ -1,6 +1,6 @@ # Access Point -![AAA](oredict:oc:accessPoint) +![AAA](oredict:opencomputers:accessPoint) *This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/adapter.md b/src/main/resources/assets/opencomputers/doc/en_us/block/adapter.md index 4e6bb63710..6e7b6eabbe 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/adapter.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/adapter.md @@ -1,6 +1,6 @@ # Adapter -![Now with 100% more everything.](oredict:oc:adapter) +![Now with 100% more everything.](oredict:opencomputers:adapter) The adapter allows [computers](../general/computer.md) to interact with blocks from vanilla Minecraft or other mods. Supported blocks adjacent to the adapter will show up as components in [computers](../general/computer.md) connected to the adapter. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/assembler.md b/src/main/resources/assets/opencomputers/doc/en_us/block/assembler.md index cb25ceb76e..a6785ac6c0 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/assembler.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/assembler.md @@ -1,6 +1,6 @@ # Assembler -![Harder, better, faster, stronger.](oredict:oc:assembler) +![Harder, better, faster, stronger.](oredict:opencomputers:assembler) The assembler is an advanced workstation that can be used to build more complex electronic devices, such as [robots](robot.md), [drones](../item/drone.md) and [tablets](../item/tablet.md). They require a large amount of energy to assemble devices, so it is recommended to power them sufficiently with a [capacitor bank](capacitor.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/cable.md b/src/main/resources/assets/opencomputers/doc/en_us/block/cable.md index 8b4c7028ec..8e81a38fb0 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/cable.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/cable.md @@ -1,6 +1,6 @@ # Cable -![Salad.](oredict:oc:cable) +![Salad.](oredict:opencomputers:cable) The cable serves as a way of connecting [computers](../general/computer.md) and machines that are far apart. If you have a compact build where all components touch each other (directly or indirectly, most blocks also behave the same way as cables) you will usually not need cables. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/capacitor.md b/src/main/resources/assets/opencomputers/doc/en_us/block/capacitor.md index 5714f14839..73ddb08746 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/capacitor.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/capacitor.md @@ -1,6 +1,6 @@ # Capacitor -![It's over 9000.](oredict:oc:capacitor) +![It's over 9000.](oredict:opencomputers:capacitor) The capacitor stores energy to be used by the network, acting as an energy buffer when needed. Unlike conversion from other mod's energy to OpenComputers' internal energy type (using a [power converter](powerConverter.md) for example), transferring energy inside a single subnetwork is instantaneous. Having an internal energy buffer will be useful for tasks that require a lot of energy, such as [assembling](assembler.md) and/or [charging](charger.md) devices such as [robots](robot.md) or [drones](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/case1.md b/src/main/resources/assets/opencomputers/doc/en_us/block/case1.md index aa9c465721..0bbafd7df9 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/case1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/case1.md @@ -1,6 +1,6 @@ # Computer Case -![Just in case.](oredict:oc:case1) +![Just in case.](oredict:opencomputers:case1) Computer cases come in three different tiers, which limits the components that can be inserted into them. An additional tier also exists for use in creative mode only. Computer cases can also be placed inside an [assembler](assembler.md) to build [robots](robot.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/chameliumblock.md b/src/main/resources/assets/opencomputers/doc/en_us/block/chameliumblock.md index 99012ba02c..0521f4124b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/chameliumblock.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/chameliumblock.md @@ -1,6 +1,6 @@ # Block of Chamelium -![So... blank.](oredict:oc:chameliumBlock) +![So... blank.](oredict:opencomputers:chameliumBlock) A few pieces of [chamelium](../item/chamelium.md) can be combined to provide a monochrome block for decorative purposes. Chamelium blocks can also be dyed with any of the 16 Minecraft colors. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/charger.md b/src/main/resources/assets/opencomputers/doc/en_us/block/charger.md index e8d3db1b47..060d089893 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/charger.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/charger.md @@ -1,6 +1,6 @@ # Charger -![All right, let's do this.](oredict:oc:charger) +![All right, let's do this.](oredict:opencomputers:charger) The charger is used to charge devices such as [robots](robot.md), [drones](../item/drone.md) and [tablets](../item/tablet.md). A charger has to be activated by applying a redstone signal to it. The charge speed is based on the applied redstone signal's strength, with a strength of 15 meaning a charge speed of 100%. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/disassembler.md b/src/main/resources/assets/opencomputers/doc/en_us/block/disassembler.md index 770376b9d9..0f5863d38a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/disassembler.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/disassembler.md @@ -1,6 +1,6 @@ # Disassembler -![Build it, tear it down.](oredict:oc:disassembler) +![Build it, tear it down.](oredict:opencomputers:disassembler) The disassembler can be used to deconstruct most items in OpenComputers into their original parts. This is mostly useful to reclaim materials from old parts that are no longer useful, or to deconstruct devices that are either no longer needed or were incorrectly built (e.g. [robots](robot.md) without an [operating system](../general/openOS.md)). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/diskdrive.md b/src/main/resources/assets/opencomputers/doc/en_us/block/diskdrive.md index 5b844adca8..9c5ed502d0 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/diskdrive.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/diskdrive.md @@ -1,6 +1,6 @@ # Disk Drive -![Going round and round and...](oredict:oc:diskDrive) +![Going round and round and...](oredict:opencomputers:diskDrive) The disk drive can be used to read [floppy disks](../item/floppy.md) using a [computer](../general/computer.md) connected to the disk drive. This is useful to get started, since the lower tier [computer cases](case1.md) do not have a built-in floppy slot, and you'll need an operating system to boot up the [computer](../general/computer.md). An [OpenOS](../general/openOS.md) disk can be crafted using an empty [floppy disk](../item/floppy.md) and a [manual](../item/manual.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/geolyzer.md b/src/main/resources/assets/opencomputers/doc/en_us/block/geolyzer.md index e7c190a69f..b8c409caa1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/geolyzer.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/geolyzer.md @@ -1,6 +1,6 @@ # Geolyzer -![It rocks.](oredict:oc:geolyzer) +![It rocks.](oredict:opencomputers:geolyzer) The geolyzer can be used by [computers](../general/computer.md) to scan the terrain surrounding the geolyzer for the blocks' approximate hardness. This can be useful to generate maps of the area to display on [hologram projectors](hologram1.md) as well as to detect potentially valuable blocks (ores are usually harder than dirt and stone). Geolyzer scan results have a certain amount of noise added; in theory, multiple scans can be performed to determine a more accurate reading of a block's hardness level. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/hologram1.md b/src/main/resources/assets/opencomputers/doc/en_us/block/hologram1.md index 0e6d897692..8191ff017f 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/hologram1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/hologram1.md @@ -1,6 +1,6 @@ # Hologram Projector -![Is this the real life? Is this just fantasy?](oredict:oc:hologram1) +![Is this the real life? Is this just fantasy?](oredict:opencomputers:hologram1) The hologram projector is a volumetric display, i.e. it provides a three dimensional array of voxels that can be individually enabled or disabled by a connected [computer](../general/computer.md). The second tier projector, while having the same resolution as the tier 1 projector, supports displaying the individual voxels in three different user-definable colors. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/keyboard.md b/src/main/resources/assets/opencomputers/doc/en_us/block/keyboard.md index 485cc6942f..20327f920a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/keyboard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/keyboard.md @@ -1,6 +1,6 @@ # Keyboard -![QWERTY](oredict:oc:keyboard) +![QWERTY](oredict:opencomputers:keyboard) A keyboard is needed to type text on [screens](screen1.md), be they in the world or built into devices such as [robots](robot.md) or [tablets](../item/tablet.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/motionsensor.md b/src/main/resources/assets/opencomputers/doc/en_us/block/motionsensor.md index 37225ec63e..5f6e78a218 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/motionsensor.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/motionsensor.md @@ -1,6 +1,6 @@ # Motion Sensor -![Don't. Blink.](oredict:oc:motionSensor) +![Don't. Blink.](oredict:opencomputers:motionSensor) The motion sensor allows [computers](../general/computer.md) to detect movement of living entities. If an entity moves faster than a set threshold, a signal will be injected into [computers](../general/computer.md) connected to the motion sensor. The threshold can be configured using the component API that the motion sensor exposes to connected computers. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/netsplitter.md b/src/main/resources/assets/opencomputers/doc/en_us/block/netsplitter.md index f6c02a7526..9122b71c45 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/netsplitter.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/netsplitter.md @@ -1,6 +1,6 @@ # Net Splitter -![*.net *.split](oredict:oc:netSplitter) +![*.net *.split](oredict:opencomputers:netSplitter) The net splitter is a device that allows controlling connectivity between subnetworks. Unlike the [relay](relay.md) or [power converter](powerConverter.md) it directly connects adjacent subnetworks, i.e. components can be accessed. Each side's connectivity can be toggled using a wrench (e.g. the [scrench](../item/wrench.md)). When a redstone signal is applied to the net splitter, all sides' connectivity is inverted. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/powerconverter.md b/src/main/resources/assets/opencomputers/doc/en_us/block/powerconverter.md index 07728c8922..735937e8af 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/powerconverter.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/powerconverter.md @@ -1,5 +1,5 @@ # Power Converter -![One of us? One of us!](oredict:oc:powerConverter) +![One of us? One of us!](oredict:opencomputers:powerConverter) The power converter serves as the fastest way to convert energy from other mods' power systems to OpenComputers' internal energy. If you only run a simple computer, you probably won't need a converter. If you have a large capacitor bank that you only drain every now and then, you probably won't need one, either. However, if you wish to directly power an [assembler](assembler.md) or [charger](charger.md), it is usually a good idea to use a converter instead of directly connecting them to external power. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/powerdistributor.md b/src/main/resources/assets/opencomputers/doc/en_us/block/powerdistributor.md index 70ec54adf4..13a4586fc4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/powerdistributor.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/powerdistributor.md @@ -1,5 +1,5 @@ # Power Distributor -![Power to the masses.](oredict:oc:powerDistributor) +![Power to the masses.](oredict:opencomputers:powerDistributor) The power distributor distributes a shared power storage (such as a [capacitor](capacitor.md)), allowing several subnetworks to share their energy without components being exposed to computers in other networks. It operates by regularly "balancing" the energy in all subnetworks it is connected to, so that the *relative* amount of energy is the same in them. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/printer.md b/src/main/resources/assets/opencomputers/doc/en_us/block/printer.md index 3fdb09cae1..7e856fee6b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/printer.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/printer.md @@ -1,6 +1,6 @@ # 3D Printer -![2D printing is so yesteryear.](oredict:oc:printer) +![2D printing is so yesteryear.](oredict:opencomputers:printer) 3D printers allow you to print any block of any shape, with any type of texture. To get started with 3D printers, you will need to place down a 3D printer block next to a computer. This will give access to the `printer3d` component API, allowing you to set up and print [models](print.md) using the provided functions. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/rack.md b/src/main/resources/assets/opencomputers/doc/en_us/block/rack.md index 528344aba7..5c2c308f9a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/rack.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/rack.md @@ -1,6 +1,6 @@ # Rack -![Free housing.](oredict:oc:rack) +![Free housing.](oredict:opencomputers:rack) A rack houses up to four rack mountables such as [servers](../item/server1.md), [terminal servers](../item/terminalServer.md) and [mountable disk drives](../item/diskDriveMountable.md). Connectivity of mountables in a rack can be configured in detail via the GUI. In particular, if [servers](../item/server1.md) contain components that support it, such as [network cards](../item/lanCard.md), network-only connections can be defined for those components. These connections will serve purely for passing along network messages, components will not be visible through them. Such network-only connections are differentiated by the lines being slimmer than the "main" connections, which also allow component access. Each internal connection must be between a mountable / component in a mountable and a bus connected to a side of the rack. To connect multiple mountables to each other, connect them to the same bus. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/raid.md b/src/main/resources/assets/opencomputers/doc/en_us/block/raid.md index 87eda27777..3ccb0efe3b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/raid.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/raid.md @@ -1,6 +1,6 @@ # Raid -![40 man instance.](oredict:oc:raid) +![40 man instance.](oredict:opencomputers:raid) The raid block houses three [hard drives](../item/hdd1.md) which will be combined into a single file system. This combined file system has the size of the sum of the capacities of the individual [hard drives](../item/hdd1.md) and is available to all [computers](../general/computer.md) connected to the raid. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/redstone.md b/src/main/resources/assets/opencomputers/doc/en_us/block/redstone.md index 8978fc8842..df2b737c92 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/redstone.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/redstone.md @@ -1,6 +1,6 @@ # Redstone I/O -![Hi Red.](oredict:oc:redstone) +![Hi Red.](oredict:opencomputers:redstone) The redstone I/O block can be used to remotely read and emit redstone signals. It behaves like a hybrid of a tier 1 and 2 [redstone card](../item/redstoneCard1.md): it can read and emit simple analog as well as bundled signals, but cannot read or emit wireless redstone signals. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/relay.md b/src/main/resources/assets/opencomputers/doc/en_us/block/relay.md index 94fc589c28..ac8b585185 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/relay.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/relay.md @@ -1,6 +1,6 @@ # Relay -![Building bridges.](oredict:oc:relay) +![Building bridges.](oredict:opencomputers:relay) The relay can be used to allow different subnetworks to send network messages to each other, without exposing components to [computers](../general/computer.md) in other networks. Keeping components local is usually a good idea, to avoid [computers](../general/computer.md) using the wrong [screen](screen1.md) or to avoid component overflows to happen (causing [computers](../general/computer.md) to crash and refuse to boot up). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/screen1.md b/src/main/resources/assets/opencomputers/doc/en_us/block/screen1.md index 75710fb6b1..8781f5d52a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/screen1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/screen1.md @@ -1,6 +1,6 @@ # Screens -![See this?](oredict:oc:screen1) +![See this?](oredict:opencomputers:screen1) A screen is used in combination with a [graphics card](../item/graphicsCard1.md), to allow [computers](../general/computer.md) to display text. Different screen tiers have different capabilities, such as supporting different resolutions and color depths. Screens range from low-resolution, monochrome displays to high-resolution displays with up to 256 colors. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/switch.md b/src/main/resources/assets/opencomputers/doc/en_us/block/switch.md index cff609e1c3..08164952e3 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/switch.md @@ -1,6 +1,6 @@ # Switch -![Building bridges.](oredict:oc:switch) +![Building bridges.](oredict:opencomputers:switch) *This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/transposer.md b/src/main/resources/assets/opencomputers/doc/en_us/block/transposer.md index f666883bbe..840dfe7383 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/transposer.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/transposer.md @@ -1,6 +1,6 @@ # Transposer -![Such a poser.](oredict:oc:transposer) +![Such a poser.](oredict:opencomputers:transposer) The transposer bridges the gap between redstone controlled hoppers and [robots](robot.md), allowing [computer](../general/computer.md)-controlled transferral of items and fluids between adjacent blocks. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/en_us/block/waypoint.md index 00dab81a44..bb6893ea1d 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/block/waypoint.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/block/waypoint.md @@ -1,6 +1,6 @@ # Waypoint -!["This way!" - "No, that way!"](oredict:oc:waypoint) +!["This way!" - "No, that way!"](oredict:opencomputers:waypoint) The waypoint's use lies not in itself, but in how it can be used. [Navigation upgrades](../item/navigationUpgrade.md) can detect waypoints, so devices with a navigation upgrade can use these waypoints to navigate the world. This is particularly useful for writing easily reusable programs for devices such as [robots](robot.md) and [drones](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/general/example.md b/src/main/resources/assets/opencomputers/doc/en_us/general/example.md index 3bdda70bc6..c381cc82b5 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/general/example.md @@ -4,13 +4,13 @@ This is some test text for the subset of Markdown supported by the planned ingam ![This is a tooltip...](opencomputers:textures/gui/printer_ink.png) ![This is a tooltip...](opencomputers:/textures/gui/printer_material.png) *This* is *italic* text, ~~strikethrough~~ maybe abc-ter **some** text **in bold**. Is _this underlined_? Oh, no, _it's also italic!_ Well, this [a link](../index.md). -![This is rendered live.](oredict:oc:assembler) +![This is rendered live.](oredict:opencomputers:assembler) ## Smaller headline [also with *link* but this __one__ longer](../block/adapter.md) -![This is another tooltip.](item:OpenComputers:item@23) +![This is another tooltip.](item:opencomputers:item@23) some text directly above the item stack renderer to test spacing -![All the colors.](oredict:craftingPiston) +![All the colors.](oredict:forge/piston) some text directly below the item stack renderer to test spacing This is *italic @@ -42,9 +42,9 @@ asdasd ![oh my god, the recursion!](img/example.png) qweqwe And finally, [this is a link!](https://avatars1.githubusercontent.com/u/514903). -![broken item image](item:this is broken) -![broken item image](block:this is broken) -![broken item image](oredict:this is broken) +![broken item image](item:this_is_broken) +![broken item image](block:this_is_broken) +![broken item image](oredict:this_is_broken) wrap testing 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 @@ -53,17 +53,17 @@ wrap testing * 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 - `123456789012345678901234567890.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890` -this is a test for an![](oredict:oc:cpu1)an inline image kakakakalalsd 123 as +this is a test for an![](oredict:opencomputers:cpu1)an inline image kakakakalalsd 123 as -this is a test for an![](oredict:oc:cpu1) +this is a test for an![](oredict:opencomputers:cpu1) an image with a break after it this is a test for an -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) an image between two lines this is a test for an -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) an image between two blank lines diff --git a/src/main/resources/assets/opencomputers/doc/en_us/general/quickstart.md b/src/main/resources/assets/opencomputers/doc/en_us/general/quickstart.md index fe5d776758..42c6038ec1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/general/quickstart.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/general/quickstart.md @@ -6,7 +6,7 @@ Also know as "how to build your first computer". To get your first [computer](co First off, you will need a [computer case](../block/case1.md). This is the block which will contain all of the components, defining the behavior of the computer you are building. -![A tier two computer case.](oredict:oc:case2) +![A tier two computer case.](oredict:opencomputers:case2) For example, you will need to choose what tier of [graphics card](../item/graphicsCard1.md) you wish to use, if you need a [network card](../item/lanCard.md), a [redstone card](../item/redstoneCard1.md) or, if you're just playing around in creative mode, maybe even a [debug card](../item/debugCard.md). @@ -37,7 +37,7 @@ Now, if you used a tier 2 [computer case](../block/case2.md) as in the screensho It lives! Or should, anyway. If it doesn't something went wrong, and you'll want to investigate using the [analyzer](../item/analyzer.md). But assuming it's running now, you're pretty much done. The hardest part is over. All that's left is to make it take input and show some output. To allow the [computer](computer.md) to show some output, you'll want to grab a [screen](../block/screen1.md) and a [graphics card](../item/graphicsCard1.md). -![No, it's not a flatscreen.](oredict:oc:screen2) +![No, it's not a flatscreen.](oredict:opencomputers:screen2) Place the [screen](../block/screen1.md) adjacent to your [computer case](../block/case1.md), or, again, connect it using some [cable](../block/cable.md). Then place a [graphics card](../item/graphicsCard1.md) of your choice into the [computer case](../block/case1.md). You should now see a blinking cursor on the [screen](../block/screen1.md). Finally, place a [keyboard](../block/keyboard.md) either on the [screen](../block/screen1.md) itself, or in a way so that it faces the [screen](../block/screen1.md), to enable [keyboard](../block/keyboard.md) input. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/abstractbuscard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/abstractbuscard.md index 4263adfe2d..081e0788f0 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/abstractbuscard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/abstractbuscard.md @@ -1,5 +1,5 @@ # Abstract Bus Card -![More networking!](oredict:oc:abstractBusCard) +![More networking!](oredict:opencomputers:abstractBusCard) This card allows [computers](../general/computer.md), [servers](server1.md) and [robots](../block/robot.md) to interact with StargateTech2's abstract bus. When the card is installed, these blocks will connect to the abstract bus and a component becomes available to the machine that can be used to send messages across the abstract bus. Incoming abstract bus messages are converted to signals that are injected into the machine. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/acid.md b/src/main/resources/assets/opencomputers/doc/en_us/item/acid.md index a8020c0da1..d7d8ef0ba5 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/acid.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/acid.md @@ -1,6 +1,6 @@ # Acid -![Reflux?](oredict:oc:materialAcid) +![Reflux?](oredict:opencomputers:materialAcid) This tasty [citation needed] concoction can be consumed if you ever feel the need for some... fun. Or ruining your digestive tract. Or both. It can also serve as ingredient in other, more useful items. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/alu.md b/src/main/resources/assets/opencomputers/doc/en_us/item/alu.md index d429cb4045..3625a3e602 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/alu.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/alu.md @@ -1,5 +1,5 @@ # Arithmetic Logic Unit -![I can into logic!](oredict:oc:materialALU) +![I can into logic!](oredict:opencomputers:materialALU) Used for crafting components that perform calculations, such as [CPUs](cpu1.md) and [graphics cards](graphicsCard1.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/analyzer.md b/src/main/resources/assets/opencomputers/doc/en_us/item/analyzer.md index b185704af1..92f8e0ee30 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/analyzer.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/analyzer.md @@ -1,6 +1,6 @@ # Analyzer -![Must. Resist. Bad. Joke.](oredict:oc:analyzer) +![Must. Resist. Bad. Joke.](oredict:opencomputers:analyzer) The analyzer is a handy tool for reading information about OpenComputers-related blocks in the world. Simply (sneak-)activate a block to get some information printed to the chat box. This ranges from basic things like the address of components, to power levels in the subnetwork the block is in, and information on the error lead to a [computer](../general/computer.md) to crash, for example. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/angelupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/angelupgrade.md index 611146a39c..badae522c2 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/angelupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/angelupgrade.md @@ -1,5 +1,5 @@ # Angel Upgrade -![Hallelujah.](oredict:oc:angelUpgrade) +![Hallelujah.](oredict:opencomputers:angelUpgrade) This upgrade allows [robots](../block/robot.md) to place blocks in thin air, without a reference block. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/apu1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/apu1.md index 674851e7af..872e4a46ca 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/apu1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/apu1.md @@ -1,6 +1,6 @@ # APU -![Awesomest Probability Unifier.](oredict:oc:apu1) +![Awesomest Probability Unifier.](oredict:opencomputers:apu1) These parts are the merry marriage of a [CPU](cpu1.md) and [graphics card](graphicsCard1.md). Using one of these essentially gives you one more card slot to work with. Like a normal CPU, it defines the architecture of the [computer](../general/computer.md), and the number of components that can be connected to the [computer](../general/computer.md) before it stops working. In addition it also provides basic graphical capabilities. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/arrowkeys.md b/src/main/resources/assets/opencomputers/doc/en_us/item/arrowkeys.md index 8357b596c5..9d46464710 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/arrowkeys.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/arrowkeys.md @@ -1,5 +1,5 @@ # Arrow Keys -![Just be grateful it's not made from arrows.](oredict:oc:materialArrowKey) +![Just be grateful it's not made from arrows.](oredict:opencomputers:materialArrowKey) At the risk of repeating myself: it's a button sink. Because the button economy has seen some serious inflation in recent years. It is used as a component for building [keyboards](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/batteryupgrade1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/batteryupgrade1.md index d1c06b34c5..c7ea13cd9b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/batteryupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/batteryupgrade1.md @@ -1,5 +1,5 @@ # Battery Ugrade -![Made of Metal.](oredict:oc:batteryUpgrade1) +![Made of Metal.](oredict:opencomputers:batteryUpgrade1) This upgrade increases the internal energy buffer of devices, such as [robots](../block/robot.md) and [tablets](tablet.md), allowing them to operate longer without having to return to a [charger](../block/charger.md). Higher tier battery upgrades can store more energy. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/buttongroup.md b/src/main/resources/assets/opencomputers/doc/en_us/item/buttongroup.md index 373d7cdc3b..1080f7f8fa 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/buttongroup.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/buttongroup.md @@ -1,5 +1,5 @@ # Button Group -![Needs more buttons.](oredict:oc:materialButtonGroup) +![Needs more buttons.](oredict:opencomputers:materialButtonGroup) Because you *always* have too many buttons lying around somewhere. Don't lie. We all shift-click that button recipe time and again. Button groups are used to build [keyboards](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/card.md b/src/main/resources/assets/opencomputers/doc/en_us/item/card.md index 55c5021b7a..d8479bcd9d 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/card.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/card.md @@ -1,5 +1,5 @@ # Card -![Can't be read.](oredict:oc:materialCard) +![Can't be read.](oredict:opencomputers:materialCard) Common crafting material for card components in OpenComputers (such as [graphics cards](graphicsCard1.md), [network cards](lanCard.md), etc). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/cardcontainer1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/cardcontainer1.md index 64698c7df8..519e17b88e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/cardcontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/cardcontainer1.md @@ -1,5 +1,5 @@ # Card Container -![Can haz cards!](oredict:oc:cardContainer1) +![Can haz cards!](oredict:opencomputers:cardContainer1) The card container is a container upgrade for [robots](../block/robot.md) allowing cards to be inserted/removed to/from [robots](../block/robot.md) on-the-fly. The maximum tier of card that the slot can hold is equal to the tier of the container. Unlike normal upgrades, the complexity of containers is twice their tier. See [here](../block/robot.md) for details on robot complexity. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/chamelium.md b/src/main/resources/assets/opencomputers/doc/en_us/item/chamelium.md index 18fabca864..7c2eb60c68 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/chamelium.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/chamelium.md @@ -1,6 +1,6 @@ # Chamelium -![From Chameleon, in case you were wondering.](oredict:oc:chamelium) +![From Chameleon, in case you were wondering.](oredict:opencomputers:chamelium) Chamelium is a shape-shifting material that is used when creating [3D prints](../block/print.md) in a [3D printer](../block/printer.md). On its own, it is featureless and therefore very useful for creating simple, plain and monochrome areas. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/chip1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/chip1.md index 945b9a8d82..bb4d72cdf4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/chip1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/chip1.md @@ -1,5 +1,5 @@ # Microchips -![Not the edible ones.](oredict:oc:circuitChip1) +![Not the edible ones.](oredict:opencomputers:circuitChip1) Microchips are the bread and butter of electronic component crafting. They come in different tiers, for crafting different tiers of components. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/chunkloaderupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/chunkloaderupgrade.md index 7f7e8210bc..dbbafe00c6 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/chunkloaderupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/chunkloaderupgrade.md @@ -1,6 +1,6 @@ # Chunk Loader Upgrade -![Not going anywhere.](oredict:oc:chunkloaderUpgrade) +![Not going anywhere.](oredict:opencomputers:chunkloaderUpgrade) The chunk loader upgrade can be installed in devices (such as [robots](../block/robot.md) and [microcontrollers](../block/microcontroller.md)) to allow them to keep the chunk they are in - as well as the surrounding chunks - loaded. This consumes energy, however. The chunk loader can be turned on and off using the component API exposed to the device. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/circuitboard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/circuitboard.md index 28ad8ecf7f..e2f0493d60 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/circuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/circuitboard.md @@ -1,5 +1,5 @@ # Circuit Board -![Needs more gold.](oredict:oc:materialCircuitBoard) +![Needs more gold.](oredict:opencomputers:materialCircuitBoard) Intermediate crafting item made from [raw circuit boards](rawCircuitBoard.md) and used to make [printed circuit boards](printedCircuitBoard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/componentbus1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/componentbus1.md index b6d2990e30..622cf9079e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/componentbus1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/componentbus1.md @@ -1,6 +1,6 @@ # Component Bus -![I need mooooore.](oredict:oc:componentBus1) +![I need mooooore.](oredict:opencomputers:componentBus1) A component bus is a [server](server1.md)-specific upgrade that allows the [server](server1.md) to communicate with more components at the same time, without shutting down. Like with [CPUs](cpu1.md), higher tier buses provide higher component limits. Higher tier [servers](server1.md) are also capable of taking more component buses, allowing for communication with many more components. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/controlunit.md b/src/main/resources/assets/opencomputers/doc/en_us/item/controlunit.md index 8935aeb967..493a6eeee3 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/controlunit.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/controlunit.md @@ -1,5 +1,5 @@ # Control Unit -![With built-in cruise control.](oredict:oc:materialCU) +![With built-in cruise control.](oredict:opencomputers:materialCU) Higher tier crafting item used in more advanced circuitry, such as [CPUs](cpu1.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/cpu1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/cpu1.md index 7ff2673948..4fc4547181 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/cpu1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/cpu1.md @@ -1,6 +1,6 @@ # CPU -![Braaainz.](oredict:oc:cpu1) +![Braaainz.](oredict:opencomputers:cpu1) The central processing unit is a core part for each [computer](../general/computer.md) or [server](server1.md). It defines the architecture of the [computer](../general/computer.md), and the number of components that can be connected to the [computer](../general/computer.md) before it stops working. Higher tier CPUs also provide a higher per-tick direct call limit to the [computer](../general/computer.md) - in simpler terms: better CPUs run faster. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/craftingupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/craftingupgrade.md index 43320ee5ea..2d726ab3f4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/craftingupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/craftingupgrade.md @@ -1,5 +1,5 @@ # Crafting Upgrade -![Crafty.](oredict:oc:craftingUpgrade) +![Crafty.](oredict:opencomputers:craftingUpgrade) The crafting upgrade allows [robots](../block/robot.md) to craft shaped and shapeless recipes using items in their [inventory](../item/inventoryUpgrade.md). When crafting, the top-left 3x3 grid in the [robot](../block/robot.md)'s [inventory](../item/inventoryUpgrade.md) is used as the crafting grid. Items have to be arranged according to the recipe. Results will be placed back into the [robot](../block/robot.md)'s [inventory](../item/inventoryUpgrade.md). As with picking up items, the result will be placed into the selected slot; failing to do so will place the item in the next available empty slot. If no inventory space remains, the result will be dropped into the world. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/cuttingwire.md b/src/main/resources/assets/opencomputers/doc/en_us/item/cuttingwire.md index 8f532bc68e..12942c96e5 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/cuttingwire.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/cuttingwire.md @@ -1,5 +1,5 @@ # Cutting Wire -![Not a garrote. Honest.](oredict:oc:materialCuttingWire) +![Not a garrote. Honest.](oredict:opencomputers:materialCuttingWire) An item encountered when using hard-mode recipes, it is used for crafting [raw circuit boards](rawCircuitBoard.md). Very inefficiently. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/databaseupgrade1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/databaseupgrade1.md index ff4b4cfd5c..b1b1d7228c 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/databaseupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/databaseupgrade1.md @@ -1,6 +1,6 @@ # Database Upgrade -![Living in the database.](oredict:oc:databaseUpgrade1) +![Living in the database.](oredict:opencomputers:databaseUpgrade1) The database upgrade can be configured to store a list of item stack representations, which can then be used by other components. This is particularly useful for items that are differentiated purely based on their NBT data, which is not part of the item stack descriptor returned by callbacks. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/datacard1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/datacard1.md index de4e1798bd..6888647813 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/datacard1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/datacard1.md @@ -1,6 +1,6 @@ # Data Card -![Contrary to popular belief, it does not store data.](oredict:oc:dataCard1) +![Contrary to popular belief, it does not store data.](oredict:opencomputers:dataCard1) The data card is a utility card that provides a couple of algorithms which would be either hard to implement on an architecture, or run relatively slowly on them. It provides hashing functionalities, as well as basic inflate/deflate. Additionally it comes with an embedded file system that provides a number of programs using the functionality provided by the card, similar to the internet card. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/disk.md b/src/main/resources/assets/opencomputers/doc/en_us/item/disk.md index 55a6e9c13d..c99793fc9b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/disk.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/disk.md @@ -1,5 +1,5 @@ # Disc -![World. RIP Terry Pratchett.](oredict:oc:materialDisk) +![World. RIP Terry Pratchett.](oredict:opencomputers:materialDisk) Basic crafting component used in crafting storage media such as [floppies](floppy.md) and [hard drives](hdd1.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/diskdrivemountable.md b/src/main/resources/assets/opencomputers/doc/en_us/item/diskdrivemountable.md index eecb3e7c5f..d5cf0f3165 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/diskdrivemountable.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/diskdrivemountable.md @@ -1,5 +1,5 @@ # Mountable Disk Drive -![Snuggly](oredict:oc:diskDriveMountable) +![Snuggly](oredict:opencomputers:diskDriveMountable) This device is functionally equivalent to a regular [disk drive](../block/diskDrive.md), except that it is installed in a [rack](../block/rack.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/dronecase1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/dronecase1.md index 3cb50e9f7f..661e889059 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/dronecase1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/dronecase1.md @@ -1,6 +1,6 @@ # Drone Case -![Droning on.](oredict:oc:droneCase1) +![Droning on.](oredict:opencomputers:droneCase1) The drone case is used to build [drones](drone.md) in the [assembler](../block/assembler.md). [Drones](drone.md) are light-weight, fast and very mobile machines with limited functionality (fewer upgrade and component slots available). Unlike [robots](../block/robot.md) they cannot use tools, and can interact with the world only in a relatively limited manner. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/eeprom.md b/src/main/resources/assets/opencomputers/doc/en_us/item/eeprom.md index e4019afbbf..583dbdafd7 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/eeprom.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/eeprom.md @@ -1,6 +1,6 @@ # EEPROM -![Let's get this party started.](oredict:oc:eeprom) +![Let's get this party started.](oredict:opencomputers:eeprom) The EEPROM is what contains the code used to initialize a computer when it is being booted. This data is stored as a plain byte array, and may mean different things to different [CPU](cpu1.md) architectures. For example, for Lua it is usually a small script that searches for file systems with an init script, for other architectures it may be actual machine code. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/experienceupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/experienceupgrade.md index 5112fee915..6b9082770e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/experienceupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/experienceupgrade.md @@ -1,6 +1,6 @@ # Experience Upgrade -![This makes no sense, but it's cool.](oredict:oc:experienceUpgrade) +![This makes no sense, but it's cool.](oredict:opencomputers:experienceUpgrade) The experience upgrade is a very special upgrade, as it allows [robots](../block/robot.md) and [drones](drone.md) to collect experience by performing various actions, such as digging up ores and killing entities. A single upgrade can store up to 30 levels, providing passive bonuses with each level, including faster harvest speeds and increased energy buffer capacity. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/floppy.md b/src/main/resources/assets/opencomputers/doc/en_us/item/floppy.md index ae60719558..d41d980e63 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/floppy.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/floppy.md @@ -1,5 +1,5 @@ # Floppy Disk -![Many inches.](oredict:oc:floppy) +![Many inches.](oredict:opencomputers:floppy) The floppy disk is the cheapest and smallest type of storage medium in OpenComputers. It is a handy early-game way of storing data and transferring it between [computers](../general/computer.md) and [robots](../block/robot.md). You may also find floppy disks with useful programs on them in dungeon chests (OpenPrograms Package Manager is a good example, allowing easy install of programs from a central GitHub repository). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/generatorupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/generatorupgrade.md index 27c5dca641..0d402d8a8e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/generatorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/generatorupgrade.md @@ -1,6 +1,6 @@ # Generator Upgrade -![Generator X.](oredict:oc:generatorUpgrade) +![Generator X.](oredict:opencomputers:generatorUpgrade) The generator upgrade allows devices to re-fuel on the go. Currently it only supports solid fuels, such as coal. It has an internal inventory that can store one item stack of fuel. Surplus fuel can be removed from the generator using the appropriate component API method. When removing a generator upgrade from a [robot](../block/robot.md), its contents will be dropped into the world. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/graphicscard1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/graphicscard1.md index e72fc5c6c0..b9174280a3 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/graphicscard1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/graphicscard1.md @@ -1,6 +1,6 @@ # Graphics Card -![Fancy images.](oredict:oc:graphicsCard1) +![Fancy images.](oredict:opencomputers:graphicsCard1) The graphics card is an essential part for most [computers](../general/computer.md) and allows the [computer](../general/computer.md) to display text on a connected [screen](../block/screen1.md). Graphics cards come in several tiers, and like [screens](../block/screen1.md), support different resolutions and color depths. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/hdd1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/hdd1.md index c873304a48..3693e08691 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/hdd1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/hdd1.md @@ -1,5 +1,5 @@ # Hard Disk Drive -![Spaaaace.](oredict:oc:hdd1) +![Spaaaace.](oredict:opencomputers:hdd1) The hard disk drives are a higher tier storage medium in OpenComputers. All storage media perform at the same speed; higher tier storage provides more space. There are also some devices that can only use disk drives (although servers could use an external [disk drive](../block/diskDrive.md), for example). Hard drives can be placed inside a [raid](../block/raid.md), allowing multiple drives to share the same file system. Note that placing a hard drive in a [raid](../block/raid.md) wipes the drive of its contents. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/hoverboots.md b/src/main/resources/assets/opencomputers/doc/en_us/item/hoverboots.md index 8ff8a375d5..ff600be336 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/hoverboots.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/hoverboots.md @@ -1,6 +1,6 @@ # Hover Boots -![Step on it.](oredict:oc:hoverBoots) +![Step on it.](oredict:opencomputers:hoverBoots) If you can't be bothered to program [drones](drone.md), here's an alternative use for them: stepping stones! Or glorified inline skates. Something like that. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/hoverupgrade1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/hoverupgrade1.md index 5406e23dda..a5a9bfdb82 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/hoverupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/hoverupgrade1.md @@ -1,6 +1,6 @@ # Hover Upgrade -![Float like a feather.](oredict:oc:hoverUpgrade1) +![Float like a feather.](oredict:opencomputers:hoverUpgrade1) The hover upgrade allows [robots](../block/robot.md) to fly much higher above the ground than they normally could. Unlike [drones](drone.md), they are by default limited to a flight height of 8. This is usually not a big problem, because they can still move up or along walls. Their general movement rules can be summarized like this: - Robots may only move if the start or target position is valid (e.g. to allow building bridges). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/inkcartridge.md b/src/main/resources/assets/opencomputers/doc/en_us/item/inkcartridge.md index 14ae30e03c..574e3db117 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/inkcartridge.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/inkcartridge.md @@ -1,5 +1,5 @@ # Ink Cartridge -![The colors of the rainbow.](oredict:oc:inkCartridge) +![The colors of the rainbow.](oredict:opencomputers:inkCartridge) Ink cartridges come in handy when refilling the color buffer of [3D printers](../block/printer.md). While it is also possible to refill them using dyes directly, this is messy and very inefficient. It is strongly recommended to invest into a real OC Ink Cartridge (TM), today! (Disclaimer: they may or may cost more than the printer itself). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/internetcard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/internetcard.md index fa80e6d7f9..33190a8a40 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/internetcard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/internetcard.md @@ -1,6 +1,6 @@ # Internet Card -![Cat videos in 3, 2, ...](oredict:oc:internetCard) +![Cat videos in 3, 2, ...](oredict:opencomputers:internetCard) The internet card grants [computers](../general/computer.md) access to the internet. It provides ways to perform simple HTTP requests, as well as to open plain TCP client sockets that can be read and written to. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/interweb.md b/src/main/resources/assets/opencomputers/doc/en_us/item/interweb.md index 91520ca79b..8d482a060f 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/interweb.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/interweb.md @@ -1,5 +1,5 @@ # Interweb -![A website is a place where there's cobweb.](oredict:oc:materialInterweb) +![A website is a place where there's cobweb.](oredict:opencomputers:materialInterweb) The interweb is a basic component for all things related to long distance communication. It uses the strange mechanics of all things End to allow something along the lines of quantum communication. Used most notably in [internet cards](internetCard.md) and [linked cards](linkedCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/inventorycontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/inventorycontrollerupgrade.md index 8cad6000e1..8cddea326d 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/inventorycontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/inventorycontrollerupgrade.md @@ -1,6 +1,6 @@ # Inventory Controller -![I'm in control.](oredict:oc:inventoryControllerUpgrade) +![I'm in control.](oredict:opencomputers:inventoryControllerUpgrade) The inventory controller upgrade provides extended inventory interaction to [robots](../block/robot.md) and [drones](drone.md). It allows the device to explicitly target slots in external inventories when dropping or sucking items. It also allows devices to read detailed information about item stacks. Lastly it provides [robots](../block/robot.md) with a means to change their equipped tool without external help. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/inventoryupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/inventoryupgrade.md index a451f184c7..a7d18f7dd8 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/inventoryupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/inventoryupgrade.md @@ -1,6 +1,6 @@ # Inventory Upgrade -![Where does it keep all this stuff...](oredict:oc:inventoryUpgrade) +![Where does it keep all this stuff...](oredict:opencomputers:inventoryUpgrade) The inventory upgrade provides inventory slots to [robots](../block/robot.md) and [drones](drone.md). For each inventory upgrade a [robot](../block/robot.md) will gain an addition 16 inventory slots, up to a maximum of 64 slots in total; a [drone](drone.md) will gain 4 slots per upgrade, up to a total of 8 slots. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/lancard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/lancard.md index fc730e0359..d14fa8039e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/lancard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/lancard.md @@ -1,5 +1,5 @@ # Network Card -![Enter the network.](oredict:oc:lanCard) +![Enter the network.](oredict:opencomputers:lanCard) The network card allows [computers](../general/computer.md) to send and receive network messages. Messages (or packets) can be broadcasted to all receiving nodes in a subnetwork, or sent to a specific node with a specified address. [Relays](../block/relay.md) can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more [relays](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/leashupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/leashupgrade.md index 0f2914abc6..8f745a7cbe 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/leashupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/leashupgrade.md @@ -1,5 +1,5 @@ # Leash Upgrade -![-redacted- ~ Vexatos 2015](oredict:oc:leashUpgrade) +![-redacted- ~ Vexatos 2015](oredict:opencomputers:leashUpgrade) The Leash upgrade allows putting animals on a leash, bound to the entity that hosts the device the component is used by, for example [drones](drone.md). Using this upgrade, multiple animals can be leashed at the same time, which makes this quite useful for moving herds. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/linkedcard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/linkedcard.md index ec3dc820c7..1ea92aab6c 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/linkedcard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/linkedcard.md @@ -1,5 +1,5 @@ # Linked Card -![I feel we some kind of connection.](oredict:oc:linkedCard) +![I feel we some kind of connection.](oredict:opencomputers:linkedCard) The linked card is a specialized but advanced version of a [network card](lanCard.md). It can only operate in pairs, providing a point-to-point communication between the paired cards. Linked cards can communicate over large (unlimited) distances and across dimensions. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/manual.md b/src/main/resources/assets/opencomputers/doc/en_us/item/manual.md index b8b7323c22..22aa2f3584 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/manual.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/manual.md @@ -1,6 +1,6 @@ # Manual -![A good read.](oredict:oc:manual) +![A good read.](oredict:opencomputers:manual) The thing you're reading right now! The manual contains a wealth of information about OpenComputers (and possibly more). If you need information on an item or block in the mod, look no further! Scroll down to learn how to use (mouse wheel or the scroll bar to the right). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/mfu.md b/src/main/resources/assets/opencomputers/doc/en_us/item/mfu.md index e288f6653b..6b03de96b7 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/mfu.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/mfu.md @@ -1,6 +1,6 @@ # MFU -![You will never know the true meaning of this acronym.](oredict:oc:mfu) +![You will never know the true meaning of this acronym.](oredict:opencomputers:mfu) This upgrade acts as a remote [adapter](../block/adapter.md). Click while sneaking onto any side of any block to bind it to a specific position. Then, place it into an adapter nearby (the range is very limited) and it will act as if the adapter was placed right next to the specific side you bound it to! diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/microcontrollercase1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/microcontrollercase1.md index 92045902be..f029e76db4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/microcontrollercase1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/microcontrollercase1.md @@ -1,6 +1,6 @@ # Microcontroller Case -![It's so cute.](oredict:oc:microcontrollerCase1) +![It's so cute.](oredict:opencomputers:microcontrollerCase1) The Microcontroller case is the base part when building [microcontrollers](../block/microcontroller.md) in the [assembler](../block/assembler.md). [Microcontrollers](../block/microcontroller.md) are very primitive [computers](../general/computer.md). They may only contain a very limited number of components, and are intended to be used in very specific use-cases, such as transforming or reacting to redstone signals, or processing network messages. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/nanomachines.md b/src/main/resources/assets/opencomputers/doc/en_us/item/nanomachines.md index aa1f48ce76..4130f339ab 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/nanomachines.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/nanomachines.md @@ -1,6 +1,6 @@ # Nanomachines -![Nanomachines, son.](oredict:oc:nanomachines) +![Nanomachines, son.](oredict:opencomputers:nanomachines) These little guys interface with your nervous system to make you harder, better, faster, stronger, or kill you. Sometimes both at the same time! Put simply, nanomachines provide a power driven system for applying buffs (and debuffs) to the player they reside in. To "install" nanomachines, eat them! diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/navigationupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/navigationupgrade.md index 4ce1d4ef5d..63f3ef0636 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/navigationupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/navigationupgrade.md @@ -1,6 +1,6 @@ # Navigation Upgrade -![I'm lost. Again.](oredict:oc:navigationUpgrade) +![I'm lost. Again.](oredict:opencomputers:navigationUpgrade) The navigation upgrade provides location and orientation information to devices it is installed in. The coordinates the upgrade provides are relative to the center of the map that was used to craft the upgrade, and the functional range is based on the size of that map. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/numpad.md b/src/main/resources/assets/opencomputers/doc/en_us/item/numpad.md index 05ff6ade73..4eaf74dd19 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/numpad.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/numpad.md @@ -1,5 +1,5 @@ # Numeric Keypad -![Check for fingerprints.](oredict:oc:materialNumPad) +![Check for fingerprints.](oredict:opencomputers:materialNumPad) The Numeric keypad is part of every [keyboard](../block/keyboard.md). It allows you to enter numbers. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/pistonupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/pistonupgrade.md index 85c62dd44b..c12ea3f1c3 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/pistonupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/pistonupgrade.md @@ -1,6 +1,6 @@ # Piston Upgrade -![Push it.](oredict:oc:pistonUpgrade) +![Push it.](oredict:opencomputers:pistonUpgrade) The piston upgrade allows some devices to act very much like a vanilla piston. When installed, a component with a single method, `push()`, becomes available. When called the device will then try to push the block in its forward facing direction. For [robots](../block/robot.md) and [microcontrollers](../block/microcontroller.md), this is the front face; for [tablets](tablet.md), it will use the player's facing direction. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/printedcircuitboard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/printedcircuitboard.md index fdd266165b..53eda41e34 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/printedcircuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/printedcircuitboard.md @@ -1,5 +1,5 @@ # Printed Circuit Board -![AKA PCB](oredict:oc:materialCircuitBoardPrinted) +![AKA PCB](oredict:opencomputers:materialCircuitBoardPrinted) The printed circuit board is, next to the [transistor](transistor.md), one of the most basic crafting materials in OpenComputers. It is used as a basis for many components, such as [cards](card.md) and a large number of [blocks](../block/index.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/ram1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/ram1.md index cd54adc4eb..9636b4ea1f 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/ram1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/ram1.md @@ -1,6 +1,6 @@ # Memory -![Do you remember, dancing in September~](oredict:oc:ram1) +![Do you remember, dancing in September~](oredict:opencomputers:ram1) Memory is, like the [CPU](cpu1.md), an essential part in all [computers](../general/computer.md). Depending on the [CPU](cpu1.md)'s architecture, the memory has a very essential effect on what a [computer](../general/computer.md) can and cannot do. For the standard Lua architecture, for example, it controls the actual amount of memory Lua scripts can use. This means that to run larger and more memory-intensive programs, you will need more memory. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/rawcircuitboard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/rawcircuitboard.md index ac00250eab..0366c08be5 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/rawcircuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/rawcircuitboard.md @@ -1,5 +1,5 @@ # Raw Circuit Board -![Not sushi.](oredict:oc:materialCircuitBoardRaw) +![Not sushi.](oredict:opencomputers:materialCircuitBoardRaw) Intermediary crafting material, used for crafting [circuit boards](circuitBoard.md) (or [printed circuit boards](printedCircuitBoard.md), depending on the recipe set being used). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/redstonecard1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/redstonecard1.md index 2110c6b84f..7b6067383f 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/redstonecard1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/redstonecard1.md @@ -1,6 +1,6 @@ # Redstone Card -![Seeing red.](oredict:oc:redstoneCard1) +![Seeing red.](oredict:opencomputers:redstoneCard1) The redstone card allows [computers](../general/computer.md) to read and emit analog redstone signal in adjacent blocks. When an incoming signal strength changes, a signal is injected into the [computer](../general/computer.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/server1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/server1.md index 00c3544753..be7aafb537 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/server1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/server1.md @@ -1,6 +1,6 @@ # Server -![Serves u right.](oredict:oc:server1) +![Serves u right.](oredict:opencomputers:server1) Servers are a form of higher tier [computer](../general/computer.md). They can be configured by holding them in the hand and using them - like opening a backpack or Ender pouch, for example. Servers can also be configured after having been installed into a [rack](../block/rack.md) by activating them (aiming at the corresponding position on the front face on the [rack](../block/rack.md)). To operate, the server has to be placed inside a [rack](../block/rack.md). For more information see the [rack](../block/rack.md) entry. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/signupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/signupgrade.md index 944bd24b64..7fdb9944b1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/signupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/signupgrade.md @@ -1,5 +1,5 @@ # Sign I/O -![I see the signs on the wall.](oredict:oc:signUpgrade) +![I see the signs on the wall.](oredict:opencomputers:signUpgrade) This upgrade allows devices to interact with signs in the world. It allows reading the current message on the sign as well as changing the message on the sign (if permitted. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/solargeneratorupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/solargeneratorupgrade.md index 6740d5e693..c382e71479 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/solargeneratorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/solargeneratorupgrade.md @@ -1,6 +1,6 @@ # Solar Generator -![Walking on the sun.](oredict:oc:solarGeneratorUpgrade) +![Walking on the sun.](oredict:opencomputers:solarGeneratorUpgrade) The solar generator upgrade can be installed in devices such as [robots](../block/robot.md), [drones](drone.md) and [tablets](tablet.md) to passively generate energy. It will only work while the device is exposed to direct sunlight; a device in an enclosed area or an area affected by weather will not generate energy. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/stickypistonupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/stickypistonupgrade.md index 318622ad3e..c1edfea4e7 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/stickypistonupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/stickypistonupgrade.md @@ -1,6 +1,6 @@ # Sticky Piston Upgrade -![Push it. Pull it.](oredict:oc:stickyPistonUpgrade) +![Push it. Pull it.](oredict:opencomputers:stickyPistonUpgrade) The sticky piston upgrade is an upgrade to the [Piston Upgrade](pistonupgrade.md) which allows some devices to act very much like a vanilla sticky piston. When installed, a piston component becomes available. Its methods are `push()` and `pull()`. When these are called the device will act like a vanilla sticky piston and try to push or pull the block in its forward facing direction. For [robots](../block/robot.md) and [microcontrollers](../block/microcontroller.md), this is the front face; for [tablets](tablet.md), it will use the player's facing direction. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tabletcase1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tabletcase1.md index 923c02aac4..3ee4513c20 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tabletcase1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tabletcase1.md @@ -1,6 +1,6 @@ # Tablet Case -![Doesn't bend.](oredict:oc:tabletCase1) +![Doesn't bend.](oredict:opencomputers:tabletCase1) The tablet case is the base part when building [tablets](tablet.md) in the [assembler](../block/assembler.md). [Tablets](tablet.md) are very compact and portable [computers](../general/computer.md). They can host a small number of select upgrades, but cannot interact with the world like [computer cases](../block/case1.md) can (using simple [network cards](lanCard.md) or [redstone cards](redstoneCard1.md) for example). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tankcontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tankcontrollerupgrade.md index 57bbda9b72..0de4182bb6 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tankcontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tankcontrollerupgrade.md @@ -1,6 +1,6 @@ # Tank Controller -![Fluid routing.](oredict:oc:tankControllerUpgrade) +![Fluid routing.](oredict:opencomputers:tankControllerUpgrade) The tank controller upgrade is to fluid tanks what the [inventory controller upgrade](inventoryControllerUpgrade.md) is to normal inventories. It allows devices to query more detailed information about tanks inside and next to them. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tankupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tankupgrade.md index 23745e61a0..f83b74e3c9 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tankupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tankupgrade.md @@ -1,5 +1,5 @@ # Tank Upgrade -![Suck it.](oredict:oc:tankUpgrade) +![Suck it.](oredict:opencomputers:tankUpgrade) The tank upgrade allows devices to store fluids. Each tank can only hold a single type of fluid, and provides a volume of 16 buckets (16000mB). [Robots](../block/robot.md) and [drones](drone.md) can drain liquids from the world and from other fluid tanks, and can fill the fluids back into fluid tanks, and when supported by the fluid, place them back into the world. There is no limit to the number of tanks that can be installed in a device. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/terminal.md b/src/main/resources/assets/opencomputers/doc/en_us/item/terminal.md index 8091c1dd18..f6b6247705 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/terminal.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/terminal.md @@ -1,6 +1,6 @@ # Remote Terminal -![Remote access.](oredict:oc:terminal) +![Remote access.](oredict:opencomputers:terminal) The remote terminal can be used to remotely control computers via [terminal servers](terminalServer.md). To use it, activate a [terminal server](terminalServer.md) that is installed in a [rack](../block/rack.md) (click on the [rack](../block/rack.md) block in the world, targeting the [terminal server](terminalServer.md) to bind the terminal to). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/terminalserver.md b/src/main/resources/assets/opencomputers/doc/en_us/item/terminalserver.md index 53cedf2e32..f009b00145 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/terminalserver.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/terminalserver.md @@ -1,5 +1,5 @@ # Terminal Server -![Remote Viewing](oredict:oc:terminalServer) +![Remote Viewing](oredict:opencomputers:terminalServer) Terminal servers provides a virtual [screen](../block/screen1.md) and [keyboard](../block/keyboard.md) which can be controlled via [remote terminals](terminal.md). See the [remote terminal](terminal.md) manual entry for more information. Terminal servers must be installed in a [rack](../block/rack.md) to function. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/texturepicker.md b/src/main/resources/assets/opencomputers/doc/en_us/item/texturepicker.md index 9a3e183fbd..1297d3d2e1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/texturepicker.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/texturepicker.md @@ -1,6 +1,6 @@ # Texture Picker -![What do you mean, it's just a re-skin?](oredict:oc:texturePicker) +![What do you mean, it's just a re-skin?](oredict:opencomputers:texturePicker) The texture picker is very useful when making models for your [3D printer](../block/printer.md). It allows getting the texture name of textures used by blocks in the world, simply by (sneak-)activating them with the tool. Disclaimer: for blocks that use special rendering, such as chests, this may not work. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tractorbeamupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tractorbeamupgrade.md index ea1535f2e8..98329e081f 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tractorbeamupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tractorbeamupgrade.md @@ -1,5 +1,5 @@ # Tractor Beam -![Beam me up.](oredict:oc:tractorBeamUpgrade) +![Beam me up.](oredict:opencomputers:tractorBeamUpgrade) The tractor beam upgrade allows devices to pick up items in a three block radius around them. This can be highly useful when using [robots](../block/robot.md) in tree or other farms, or when having them use tools that break multiple blocks around them (such as Tinker's Construct tools). Each operation will try to suck a single item stack in range and consume some energy. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tradingupgrade.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tradingupgrade.md index 8a599707cb..2671d4c439 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tradingupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tradingupgrade.md @@ -1,5 +1,5 @@ # Trading Upgrade -![Equivalent Exchange](oredict:oc:tradingUpgrade) +![Equivalent Exchange](oredict:opencomputers:tradingUpgrade) The trading upgrade enables automatic trading with merchants, such as villagers. It can be used in agents such as [robots](../block/robot.md) and [drones](drone.md) and provides capabilities to identify available trades from nearby merchants, retrieve information on individual trade offers, and finally for performing trades. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/transistor.md b/src/main/resources/assets/opencomputers/doc/en_us/item/transistor.md index edef8d4b9e..8d3e75f42b 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/transistor.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/transistor.md @@ -1,5 +1,5 @@ # Transistor -![I think I've used up all my references.](oredict:oc:materialTransistor) +![I think I've used up all my references.](oredict:opencomputers:materialTransistor) The transistor is one of the most basic crafting ingredients in OpenComputers. It is mainly used to craft [microchips](chip1.md) and other electronic goodies. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/upgradecontainer1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/upgradecontainer1.md index 2a704973ad..dd847a1916 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/upgradecontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/upgradecontainer1.md @@ -1,5 +1,5 @@ # Upgrade Container -![Can haz upgrade.](oredict:oc:upgradeContainer1) +![Can haz upgrade.](oredict:opencomputers:upgradeContainer1) The upgrade container is a container type upgrade for [robots](../block/robot.md), that provides a slot in the finished [robots](../block/robot.md) into which normal upgrades can be placed. The maximum tier of upgrade that slot can hold is equal to the tier of the container. Unlike normal upgrades, the complexity of containers is twice their tier. Refer to [robot](../block/robot.md) and [assembler](../block/assembler.md) documentation on complexity. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/wlancard1.md b/src/main/resources/assets/opencomputers/doc/en_us/item/wlancard1.md index c6b69aed85..669547f0db 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/wlancard1.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/wlancard1.md @@ -1,6 +1,6 @@ # WLAN Card -![May cause cancer. May not.](oredict:oc:wlanCard2) +![May cause cancer. May not.](oredict:opencomputers:wlanCard2) The wireless network card is an upgraded [network card](lanCard.md) that can send and receive wireless network messages. Tier two cards can also send and receive wired messages. The signal strength directly controls the distance up to which a sent message can be received, where the strength is equal to the distance in blocks. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/worldsensorcard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/worldsensorcard.md index 168d1e93d6..b101dd009a 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/worldsensorcard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/worldsensorcard.md @@ -1,5 +1,5 @@ # World Sensor Card -![To boldly go...](oredict:oc:worldSensorCard) +![To boldly go...](oredict:opencomputers:worldSensorCard) The world sensor card allows reading of atmospheric information as well as gravity on different planets added by Galacticraft. This can be useful for [robots](../block/robot.md) or [drones](drone.md) operating in space. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/wrench.md b/src/main/resources/assets/opencomputers/doc/en_us/item/wrench.md index b26d7922e5..696c1cf4bc 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/wrench.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/wrench.md @@ -1,5 +1,5 @@ # Scrench -![Made in Swiss.](oredict:oc:wrench) +![Made in Swiss.](oredict:opencomputers:wrench) Like pretty much any other technology-focused mod, OpenComputers has it's own version of a wrench tool. In this case, it is a hybrid of a screwdriver and a wrench, that looks like it's incredibly awkward to use. It can be used to rotate most blocks, and is also compatible with most other mods' blocks that can be interacted with by using wrench-like tools. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/accessPoint.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/accessPoint.md index cda879aa04..a53c9bb19c 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/accessPoint.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/accessPoint.md @@ -1,6 +1,6 @@ # Point d'accès -![AAA](oredict:oc:accessPoint) +![AAA](oredict:opencomputers:accessPoint) *Ce bloc est déprécié et sera retiré dans une version future.* Transformez les en [relai](relay.md) pour éviter de les perdre. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/adapter.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/adapter.md index e451559f6d..c3614096fb 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/adapter.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/adapter.md @@ -1,6 +1,6 @@ # Adaptateur -![Maintenant, avec 100% de plus de tout.](oredict:oc:adapter) +![Maintenant, avec 100% de plus de tout.](oredict:opencomputers:adapter) L'adaptateur permet aux [ordinateurs](../general/computer.md) d'interagir avec des blocs de Minecraft Vanilla ou d'autres mods. Les blocs supportés placés contre l'adaptateur s'afficheront en tant que composants sur les [ordinateurs](../general/computer.md) connectés à l'adaptateur. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/assembler.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/assembler.md index fe84927051..7662f7d703 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/assembler.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/assembler.md @@ -1,6 +1,6 @@ # Assembleur électronique -![Harder, better, faster, stronger.](oredict:oc:assembler) +![Harder, better, faster, stronger.](oredict:opencomputers:assembler) L'assembleur est une table avancée qui peut être utilisée pour construire des appareils électroniques plus complexes, comme les [robots](robot.md), les [drones](../item/drone.md) et les [tablettes](../item/tablet.md). Il nécessite une grande quantité d'énergie pour assembler des appareils, il est donc recommandé de lui fournir suffisament d'énergie avec un [capaciteur](capacitor.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/cable.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/cable.md index 573434e902..1753f9cd55 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/cable.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/cable.md @@ -1,6 +1,6 @@ # Câble -![Salade de câbles.](oredict:oc:cable) +![Salade de câbles.](oredict:opencomputers:cable) Le câble à connecter des [ordinateurs](../general/computer.md) et des machines éloignés les uns des autres. Si vous avez un système compact où tous les composants sont en contact (directement ou indirectement, la plupart des blocs du mod se comportent également de la même manière que les câbles), vous n'aurez généralement pas besoin de câbles. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/capacitor.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/capacitor.md index 5304cc37b0..1af8e18174 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/capacitor.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/capacitor.md @@ -1,6 +1,6 @@ # Capaciteur -![Au delà des 8000.](oredict:oc:capacitor) +![Au delà des 8000.](oredict:opencomputers:capacitor) Le capaciteur emmagasine de l'énergie qui sera utilisée par le réseau, en se comportant comme un tampon d'énergie au besoin. Contrairement à la conversion de l'énergie d'autres mods vers l'énergie interne à OpenComputers (en utilisant un [convertisseur énergétique](powerConverter.md) par exemple), le transfert d'énergie dans un sous-réseau est instantané. Posséder un tampon d'énergie interne sera utile pour des tâches qui nécessitent beaucoup d'énergie, comme l'[assemblage](assembler.md) et/ou la [charge](charger.md) d'appareils comme les [robots](robot.md) ou les [drones](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/case1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/case1.md index d76af8b20f..0221c16ade 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/case1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/case1.md @@ -1,6 +1,6 @@ # Boîtier d'ordinateur -![Ah qu'est-ce qu'on est serré...](oredict:oc:case1) +![Ah qu'est-ce qu'on est serré...](oredict:opencomputers:case1) Les boîtiers d'ordinateur existent en 3 niveaux différents, ce qui limite les composants qui peuvent y être insérés. Un niveau supplémentaire existe aussi, seulement pour le mode créatif. Les boîtiers d'ordinateur peuvent également être placés dans un [assembleur électronique](assembler.md) pour construire des [robots](robot.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/chameliumBlock.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/chameliumBlock.md index e437c066eb..73bf1ed82b 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/chameliumBlock.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/chameliumBlock.md @@ -1,6 +1,6 @@ # Bloc de Chamélium -![Tellement... blanc.](oredict:oc:chameliumBlock) +![Tellement... blanc.](oredict:opencomputers:chameliumBlock) Quelques morceaux de [chamélium](../item/chamelium.md) peuvent être combinés pour fournir un bloc monochrome à des fins décoratives. Les blocs de chamélium peuvent aussi être teintés avec n'importe laquelle des 16 couleurs de Minecraft. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/charger.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/charger.md index 824302c285..e3580c106e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/charger.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/charger.md @@ -1,6 +1,6 @@ # Chargeur -![LEEROOOOOOOOOY](oredict:oc:charger) +![LEEROOOOOOOOOY](oredict:opencomputers:charger) Le chargeur est utilisé pour charger des appareils comme les [robots](robot.md), les [drones](../item/drone.md) et les [tablettes](../item/tablet.md). Un chargeur doit être activé en lui appliquant un signal de redstone. La vitesse de charge est basée sur la force du signal de redstone appliqué, avec une force de 15 donnant une vitesse de charge de 100%. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/disassembler.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/disassembler.md index 8273de5f46..2fdcaeeb8b 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/disassembler.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/disassembler.md @@ -1,6 +1,6 @@ # Désassembleur -![Construire, détruire](oredict:oc:disassembler) +![Construire, détruire](oredict:opencomputers:disassembler) Le désassembleur peut être utilisé pour déconstruire la plupart des objets d'OpenComputers en leurs éléments d'origine. C'est particulièrement utile pour récupérer des matériaux à partir d'anciens éléments qui ne sont plus utiles, ou pour déconstruire des appareils qui ne sont plus nécessaire ou qui ont été mal montés (ex : un [robot](robot.md) sans [système d'exploitation](../general/openOS.md)). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/diskDrive.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/diskDrive.md index e369473cce..53df9ef779 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/diskDrive.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/diskDrive.md @@ -1,6 +1,6 @@ # Lecteur de disquettes -![Silence, on tourne !](oredict:oc:diskDrive) +![Silence, on tourne !](oredict:opencomputers:diskDrive) Le lecteur de disquettes peut être utilisé pour lire des [disquettes](../item/floppy.md) en se servant d'un [ordinateur](../general/computer.md) connecté au lecteur de disquette. C'est très utile pour démarrer, car les niveaux les plus inférieurs de [boîtier d'ordinateur](case1.md) n'ont pas d'emplacement pour disquettes pré-construit, et vous aurez besoin d'un système d'exploitation pour démarrer l'[ordinateur](../general/computer.md). Un disque d'[OpenOS](../general/openOS.md) peut être fabriqué en utilisant une [disquette](../item/floppy.md) vierge et un [manuel](../item/manual.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/geolyzer.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/geolyzer.md index b4cb8067bb..45edb2d187 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/geolyzer.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/geolyzer.md @@ -1,6 +1,6 @@ # Géolyseur -![Pierre qui roule...](oredict:oc:geolyzer) +![Pierre qui roule...](oredict:opencomputers:geolyzer) Le géolyseur peut être utilisé par les [ordinateurs](../general/computer.md) pour scanner le terrain autour du géolyseur par la dureté approximative des blocs. Cela peut être utile pour générer des cartes du terrain à afficher sur un [projecteur d'hologramme](hologram1.md) ou pour détecter des blocs potentiellement précieux (les minerais sont généralement plus durs que la terre et la pierre). Les résultats du scan du géolyseur ont une certaine quantité de bruit ajoutée; en théorie, de nombreux scans peuvent être effectués pour obtenir une meilleure lecture du niveau de dureté d'un bloc. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/hologram1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/hologram1.md index 482d069853..e78309301a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/hologram1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/hologram1.md @@ -1,6 +1,6 @@ # Projecteur d'hologramme -![Is this the real life? Is this just fantasy?](oredict:oc:hologram1) +![Is this the real life? Is this just fantasy?](oredict:opencomputers:hologram1) Le projecteur d'hologramme est un afficheur volumétrique, c'est à dire qu'il fournit un tableau de voxels en trois dimensions qui peuvent être contrôlés individuellement par un [ordinateur](../general/computer.md) auquel il serait connecté. Le deuxième niveau de projecteur, même s'il a la même résolution que le projecteur de niveau 1, gère l'affichage de chaque voxel avec trois couleurs définissable par l'utilisateur. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/keyboard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/keyboard.md index b587ed71ea..c9ca8b535d 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/keyboard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/keyboard.md @@ -1,6 +1,6 @@ # Clavier -![AZERTY](oredict:oc:keyboard) +![AZERTY](oredict:opencomputers:keyboard) Un clavier est requis pour saisir du texte sur l'[écran](screen1.md), qu'ils existent physiquement dans le monde ou intégrés à un appareil comme un [robot](robot.md) ou une [tablette](../item/tablet.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/motionSensor.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/motionSensor.md index 21cbacb66f..544fbbf5df 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/motionSensor.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/motionSensor.md @@ -1,6 +1,6 @@ # Détecteur de mouvement -![Ne. Clignez. Pas. Des. Yeux.](oredict:oc:motionSensor) +![Ne. Clignez. Pas. Des. Yeux.](oredict:opencomputers:motionSensor) Le détecteur de mouvement permet aux [ordinateurs](../general/computer.md) de détecter le mouvement des entités vivantes. Si une entité se déplace plus vite qu'un seuil défini, un signal sera injecté dans les [ordinateurs](../general/computer.md) connectés au détecteur de mouvement. Le seuil peut être configuré en utilisant l'API des composants que le détecteur de mouvement expose aux ordinateurs connectés. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/netSplitter.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/netSplitter.md index 0b395c0142..7b71c9435e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/netSplitter.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/netSplitter.md @@ -1,6 +1,6 @@ # Répartiteur réseau -![*.net *.split](oredict:oc:netSplitter) +![*.net *.split](oredict:opencomputers:netSplitter) Le répartiteur réseau est un appareil qui permet de contrôler la connectivité entre des sous-réseaux. A la différence du [relai](relay.md) ou du [convertisseur énergétique](powerConverter.md), il connecte directement entre eux des sous-réseaux voisins, c'est à dire qu'il est possible d'accéder aux composants des sous-réseaux. La connectivité de chaque face peut être activée en utilisant une clé (ex: le [crisseur](../item/wrench.md)). Quand un signal de redstone est appliqué au répartiteur réseau, la connectivité de toutes les faces est inversée. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerConverter.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerConverter.md index 91592a8638..fc6a583c6e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerConverter.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerConverter.md @@ -1,5 +1,5 @@ # Convertisseur énergétique -![L'un des nôtres ? L'un des nôtres !](oredict:oc:powerConverter) +![L'un des nôtres ? L'un des nôtres !](oredict:opencomputers:powerConverter) Le convertisseur énergétique est la manière la plus rapide de convertir de l'énergie provenant d'autres mods en énergie interne à OpenComputers. Si vous utilisez un seul ordinateur, vous n'aurez probablement pas besoin de convertisseur. Si vous avez un grand groupe de capaciteurs dont vous utilisez l'énergie de temps en temps, vous n'en aurez sûrement pas besoin non plus. Cependant, si vous souhaitez alimenter directement un [assembleur électronique](assembler.md) ou un [chargeur](charger.md), c'est généralement une bonne idée d'utiliser un convertiseeur au lieu de les connecter directement à une source externe d'énergie. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerDistributor.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerDistributor.md index fa717d3b5b..2b98941257 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerDistributor.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/powerDistributor.md @@ -1,5 +1,5 @@ # Distributeur énergétique -![Le pouvoir au peuple.](oredict:oc:powerDistributor) +![Le pouvoir au peuple.](oredict:opencomputers:powerDistributor) Le distributeur énergétique distribue l'énergie d'un espace de stockage partagé (comme un [capaciteur](capacitor.md)), permettant à de nombreux sous-réseaux de partager leur énergie sans que leurs composants soient exposés aux ordinateurs des autres sous-réseaux. Il fonctionne en répartissant équitablement l'énergie dans tous les sous-réseaux auquel il est connecté, de façon à ce que la quantité *relative* d'énergie est la même dans chacun. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/printer.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/printer.md index 9b47a78b07..7582613551 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/printer.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/printer.md @@ -1,6 +1,6 @@ # Imprimante 3D -![L'impression en 2D, c'est tellement dépassé.](oredict:oc:printer) +![L'impression en 2D, c'est tellement dépassé.](oredict:opencomputers:printer) Les imprimantes 3D vous permettent d'imprimer n'importe quel bloc de n'importe forme, avec n'importe quelle texture. Pour démarrer avec les imprimantes 3D, vous devrez placer un bloc d'imprimante 3D à côté d'un ordinateur. Cela vous donnera accès à l'API de composant `printer3d`, ce qui permet de paramétrer et d'imprimer des [modèles](print.md) en utilisant les fonction fournies. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/raid.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/raid.md index 6a849e991e..b8b0fa5d59 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/raid.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/raid.md @@ -1,6 +1,6 @@ # Raid -![Une instance à 40.](oredict:oc:raid) +![Une instance à 40.](oredict:opencomputers:raid) Le bloc de RAID peut accueillir 3 [disques dur](../item/hdd1.md) qui seront combinés en un seul système de fichiers. Ce système de fichiers combinés a la taille de la somme des capacités des [disques dur](../item/hdd1.md) individuels, et est disponible pour tous les [ordinateurs](../general/computer.md) connectés au RAID. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/redstone.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/redstone.md index ae35bfc29c..f8d72aeb82 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/redstone.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/redstone.md @@ -1,6 +1,6 @@ # Redstone E/S -![Salut Red.](oredict:oc:redstone) +![Salut Red.](oredict:opencomputers:redstone) Le bloc d'E/S de redstone peut être utilisé pour lire et émettre des signaux de redstone à distance. Il se comporte comme un hybride des [cartes de redstone](../item/redstoneCard1.md) de niveau 1 et 2 : il peut aussi bien lire et émettre des signaux analogiques que des signaux empaquetés (bundle), mais il ne peut pas lire ou émettre de signaux redstone sans-fil. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/relay.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/relay.md index 6e70c3eee3..bcad6afe33 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/relay.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/relay.md @@ -1,6 +1,6 @@ # Relai -![Construit des ponts.](oredict:oc:relay) +![Construit des ponts.](oredict:opencomputers:relay) Le relai peut être utilisé pour permettre à différents sous-réseaux de s'envoyer des messages réseau entre eux, sans exposer leurs composants aux [ordinateurs](../general/computer.md) des autres réseaux. Maintenir l'état "local" d'un composant est généralement une bonne idée, pour éviter aux [ordinateurs](../general/computer.md) d'utiliser le mauvais [écran](screen1.md) ou pour éviter qu'une surcharge de composants survienne (ce qui provoque le crash de l'[ordinateur](../general/computer.md) et l'empêche de démarrer). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/screen1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/screen1.md index e58a88981e..f8822c8033 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/screen1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/screen1.md @@ -1,6 +1,6 @@ # Ecrans -![Tu vois ça ?](oredict:oc:screen1) +![Tu vois ça ?](oredict:opencomputers:screen1) Un écran est utilisé en combinaison avec une [carte graphique](../item/graphicsCard1.md), pour permettre aux [ordinateurs](../general/computer.md) d'afficher du texte. Les différents niveaux d'écrans ont différentes capacités, comme le support de différentes résolutions et couleurs. La qualité des écrans va d'une faible résolution en affichage monochrome à une haute résolution en 256 couleurs. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/serverRack.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/serverRack.md index 681fcc50f6..288a0f4197 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/serverRack.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/serverRack.md @@ -1,6 +1,6 @@ # Support de serveur -![Logement gratuit.](oredict:oc:serverRack) +![Logement gratuit.](oredict:opencomputers:serverRack) Un support de serveur peut contenir jusqu'à 4 [serveurs](../item/server1.md). Un [serveur](../item/server1.md) est un [ordinateur](../general/computer.md) de plus haut niveau, qui peut seulement être lancé dans un support de serveur. Les [serveurs](../item/server1.md) peuvent être contrôlés à distance avec un [terminal à distance](../item/terminal.md). Le nombre de [terminaux à distance](../item/terminal.md) qui peuvent être connectés à un seul [serveur](../item/server1.md) à la fois dépend du niveau du [serveur](../item/server1.md). La distance jusqu'à laquelle le [terminal à distance](../item/terminal.md) fonctionne peut être configurée dans l'interface du support. De plus grandes valeurs nécessitent plus d'énergie. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/switch.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/switch.md index c5421f8985..beb888c171 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/switch.md @@ -1,6 +1,6 @@ # Routeur -![[relays](relay.md).](oredict:oc:switch) +![[relays](relay.md).](oredict:opencomputers:switch) *Ce bloc est déprécié et sera retiré dans une version future.* Transformez les en [relai](relay.md) pour éviter de les perdre. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/transposer.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/transposer.md index 1d743969d5..a83ece0a8e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/transposer.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/transposer.md @@ -1,6 +1,6 @@ # Transposeur -![Quel poseur!](oredict:oc:transposer) +![Quel poseur!](oredict:opencomputers:transposer) Le transposeur fait le lien entre les entonnoirs controlés par la redstone et les [robots](robot.md), ce qui permet le transfert d'objets et de fluides entre des blocs voisins contrôlé par [ordinateur](../general/computer.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/fr_fr/block/waypoint.md index de69a8be8b..4ea7bdfb07 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/block/waypoint.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/block/waypoint.md @@ -1,6 +1,6 @@ # Point de passage -!["Par là !" - "Non, par là !"](oredict:oc:waypoint) +!["Par là !" - "Non, par là !"](oredict:opencomputers:waypoint) Le point de passage n'a aucune utilité en soi, mais dans la façon dont il peut être utilisé. Les [améliorations de navigation](../item/navigationUpgrade.md) peuvent détecter les points de passage, ainsi les appareils équipés d'une amélioration de navigation peuvent utiliser ces points de passage pour parcourir le monde. C'est particulièrement utile pour écrire des programmes facilement ré-utilisables par des appreils comme les [robots](robot.md) et les [drones](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md b/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md index c0937a7a79..d76a890c27 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md @@ -4,13 +4,13 @@ Voici un peu de texte de test pour la version de Markdown supportée par le syst ![Ceci est une info-bulle...](opencomputers:textures/gui/printer_ink.png) ![Ceci est une info-bulle...](opencomputers:/textures/gui/printer_material.png) *Ceci* est du texte en *italique*, ~~barré~~ peut-être **un peu** de texte **en gras**. Est-ce que _c'est souligné _? Oh, non, _c'est aussi en italique!_ Bon, c'est [un lien](../index.md). -![C'est rendu en direct.](oredict:oc:assembler) +![C'est rendu en direct.](oredict:opencomputers:assembler) ## Entête plus petite [avec un *lien* aussi mais cette __fois__ plus long](../block/adapter.md) -![Ceci est une autre info-bulle.](item:OpenComputers:item@23) +![Ceci est une autre info-bulle.](item:opencomputers:item@23) un peu de texte directement au dessus de l'afficheur d'objet pour tester l'espacement -![Toutes ces couleurs.](oredict:craftingPiston) +![Toutes ces couleurs.](oredict:forge/piston) un peu de texte directement en dessous de l'afficheur d'objet pour tester l'espacement Ceci est en *italique @@ -42,9 +42,9 @@ asdasd ![oh mon dieu, la récursion !](img/example.png) qweqwe Et finalement, [c'est un lien !](https://avatars1.githubusercontent.com/u/514903). -![image de l'objet inexistante](item:ça n'existe pas) -![image de l'objet inexistante](block:ça n'existe pas) -![image de l'objet inexistante](oredict:ça n'existe pas) +![image de l'objet inexistante](item:this_is_broken) +![image de l'objet inexistante](block:this_is_broken) +![image de l'objet inexistante](oredict:this_is_broken) tests de retour à la ligne 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 @@ -53,17 +53,17 @@ tests de retour à la ligne * 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 - `123456789012345678901234567890.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890` -c'est un test pour une![](oredict:oc:cpu1)une image dans une lignekakakakalalsd 123 as +c'est un test pour une![](oredict:opencomputers:cpu1)une image dans une lignekakakakalalsd 123 as -c'est un test pour une![](oredict:oc:cpu1) +c'est un test pour une![](oredict:opencomputers:cpu1) une image avec un retour à la ligne après c'est un test pour une -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) une image entre deux lignes c'est un test pour une -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) une image entre deux lignes vides diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/general/quickstart.md b/src/main/resources/assets/opencomputers/doc/fr_fr/general/quickstart.md index 133f50ec5b..9eba8fb45f 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/general/quickstart.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/general/quickstart.md @@ -6,7 +6,7 @@ Aussi connu en tant que "comment construire votre premier ordinateur". Pour fair Premièrement, vous aurez besoin d'un [boîtier](../block/case1.md). C'est le bloc qui contiendra tous les composants, en définissant le comportement de l'ordinateur que vous construisez. -![Un boîtier d'ordinateur de niveau 2.](oredict:oc:case2) +![Un boîtier d'ordinateur de niveau 2.](oredict:opencomputers:case2) Par exemple, vous aurez besoin de choisir quel niveau de [carte graphique](../item/graphicsCard1.md) vous voulez utiliser, si vous avez besoin d'une [carte réseau](../item/lanCard.md), une [carte de redstone](../item/redstoneCard1.md) ou, si vous faites des essais en mode créatif, peut être même une [carte de débogueur](../item/debugCard.md). @@ -39,7 +39,7 @@ Maintenant, si vous avez utilisé un [boîtier](../block/case2.md) de niveau 2 c Il vit ! Ou il devrait, en tout cas. Si ce n'est pas le cas quelque chose ne va pas, et vous devriez investiguer en utilisant l'[analyzer](../item/analyzer.md). Mais en supposant qu'il fonctionne maintenant, vous avez presque fini. La partie la plus compliquée est terminée. Tout ce qui reste à faire est de lui permettre d'accepter des entrées, et d'afficher des sorties. Pour permettre à l'[ordinateur](computer.md) d'afficher des informations, vous devrez vous munir d'un [écran](../block/screen1.md) et d'une [carte graphique](../item/graphicsCard1.md). -![Non, ce n'est pas un écran plat.](oredict:oc:screen2) +![Non, ce n'est pas un écran plat.](oredict:opencomputers:screen2) Placez l'[écran](../block/screen1.md) à côté de votre [boîtier](../block/case1.md) ou, à nouveau, connectez le en utilisant des [câbles](../block/cable.md). Puis placez une [carte graphique](../item/graphicsCard1.md) de votre choix dans le [boîtier](../block/case1.md). Vous devriez maintenant voir un curseur clignotant sur l'[écran](../block/screen1.md). Finalement, placez un [clavier](../block/keyboard.md) soit sur l'[écran](../block/screen1.md) lui-même, soit juste en face de l'[écran](../block/screen1.md), pour activer l'entrée de données au [clavier](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/abstractBusCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/abstractBusCard.md index bd5e7d93f6..3c957c25b5 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/abstractBusCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/abstractBusCard.md @@ -1,5 +1,5 @@ # Carte de bus abstraite -![Plus de réseau!](oredict:oc:abstractBusCard) +![Plus de réseau!](oredict:opencomputers:abstractBusCard) Cette carte permet aux [ordinateurs](../general/computer.md), aux [serveurs](server1.md) et aux [robots](../block/robot.md) d'interagir avec le bus d'abstraction de StargateTech2. Quand la carte est installée, ces blocs se connecteront au bus d'abstraction et un composant devient disponible aux machines, pour envoyer des messages à travers le bus d'abstraction. Les messages entrants du bus d'abstraction sont convertis en signaux qui sont injectés dans la machine. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/acid.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/acid.md index 93b20ef3f2..9020586237 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/acid.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/acid.md @@ -1,6 +1,6 @@ # Grog -![Un reflux?](oredict:oc:materialAcid) +![Un reflux?](oredict:opencomputers:materialAcid) Cette délicieuse [citation nécessaire] mixture peut être consommée si jamais vous ressentez le besoin de vous... amuser. Ou de détruire votre tube digestif. Ou les deux. Il peut également servir en tant qu'ingrédient pour d'autres objets beaucoup plus utiles. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/alu.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/alu.md index ed6a818835..4d6f93f2c6 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/alu.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/alu.md @@ -1,5 +1,5 @@ # Unité de Logique Arithmétique -![Je être logique !](oredict:oc:materialALU) +![Je être logique !](oredict:opencomputers:materialALU) Elle est utilisée pour fabriquer des composants effectuant des calculs, comme les [processeurs](cpu1.md) et les [cartes graphiques](graphicsCard1.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/analyzer.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/analyzer.md index 5d7bd945bf..09d6d5e7c4 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/analyzer.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/analyzer.md @@ -1,6 +1,6 @@ # Analyseur -![Je... ne dois pas... faire... de... mauvaises blagues...](oredict:oc:analyzer) +![Je... ne dois pas... faire... de... mauvaises blagues...](oredict:opencomputers:analyzer) L'analyseur est un outil très pratique pour afficher des informations les blocs liés à OpenComputers. Cliquez simplement sur un bloc (en étant éventuellement accroupi) pour afficher quelques informations dans la zone de chat. Cela va de choses basiques comme l'adresse des composants, aux niveaux d'énergie du sous-réseau dont le bloc fait partie, jusqu'aux informations sur l'erreur qui a mené un [ordinateur](../general/computer.md) à planter, par exemple. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/angelUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/angelUpgrade.md index 2384a61d86..e8e7283602 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/angelUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/angelUpgrade.md @@ -1,5 +1,5 @@ # Amélioration Ange -![Alléluia.](oredict:oc:angelUpgrade) +![Alléluia.](oredict:opencomputers:angelUpgrade) Cette amélioration permet aux [robots](../block/robot.md) de placer des blocs en l'air, sans bloc de soutien. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/apu1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/apu1.md index 9ab4090c17..f999898618 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/apu1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/apu1.md @@ -1,6 +1,6 @@ # Unité de Traitement Accélérée (UTA) -![Unificateur de Tabourets Acrobates.](oredict:oc:apu1) +![Unificateur de Tabourets Acrobates.](oredict:opencomputers:apu1) Cet élément est la parfaite union d'un [processeur](cpu1.md) et d'une [carte graphique](graphicsCard1.md). En utiliser une revient à vous laisser un emplacement de carte supplémentaire. Comme un processeur normal, elle définit l'architecture de l'[ordinateur](../general/computer.md), et le nombre de composants qui peuvent être connectés à l'[ordinateur](../general/computer.md) avant qu'il ne puisse plus fonctionner. De plus, elle fournit des fonctionnalités graphiques de base. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/arrowKeys.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/arrowKeys.md index 1c272ae924..1911ffd3fc 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/arrowKeys.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/arrowKeys.md @@ -1,5 +1,5 @@ # Touches directionnelles -![Heureusement que ce n'est pas fait avec de vraies flèches...](oredict:oc:materialArrowKey) +![Heureusement que ce n'est pas fait avec de vraies flèches...](oredict:opencomputers:materialArrowKey) Au risque de me répéter : c'est la crise des touches. Parce que l'économie des touches a connu une sérieuse inflation ces dernières années. Elles sont utilisées pour fabriquer des [claviers](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/batteryUpgrade1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/batteryUpgrade1.md index ac291754bb..dc51781a6a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/batteryUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/batteryUpgrade1.md @@ -1,5 +1,5 @@ # Amélioration de batterie -![Made of Metal.](oredict:oc:batteryUpgrade1) +![Made of Metal.](oredict:opencomputers:batteryUpgrade1) Cette amélioration augmente la quantité maximale d'énergie stockée par les appareils, comme les [robots](../block/robot.md) et les [tablettes](tablet.md), ce qui leur permet de fonctionner plus longtemps sans devoir revenir vers un [chargeur](../block/charger.md). Les améliorations de batterie de niveau supérieur peuvent stocker plus d'énergie. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/buttonGroup.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/buttonGroup.md index 87d5208f4e..a3f9049dd0 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/buttonGroup.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/buttonGroup.md @@ -1,5 +1,5 @@ # Groupe de boutons -![Avec Biactol...](oredict:oc:materialButtonGroup) +![Avec Biactol...](oredict:opencomputers:materialButtonGroup) Parce que vous avez *toujours* plein de boutons qui trainent quelque part. Ne mentez pas. Nous avons tous à faire un Maj-clic sur ce bouton de recette, encore et encore. Les groupes de boutons sont utilisés pour fabriquer des [claviers](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/card.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/card.md index 27550f6da1..2b3894aec7 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/card.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/card.md @@ -1,5 +1,5 @@ # Base de carte -![Ne peut pas être lue.](oredict:oc:materialCard) +![Ne peut pas être lue.](oredict:opencomputers:materialCard) C'est un matériau communément utilisé pour fabriquer des cartes dans OpenComputers (comme les [cartes graphiques](graphicsCard1.md), les [cartes réseau](lanCard.md), etc.). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cardContainer1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cardContainer1.md index ae60a4532f..007b110add 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cardContainer1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cardContainer1.md @@ -1,5 +1,5 @@ # Conteneur de carte -![il peu avwar dé cart !](oredict:oc:cardContainer1) +![il peu avwar dé cart !](oredict:opencomputers:cardContainer1) Le conteneur de carte est une amélioration de conteneur pour que des cartes puissent être insérées ou retirées d'un [robot](../block/robot.md) à la volée. Le niveau maximum de la carte que l'emplacement peut contenir est égal au niveau du conteneur. Contrairement aux amélioration classiques, la complexité des conteneurs est de deux fois leur niveau. Regardez [par là](../block/robot.md) pour avoir des détails sur la complexité des robots. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chamelium.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chamelium.md index 4c3e4b214a..d6a4b76730 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chamelium.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chamelium.md @@ -1,6 +1,6 @@ # Chamélium -![Ca vient de caméléon, si vous vous demandiez.](oredict:oc:chamelium) +![Ca vient de caméléon, si vous vous demandiez.](oredict:opencomputers:chamelium) Le chamélium est un matériau métamorphe qui est utilisé pour créer des [impressions 3D](../block/print.md) dans une [imprimante 3D](../block/printer.md). De lui même, il n'a aucune utilité, mais est très pratique pour créer des zones simples et entièrement monochromes. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chip1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chip1.md index 5f6a89e2e4..9229704abf 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chip1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chip1.md @@ -1,5 +1,5 @@ # Puce électronique -![Pas celles qui se mangent.](oredict:oc:circuitChip1) +![Pas celles qui se mangent.](oredict:opencomputers:circuitChip1) Les puces électroniques sont le pain quotidien de la fabrication de composants électroniques. Elles existent en différents niveaux, pour fabriquer différents niveaux de composants. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chunkloaderUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chunkloaderUpgrade.md index f7acaaac48..9816731f3a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/chunkloaderUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/chunkloaderUpgrade.md @@ -1,6 +1,6 @@ # Amélioration chargement de chunk -![Je ne vais nulle part.](oredict:oc:chunkloaderUpgrade) +![Je ne vais nulle part.](oredict:opencomputers:chunkloaderUpgrade) L'amélioration de chargement de chunk peut être installée sur des appareils (comme les [robots](../block/robot.md) et les [microcontrôleurs](../block/microcontroller.md)) pour leur permettre de garder le chunk dans lequel ils sont - ainsi que les chunks environnants - chargés. Cela consomme de l'énergie, cependant. Le chargeur de chunks peut être activé et désactivé par l'API du composant exposé à l'appareil. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/circuitBoard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/circuitBoard.md index e6657ab101..7cf16a1f8e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/circuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/circuitBoard.md @@ -1,5 +1,5 @@ # Plaque de circuit imprimé -![Il faut plus d'or.](oredict:oc:materialCircuitBoard) +![Il faut plus d'or.](oredict:opencomputers:materialCircuitBoard) C'est un objet intermédiaire fait à partir de [plaques de circuit imprimé brutes](rawCircuitBoard.md) et utilisé pour fabriquer des [circuits imprimés](printedCircuitBoard.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/componentBus1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/componentBus1.md index 7a61997a07..f2cf2634cf 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/componentBus1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/componentBus1.md @@ -1,6 +1,6 @@ # Bus des composants -![Plus de P-P-P-PUISSANCE.](oredict:oc:componentBus1) +![Plus de P-P-P-PUISSANCE.](oredict:opencomputers:componentBus1) Un bus des composants est une amélioration dédiée aux [serveurs](server1.md) qui permet à un [serveur](server1.md) de communiquer avec plus de composants à la fois, sans s'éteindre. Comme avec les [processeurs](cpu1.md), un bus de plus haut niveau augmente la limite des composants. Les [serveurs](server1.md) de plus haut niveau sont également capables d'accepter plus de bus de composants, ce qui permet de communiquer avec encore plus de composants. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/controlUnit.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/controlUnit.md index 8a90625280..0ca45d940d 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/controlUnit.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/controlUnit.md @@ -1,5 +1,5 @@ # Unité de contrôle -![Avec régulateur de vitesse intégré.](oredict:oc:materialCU) +![Avec régulateur de vitesse intégré.](oredict:opencomputers:materialCU) Un objet de fabrication de haut niveau utilisé dans des circuits avancés, comme les [processeurs](cpu1.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cpu1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cpu1.md index dfcab698a0..eec640073c 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cpu1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cpu1.md @@ -1,6 +1,6 @@ # Processeur -![Cerveaaauuu...](oredict:oc:cpu1) +![Cerveaaauuu...](oredict:opencomputers:cpu1) L'unité centrale de traitement (ou *central processing unit (CPU)* en anglais) est un composant essentiel à chaque [ordinateur](../general/computer.md) ou [serveur](server1.md). Il définit l'architecture de l'[ordinateur](../general/computer.md), et le nombre de composants qui peuvent être connectés à l'[ordinateur](../general/computer.md) avant qu'il ne cesse de fonctionner. Un processeur de plus haut niveau permet également d'avoir plus d'appels par tick à l'[ordinateur](../general/computer.md) - en d'autres termes : plus le processeur est puissant, plus il est rapide. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/craftingUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/craftingUpgrade.md index 8c0420354c..22a4d0daa9 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/craftingUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/craftingUpgrade.md @@ -1,5 +1,5 @@ # Amélioration d'artisanat -![C'est en forgeant...](oredict:oc:craftingUpgrade) +![C'est en forgeant...](oredict:opencomputers:craftingUpgrade) L'amélioration d'artisanat permet aux [robots](../item/inventoryUpgrade.md) de réaliser des recettes avec et sans forme en utilisant leur [inventaire](../item/inventoryUpgrade.md). Pour fabriquer, la grille de 3x3 en haut à gauche de l'[inventaire](../item/inventoryUpgrade.md) du [robot](../block/robot.md) est utilisée comme grille de fabrication. Les objets doivent être arrangés selon la recette. Les résultats seront replacés dans l'[inventaire](../item/inventoryUpgrade.md) du [robot](../block/robot.md). Comme pour les objets ramassés par terre, le résultat sera placé dans l'emplacement sélectionné; s'il n'y arrive pas, l'objet sera placé dans l'emplacement vide suivant. S'il n'y a plus de place libre, le résultat sera lâché par terre. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cuttingWire.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cuttingWire.md index d5aa5385ea..7e70cbe7e6 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/cuttingWire.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/cuttingWire.md @@ -1,5 +1,5 @@ # Fil de coupe -![Ce n'est pas un garrot. Je le jure.](oredict:oc:materialCuttingWire) +![Ce n'est pas un garrot. Je le jure.](oredict:opencomputers:materialCuttingWire) Un objet qui n'existe que si vous utilisez les recettes difficiles, il est utilisé pour fabriquer des [plaques de circuit imprimé brutes](rawCircuitBoard.md). Très inefficacement. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/dataCard1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/dataCard1.md index 422df92d9d..9bacb6ad9a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/dataCard1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/dataCard1.md @@ -1,6 +1,6 @@ # Carte de données -![Contrairement à la croyance populaire, elle ne stocke pas de données.](oredict:oc:dataCard1) +![Contrairement à la croyance populaire, elle ne stocke pas de données.](oredict:opencomputers:dataCard1) La carte de données est une carte utilitaire qui fournit quelques algorithmes qui seraient soit compliqués à implémenter sur une architecture, soit lent à l'exécution. Elle fournit des fonctionnalités de hachage, ainsi que des fonctionnalités d'inflation/déflation. De plus, elle contient un système de fichiers qui fournit un certain nombre de programmes en utilisant les fonctionnalités de la carte, comme la carte internet. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/databaseUpgrade1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/databaseUpgrade1.md index 64b34c2e8e..fc58ed5928 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/databaseUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/databaseUpgrade1.md @@ -1,6 +1,6 @@ # Amélioration de base de données -![Je suis dans la matrice!](oredict:oc:databaseUpgrade1) +![Je suis dans la matrice!](oredict:opencomputers:databaseUpgrade1) L'amélioration de base de données peut être configurée pour stocker une liste de représentations de groupes d'objets, qui peuvent ensuite être utilisés par d'autres composants. C'est particulièrement utile pour les objets qui sont uniquement distinguables par leurs données NBT, ce qui ne fait pas partie des données affichées de base pour un groupe d'objets. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/disk.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/disk.md index 57f96b5bfa..5eec5867ee 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/disk.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/disk.md @@ -1,5 +1,5 @@ # Disque -![Monde. RIP Terry Pratchett.](oredict:oc:materialDisk) +![Monde. RIP Terry Pratchett.](oredict:opencomputers:materialDisk) Un composant de fabrication de base, utilisé pour fabriquer des moyens de stockage de données, comme des [disquettes](floppy.md) et des [disques durs](hdd1.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/droneCase1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/droneCase1.md index 08b8b686fe..eacfaf5fe0 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/droneCase1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/droneCase1.md @@ -1,6 +1,6 @@ # Boitier de drone -![Le vol du bourdon.](oredict:oc:droneCase1) +![Le vol du bourdon.](oredict:opencomputers:droneCase1) Le boitier de drone est utilisé pour construire des [drones](drone.md) dans l'[assembler](../block/assembler.md). Les [drones](drone.md) sont des machines légères, rapides et très mobiles avec des fonctionnalités réduites (moins d'améliorations et d'emplacements de composant disponibles). Contrairement aux [robots](../block/robot.md), ils ne peuvent pas utiliser d'outils, et peuvent seulement interagir avec le monde de manière relativement limitée. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/eeprom.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/eeprom.md index 678207bf74..61550d9bde 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/eeprom.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/eeprom.md @@ -1,6 +1,6 @@ # EEPROM -![Let's get this party started.](oredict:oc:eeprom) +![Let's get this party started.](oredict:opencomputers:eeprom) L'EEPROM est ce qui contient le code utilisé pour initialiser un ordinateur quand il démarre. Les données sont stockées dans un tableau d'octets, et peuvent avoir différentes significations pour différentes architectures de [processeur](cpu1.md). Par exemple, pour Lua, c'est généralement un petit script qui recherche les systèmes de fichier avec un script d'initialisation, pour d'autres architectures ça pourrait être du code machine. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/experienceUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/experienceUpgrade.md index 3ae09f216d..73614b348e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/experienceUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/experienceUpgrade.md @@ -1,6 +1,6 @@ # Amélioration d'expérience -![Ca n'a aucun sens, mais c'est cool.](oredict:oc:experienceUpgrade) +![Ca n'a aucun sens, mais c'est cool.](oredict:opencomputers:experienceUpgrade) L'amélioration d'expérience est une amélioration très spéciale, puisqu'elle permet aux [robots](../block/robot.md) et aux [drones](drone.md) pour collecter l'expérience en accomplissant de nombreuses actions, comme de casser des blocs de minerai ou de tuer des entités. Une simple amélioration peut stocker jusqu'à 30 niveaux, en fournissant un bonus passif par niveau d'expérience, comme de plus grandes vitesses de récolte et une capacité d'énergie accrue. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/floppy.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/floppy.md index 0e6c7aa256..b83d6a0316 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/floppy.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/floppy.md @@ -1,5 +1,5 @@ # Disquette -![Plusieurs pouces.](oredict:oc:floppy) +![Plusieurs pouces.](oredict:opencomputers:floppy) La disquette est le moyen de stockage de données le moins cher et le plus petit dans OpenComputers. C'est une manière très pratique de stocker des données et de les transférer entre les [ordinateurs](../general/computer.md) et les [robots](../block/robot.md) dès le début de la partie. Vous pourrez également trouver des disquettes avec des programmes utiles dans des coffres de donjons (le gestionnaire de paquets Open Programs est un bon exemple, ce qui permet d'installer facilement des programmes à partir d'un dépôt GitHub central). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/generatorUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/generatorUpgrade.md index 40750c39c5..a1714e8185 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/generatorUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/generatorUpgrade.md @@ -1,6 +1,6 @@ # Amélioration de générateur -![Generator X.](oredict:oc:generatorUpgrade) +![Generator X.](oredict:opencomputers:generatorUpgrade) L'amélioration de générateur permet aux appareils de se recharcher à la volée. Pour le moment elle supporte seulement les carburants solides, comme le charbon. Elle a un inventaire interne qui peut stocker un groupe plein de carburant. Le carburant en surplus peut être retiré du générateur en utilisant la méthode appropriée de l'API du composant. En retirant une amélioration du générateur d'un [robot](../block/robot.md), son contenu sera lâché par terre. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/graphicsCard1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/graphicsCard1.md index 014d083140..9d5641f830 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/graphicsCard1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/graphicsCard1.md @@ -1,6 +1,6 @@ # Carte graphique -![Images fantaisie.](oredict:oc:graphicsCard1) +![Images fantaisie.](oredict:opencomputers:graphicsCard1) La carte graphique est un composant essentiel pour la plupart des [ordinateurs](../general/computer.md), elle leur permet d'afficher du texte sur un [écran](../block/screen1.md) qui leur est connecté. La carte graphique a plusieurs niveaux, et comme les [écrans](../block/screen1.md), elle supporte différentes résolutions et profondeurs de couleur. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hdd1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hdd1.md index 5580dd8b3b..6c6edc9e49 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hdd1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hdd1.md @@ -1,5 +1,5 @@ # Disque dur -![Spaaaace.](oredict:oc:hdd1) +![Spaaaace.](oredict:opencomputers:hdd1) Le disque dur est un moyen de stockage de données de haut niveau dans OpenComputers. Tous les moyens de stockage de données fonctionnent à la même vitesse ; un moyen de stockage de plus haut niveau fournit juste plus d'espace. Il y a également quelques appareils qui peuvent seulement utiliser les disques durs (bien que les serveurs pourraient utiliser un [lecteur de disquettes](../block/diskDrive.md) externe, par exemple). Les disques durs peuvent être placés dans un [raid](../block/raid.md), ce qui permet à plusieurs disques de partager le même système de fichiers. Remarquez que placer un disque dur dans un [raid](../block/raid.md) efface le contenu du disque. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverBoots.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverBoots.md index c6be248c72..9acaa25da2 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverBoots.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverBoots.md @@ -1,6 +1,6 @@ # Bottes de vol plané -![Si vous attrapez un poulet, et que vous sautez...](oredict:oc:hoverBoots) +![Si vous attrapez un poulet, et que vous sautez...](oredict:opencomputers:hoverBoots) Si vous ne voulez pas vous embêter à programmer un [drone](drone.md), voici un usage alternatif : un tremplin ! Ou de glorieux patins à roulettes. Un truc du genre. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverUpgrade1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverUpgrade1.md index 0ae92f3f69..6133141cba 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/hoverUpgrade1.md @@ -1,6 +1,6 @@ # Amélioration de vol plané -![Flotter comme une plume.](oredict:oc:hoverUpgrade1) +![Flotter comme une plume.](oredict:opencomputers:hoverUpgrade1) L'amélioration de vol plané permet aux [robots](../block/robot.md) de voler beaucoup plus au dessus du sol que ce qu'ils pourraient le faire normalement. Contrairement aux [drones](drone.md), ils sont limités par défaut à une hauteur de vol de 8 blocs. Ca n'est généralement pas un problème, parce qu'ils peuvent tout de même monter aux murs. Leurs règles de déplacement peuvent être résumées ainsi : - Les robots peuvent seulement bouger si la position de départ ou d'arrivée est valide (par exemple pour un pont). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inkCartridge.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inkCartridge.md index 6a8b603c19..8162b64f38 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inkCartridge.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inkCartridge.md @@ -1,5 +1,5 @@ # Cartouche d'encre -![Les couleurs de l'arc-en-ciel.](oredict:oc:inkCartridge) +![Les couleurs de l'arc-en-ciel.](oredict:opencomputers:inkCartridge) Les cartouches d'encre sont faites pour recharger le tampon encreur des [imprimantes 3D](../block/printer.md). Même s'il est possible de les recharger directement avec les pigments, c'est désordonné et inefficace. Il est vivement recommandé d'investir dans une véritable cartouche d'encre OpenComputers (TM), dès aujourd'hui ! (Annonce : elles pourraient coûter plus cher que l'imprimante elle-même, ou pas). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/internetCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/internetCard.md index 0f8f415fb2..fe084bcc38 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/internetCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/internetCard.md @@ -1,6 +1,6 @@ # Carte internet -![Vidéos de chat dans 3, 2, ...](oredict:oc:internetCard) +![Vidéos de chat dans 3, 2, ...](oredict:opencomputers:internetCard) La carte internet donne accès à Internet aux [ordinateurs](../general/computer.md). Elle permet d'exécuter de simples requêtes HTTP, et aussi d'ouvrir des sockets client TCP dans lesquels vous pouvez lire et écrire. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/interweb.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/interweb.md index aae00aad14..0f88875d09 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/interweb.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/interweb.md @@ -1,5 +1,5 @@ # Interweb -![Un site sur la toile est un endroit où il y a des toiles. D'araignée.](oredict:oc:materialInterweb) +![Un site sur la toile est un endroit où il y a des toiles. D'araignée.](oredict:opencomputers:materialInterweb) L'interweb est un composant de base pour tout ce qui est lié à des communications à longue distance. Il utilise les mécaniques bizarres de tous les trucs liés au Néant pour permettre la communication via des tunnels quantiques. Il est utilisé notamment pour les [cartes internet](internetCard.md) et les [cartes liées](linkedCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryControllerUpgrade.md index d872e38942..fbabc489cd 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryControllerUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryControllerUpgrade.md @@ -1,6 +1,6 @@ # Amélioration du contrôleur d'inventaire -![J'ai le contrôle.](oredict:oc:inventoryControllerUpgrade) +![J'ai le contrôle.](oredict:opencomputers:inventoryControllerUpgrade) L'amélioration du contrôleur d'inventaire donne aux [robots](../block/robot.md) et aux [drones](drone.md) des capacités d'interactions étendues avec les inventaires. Elle permet à un appareil de viser spécifiquement un emplacement dans un inventaire externe quand il lâche ou ramasse un objet. Elle permet également à un appareil de lire des informations détaillées à propos d'un objet. Enfin, elle fournit aux [robots](../block/robot.md) un moyen de changer leur outil équipé sans aide extérieure. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryUpgrade.md index c1b94d3e2e..ca3c5aea26 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/inventoryUpgrade.md @@ -1,6 +1,6 @@ # Amélioration d'inventaire -![Mais où est-ce qu'elle met tout ça...](oredict:oc:inventoryUpgrade) +![Mais où est-ce qu'elle met tout ça...](oredict:opencomputers:inventoryUpgrade) L'amélioration d'inventaire fournit des emplacements d'inventaire supplémentaires pour les [robots](../block/robot.md) et les [drones](drone.md). Pour chaque amélioration d'inventaire, un [robot](../block/robot.md) gagnera 16 emplacements d'inventaire supplémentaires, pour un maximum de 64 emplacements au total ; un [drone](drone.md) gagnera 4 emplacements par amélioration, pour un total de 8 emplacements. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/lanCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/lanCard.md index 5a7fc0794a..faafb5c06f 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/lanCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/lanCard.md @@ -1,5 +1,5 @@ # Carte réseau -![Entre dans le réseau.](oredict:oc:lanCard) +![Entre dans le réseau.](oredict:opencomputers:lanCard) La carte réseau permet aux [ordinateurs](../general/computer.md) d'envoyer et de recevoir des messages réseau. Les messages (ou paquets) peuvent être diffusés à tous les noeuds de réception dans un sous-réseau, ou envoyés à un noeud spécifique avec une certaine adresse. Un [relai](../block/relay.md) peut être utilisé pour relier plusieurs sous-réseaux pour relayer des messages entre les sous-réseaux auxquels il est connecté. Il est également possible d'envoyer un message ciblé si le receveur est dans un autre sous-réseau, et si les réseaux sont connectés par un ou plusieurs [relais](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/leashUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/leashUpgrade.md index bf9b22a91a..52c87d60b4 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/leashUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/leashUpgrade.md @@ -1,5 +1,5 @@ # Amélioration de laisse -![-censuré- ~ Vexatos 2015](oredict:oc:leashUpgrade) +![-censuré- ~ Vexatos 2015](oredict:opencomputers:leashUpgrade) L'amélioration de laisse permet de mettre les animaux en laisse, attachés à l'entité contenue dans l'appareil qui héberge le composant, par exemple un [drone](drone.md). En utilisant cette amélioration, plusieurs animaux peuvent être attachés en même temps, ce qui la rend assez utile pour déplacer des troupeaux. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/linkedCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/linkedCard.md index 4095d496c4..0ccf61c6b6 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/linkedCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/linkedCard.md @@ -1,5 +1,5 @@ # Carte liée -![Je sens une connexion entre nous.](oredict:oc:linkedCard) +![Je sens une connexion entre nous.](oredict:opencomputers:linkedCard) La carte liée est une version spécialisée et avancée de la [carte réseau](lanCard.md). Elle peut seulement fonctionner par paires, en fournissant une communication en point à point entre les cartes appairées. Les cartes liées peuvent communiquer sur de longues (illimité) distances, et même à travers les dimensions. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/manual.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/manual.md index bba03165f5..71c156926a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/manual.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/manual.md @@ -1,6 +1,6 @@ # Manuel -![Une bonne lecture.](oredict:oc:manual) +![Une bonne lecture.](oredict:opencomputers:manual) Ce que vous êtes en train de lire en ce moment même ! Le manuel contient une mine d'informations à propos d'OpenComputers (et sûrement plus). Si vous avez besoin d'information sur un objet ou un bloc, ne cherchez pas plus loin ! Descendez pour apprendre comment l'utiliser (avec la souris ou la barre d'ascenseur sur la droite). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/microcontrollerCase1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/microcontrollerCase1.md index 438f776f09..fdf45d35b7 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/microcontrollerCase1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/microcontrollerCase1.md @@ -1,6 +1,6 @@ # Boitier de microcontrôleur -![Qu'il est mignon !](oredict:oc:microcontrollerCase1) +![Qu'il est mignon !](oredict:opencomputers:microcontrollerCase1) Le boitier de microcontrôleur est la base de construction des [microcontrôleurs](../block/microcontroller.md) grâce à l'[assembleur](../block/assembler.md). Les [microcontrôleurs](../block/microcontroller.md) sont des [ordinateurs](../general/computer.md) très primitifs. Ils peuvent seulement contenir une quantité très limitée de composants, et sont faits pour être avoir une utilisation très spécifique, comme la transformation de signaux de redstone, ou le traitement de messages réseaux. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/nanomachines.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/nanomachines.md index e283b9792d..4ad8e3e860 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/nanomachines.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/nanomachines.md @@ -1,6 +1,6 @@ # Nanomachines -![Nanomachines, son.](oredict:oc:nanomachines) +![Nanomachines, son.](oredict:opencomputers:nanomachines) Ces petits bonhommes créent une interface avec votre système nerveux pour vous faire aller toujours plus loin, toujours plus haut, toujours plus fort ! Ou vous tuer. Parfois les deux en même temps ! En clair, les nanomachines fournissent un système basé sur de l'énergie pour appliquer des effets (bons ou mauvais) sur le joueur dans lequel elles résident. Pour "installer" des nanomachines, mangez les ! diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/navigationUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/navigationUpgrade.md index eb13c10b99..c4195fa4e5 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/navigationUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/navigationUpgrade.md @@ -1,6 +1,6 @@ # Amélioration de navigation -![Je suis perdu. Encore.](oredict:oc:navigationUpgrade) +![Je suis perdu. Encore.](oredict:opencomputers:navigationUpgrade) L'amélioration de navigation fournit des informations de localisation et d'orientation aux appareils sur lesquels elle est installée. Les coordonnées que l'amélioration fournit sont relatives au centre de la carte qui a été utilisée pour fabriquer l'amélioration, et la portée fonctionnelle est basée sur la taille de la carte. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/numPad.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/numPad.md index 3112bfeb2d..32f3c24c86 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/numPad.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/numPad.md @@ -1,5 +1,5 @@ # Pavé numérique -![Vérifiez les empreintes, Jackson.](oredict:oc:materialNumPad) +![Vérifiez les empreintes, Jackson.](oredict:opencomputers:materialNumPad) Le pavé numérique est une partie de chaque [clavier](../block/keyboard.md). Il vous permet de saisir des nombres. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/pistonUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/pistonUpgrade.md index b4945e9f96..572aa83be2 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/pistonUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/pistonUpgrade.md @@ -1,6 +1,6 @@ # Amélioration de piston -![Push it.](oredict:oc:pistonUpgrade) +![Push it.](oredict:opencomputers:pistonUpgrade) L'amélioration de piston permet à certains appareils de se comporter comme un piston de base. Une fois installée, un composant avec une seule méthode, `push()`, devient disponible. Si elle est appelée, l'appareil essayera de pousser le bloc qui lui fait face. Pour les [robots](../block/robot.md) et les [microcontrôleurs](../block/microcontroller.md), c'est la face avant; pour les [tablettes](tablet.md), elle utilisera la face avant du joueur. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/printedCircuitBoard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/printedCircuitBoard.md index 03d035dceb..157d727b2a 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/printedCircuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/printedCircuitBoard.md @@ -1,5 +1,5 @@ # Circuit imprimé -![Il ne sort pas d'une imprimante.](oredict:oc:materialCircuitBoardPrinted) +![Il ne sort pas d'une imprimante.](oredict:opencomputers:materialCircuitBoardPrinted) Le circuit imprimé est, avec le [transistor](transistor.md), l'un des matériaux les plus basiques d'OpenComputers. Il est utilisé en tant que base pour beaucoup de composants, comme les [bases de carte](card.md) et un grand nombre de [blocs](../block/index.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/ram1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/ram1.md index 5dad065207..4752f69b17 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/ram1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/ram1.md @@ -1,6 +1,6 @@ # Mémoire -![Do you remember, dancing in September~](oredict:oc:ram1) +![Do you remember, dancing in September~](oredict:opencomputers:ram1) La mémoire est, au même titre que le [processeur](cpu1.md), un composant essentiel à chaque [ordinateur](../general/computer.md). En fonction de l'architecture du [processeur](cpu1.md), la mémoire a un effet important sur ce que l'[ordinateur](../general/computer.md) peut ou ne peut pas faire. Pour l'architecture Lua standard, par exemple, elle contrôle la quantité réelle de mémoire que les scripts Lua peuvent utiliser. Cela signifie que pour exécuter des programmes demandant plus de ressources, vous aurez besoin de plus de mémoire. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/rawCircuitBoard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/rawCircuitBoard.md index dc64cebe8b..efcc446f38 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/rawCircuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/rawCircuitBoard.md @@ -1,5 +1,5 @@ # Plaque de circuit imprimé brute -![Mais elle est très gentille, en fait.](oredict:oc:materialCircuitBoardRaw) +![Mais elle est très gentille, en fait.](oredict:opencomputers:materialCircuitBoardRaw) C'est un matériau de fabrication intermédiaire, utilisé pour fabriquer des [plaques de circuit imprimé](circuitBoard.md) (ou des [circuits imprimés](printedCircuitBoard.md), en fonction de l'ensemble de recettes utilisé). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/redstoneCard1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/redstoneCard1.md index 5f818f81b5..04b8f1d641 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/redstoneCard1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/redstoneCard1.md @@ -1,6 +1,6 @@ # Carte de Redstone -![Voir rouge.](oredict:oc:redstoneCard1) +![Voir rouge.](oredict:opencomputers:redstoneCard1) La carte de redstone permet aux [ordinateurs](../general/computer.md) de lire et émettre des signaux de redstone analogiques dans les blocs adjacents. Quand la force d'un signal entrant change, un signal est injecté dans l'[ordinateur](../general/computer.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/server1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/server1.md index e2b9e9100b..ccb737b181 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/server1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/server1.md @@ -1,6 +1,6 @@ # Serveur -![Ca te servira de leçon.](oredict:oc:server1) +![Ca te servira de leçon.](oredict:opencomputers:server1) Les serveurs sont une sorte d'[ordinateur](../general/computer.md) de haut niveau. Ils peuvent être configurés en les tenant en main et en les utilisant en même temps - comme pour ouvrir un sac à dos ou une Bourse du Néant (d'EnderStorage), par exemple. Après avoir inséré un [processeur](cpu1.md), de la [mémoire](ram1.md) et des cartes d'extension, le serveur doit être placé dans un [support de serveur](../block/serverRack.md). Pour plus d'informations, allez voir l'entrée sur le [support de serveur](../block/serverRack.md). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/signUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/signUpgrade.md index b886cb3259..930bc45632 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/signUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/signUpgrade.md @@ -1,5 +1,5 @@ # Amélioration de panneau d'E/S -![Je vois les signes sur le mur.](oredict:oc:signUpgrade) +![Je vois les signes sur le mur.](oredict:opencomputers:signUpgrade) Cette amélioration permet d'interagir avec les panneaux dans le monde. Elle permet de lire le message actuel sur le panneau, ainsi que de changer le message sur le panneau (si c'est autorisé). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/solarGeneratorUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/solarGeneratorUpgrade.md index 63d691f623..d5d2858b47 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/solarGeneratorUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/solarGeneratorUpgrade.md @@ -1,6 +1,6 @@ # Amélioration du générateur solaire -![Walking on the sun.](oredict:oc:solarGeneratorUpgrade) +![Walking on the sun.](oredict:opencomputers:solarGeneratorUpgrade) L'amélioration du générateur solaire peut être installée sur des appareils comme les [robots](../block/robot.md), les [drones](drone.md) et les [tablettes](tablet.md) pour générer passivement de l'énergie. Elle fonctionnera seulement si l'appareil est directement exposé à la lumière du soleil ; un appareil dans un endroit fermé ou une zone affectée par la météo ne génèrera pas d'énergie. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tabletCase1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tabletCase1.md index bba691e488..1b2e5e0bd3 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tabletCase1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tabletCase1.md @@ -1,6 +1,6 @@ # Boitier de tablette -![Il ne se plie pas.](oredict:oc:tabletCase1) +![Il ne se plie pas.](oredict:opencomputers:tabletCase1) Le boitier de tablette est la base de construction des [tablettes](tablet.md), grâce à l'[assembleur](../block/assembler.md). Les [tablettes](tablet.md) sont des [ordinateurs](../general/computer.md) portables très compacts. Elles peuvent accueillir un petit nombre d'améliorations, mais ne peuvent pas interagir avec le monde de la même manière qu'un [boitier d'ordinateur](../block/case1.md) (ce n'est pas aussi simple qu'avec une [carte réseau](lanCard.md) ou une [carte redstone](redstoneCard1.md) par exemple). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankControllerUpgrade.md index 6e115a0b24..076b8359dd 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankControllerUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankControllerUpgrade.md @@ -1,6 +1,6 @@ # Amélioration du contrôleur de réservoir -![Routage liquide.](oredict:oc:tankControllerUpgrade) +![Routage liquide.](oredict:opencomputers:tankControllerUpgrade) L'amélioration du contrôleur de réservoir est aux réservoirs de fluides ce que l'[amélioration de contrôleur d'inventaire](inventoryControllerUpgrade.md) est aux inventaires. Elle permet aux appareils d'obtenir plus d'informations sur les réservoirs internes et adjacents. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankUpgrade.md index 5af40ea890..af37cf6908 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tankUpgrade.md @@ -1,5 +1,5 @@ # Amélioration de réservoir -![Je suis un écureuil.](oredict:oc:tankUpgrade) +![Je suis un écureuil.](oredict:opencomputers:tankUpgrade) L'amélioration de réservoir permet aux appareils de stocker des liquides. Chaque réservoir peut contenir un seul type de liquide, et fournit un volume de 16 seaux (16000mB). Les [robots](../block/robot.md) et les [drones](drone.md) peuvent aspirer des liquides dans le monde et à partir d'autres réservoirs, et peuvent remplir les réservoirs externes, et si le liquide le supporte, ils peuvent également en placer dans le monde. Il n'y a pas de limite au nombre de réservoirs qui peuvent être installés sur un appareil. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/terminal.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/terminal.md index 8dc7db009f..a006484469 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/terminal.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/terminal.md @@ -1,6 +1,6 @@ # Terminal à distance -![Accès à distance.](oredict:oc:terminal) +![Accès à distance.](oredict:opencomputers:terminal) Le terminal à distance peut être utilisé pour contrôler à distance des [serveurs](server1.md). Pour l'utiliser, faites un clic droit en étant accroupi sur un serveur installé dans un [support de serveur](../block/serverRack.md) (cliquez sur le bloc du [support de serveur](../block/serverRack.md), en visant le [serveur](server1.md) pour y lier le terminal). diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/texturePicker.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/texturePicker.md index 0c1e341e1b..b6e9b87a1c 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/texturePicker.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/texturePicker.md @@ -1,6 +1,6 @@ # Ramasseur de texture -![Comment ça, c'est juste une re-texturation ?](oredict:oc:texturePicker) +![Comment ça, c'est juste une re-texturation ?](oredict:opencomputers:texturePicker) Le ramasseur de texture est très utile pour réaliser des modèles pour votre [imprimante 3D](../block/printer.md). Il vous permet de récupérer le nom de la texture utilisé par un bloc, simplement en l'utilisant (et en étant éventuellement accroupi) sur le bloc. Avertissement : pour les blocs qui utilisent un rendu spécial, comme les coffres, cela peut ne pas marcher. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tractorBeamUpgrade.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tractorBeamUpgrade.md index c82cea939f..939ad46422 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tractorBeamUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tractorBeamUpgrade.md @@ -1,5 +1,5 @@ # Amélioration du rayon tracteur -![Beam me up, Scotty !](oredict:oc:tractorBeamUpgrade) +![Beam me up, Scotty !](oredict:opencomputers:tractorBeamUpgrade) L'amélioration du rayon tracteur permet aux appareils de ramasser des objets dans un rayon de 3 blocs autour d'eux. Cela peut être très utile quand vous vous servez de [robots](../block/robot.md) dans un ferme à bois ou à quoi que ce soit d'autre, ou quand vous leur faites utiliser des outils qui cassent plusieurs blocs autour d'eux (comme les outils de Tinker's Construct). Chaque opération essayera d'aspirer une pile d'objets à portée, et consommera un peu d'énergie. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/transistor.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/transistor.md index 859c9d50d4..5da62abd92 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/transistor.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/transistor.md @@ -1,5 +1,5 @@ # Transistor -![Désolé, les blagues ne pas disponibles. Si vous souhaitez laisser un message, faites le 1.](oredict:oc:materialTransistor) +![Désolé, les blagues ne pas disponibles. Si vous souhaitez laisser un message, faites le 1.](oredict:opencomputers:materialTransistor) Le transistor est l'un des ingrédients pour la fabrication les plus basiques d'OpenComputers. Il est principalement utilisé pour fabriquer des [puces électroniques](chip1.md) et d'autres bidules électroniques. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/upgradeContainer1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/upgradeContainer1.md index 91ab378dc9..5bbdcb2719 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/upgradeContainer1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/upgradeContainer1.md @@ -1,5 +1,5 @@ # Amélioration de conteneur -![il peu avwar dé améliorassion !](oredict:oc:upgradeContainer1) +![il peu avwar dé améliorassion !](oredict:opencomputers:upgradeContainer1) L'amélioration de conteneur est une amélioration d'espace pour les [robots](../block/robot.md), qui fournit un emplacement dans les [robots](../block/robot.md) terminés dans lequel des améliorations classiques peuvent être insérées. Le niveau maximal des améliorations que cet emplacement peut contenir est égal au niveau du conteneur. Contrairement aux améliorations classiques, la complexité du conteneur est de deux fois son niveau. Allez voir la page des [robots](../block/robot.md) et de l'[assembleur](../block/assembler.md) pour plus d'informations sur la complexité. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/wlanCard1.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/wlanCard1.md index e6f6eefeea..ee041925bd 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/wlanCard1.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/wlanCard1.md @@ -1,6 +1,6 @@ # Carte de réseau sans-fil -![Peut provoquer le cancer. Ou pas.](oredict:oc:wlanCard2) +![Peut provoquer le cancer. Ou pas.](oredict:opencomputers:wlanCard2) La carte de réseau sans fil est une [carte réseau](lanCard.md) améliorée qui, en plus de gérer les messages réseaux filaires, peut également envoyer et recevoir des messages réseaux sans fil. La force du signal contrôle directement la distance jusqu'à laquelle un message peut être reçu, avec cette force étant égale à la distance en blocs. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/worldSensorCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/worldSensorCard.md index d0fd8a6bcb..320b85b247 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/worldSensorCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/worldSensorCard.md @@ -1,5 +1,5 @@ # Carte de capteur du monde -![Au mépris du danger... Aller là où aucun homme n'est jamais allé auparavant.](oredict:oc:worldSensorCard) +![Au mépris du danger... Aller là où aucun homme n'est jamais allé auparavant.](oredict:opencomputers:worldSensorCard) La carte de capteur du monde permet de lire les informations atmosphériques ainsi que la gravité des différentes planètes ajoutées par Galacticraft. Cela peut être utile pour les [robots](../block/robot.md) ou les [drones](drone.md) travaillant dans l'espace. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/wrench.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/wrench.md index 95b523841a..e4aa05a9c3 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/wrench.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/wrench.md @@ -1,5 +1,5 @@ # Crisseur -![Fait en Suisse.](oredict:oc:wrench) +![Fait en Suisse.](oredict:opencomputers:wrench) Un peu comme n'importe quel autre mod technologique, OpenComputers a sa propre version d'une clé anglaise. En l'occurrence, c'est un hybride entre un tournevis et une clé anglaise, ce qui a l'air d'être incroyablement compliqué à l'usage. Il peut être utilisé pour faire tourner la plupart des blocs, et il est également compatible avec les blocs de la plupart des autres mods qui réagissent aux outils de type clé. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/accessPoint.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/accessPoint.md index 6607601f95..1fea56379b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/accessPoint.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/accessPoint.md @@ -1,6 +1,6 @@ # Точка доступа -![AAA](oredict:oc:accessPoint) +![AAA](oredict:opencomputers:accessPoint) *Этот блок устарел и будет удален в следующих версиях.* Замените его на [ретранслятор](relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/adapter.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/adapter.md index 9e45e2a238..4d5d753809 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/adapter.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/adapter.md @@ -1,6 +1,6 @@ # Адаптер -![Теперь на 100% больше всего.](oredict:oc:adapter) +![Теперь на 100% больше всего.](oredict:opencomputers:adapter) Адаптеры позволяют [компьютерам](../general/computer.md) взаимодействовать с блоками Minecraft или из других модов. Поддерживаемые блоки, прилегающие к адаптеру, будут отображаться как компоненты [компьютера](../general/computer.md), подключенного к адаптеру. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/assembler.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/assembler.md index bc6a27b4a7..ff01b0e2d2 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/assembler.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/assembler.md @@ -1,6 +1,6 @@ # Сборщик роботов -![Harder, better, faster, stronger.](oredict:oc:assembler) +![Harder, better, faster, stronger.](oredict:opencomputers:assembler) Сборщик роботов - это продвинутая рабочая станция, позволяющая собирать такие сложные устройства, как [роботы](robot.md), [дроны](../item/drone.md) и [планшеты](../item/tablet.md). Они требуют для сборки большое количество энергии, поэтому рекомендуется использовать их совместно с [конденсатором энергии](capacitor.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/cable.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/cable.md index 3399bdb38a..9664410d50 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/cable.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/cable.md @@ -1,6 +1,6 @@ # Кабель -![Зелень.](oredict:oc:cable) +![Зелень.](oredict:opencomputers:cable) Кабель служит для соединения [компьютеров](../general/computer.md) и машин, которые далеки друг от друга. Если вы используете компактную сборку, где все компоненты касаются друг друга (напрямую или нет, большинство блоков ведут себя как кабели), вам они обычно не понадобятся. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/capacitor.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/capacitor.md index 38cd3a6fa7..b73f04f1a1 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/capacitor.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/capacitor.md @@ -1,6 +1,6 @@ # Конденсатор энергии -![Больше 9000.](oredict:oc:capacitor) +![Больше 9000.](oredict:opencomputers:capacitor) Конденсатор хранит энергию, используемую в сети, действуя в качестве буфера энергии, когда это необходимо. В отличие от конвертации энергии других модов во внутреннюю энергию OpenComputers (с помощью [конвертера энергии](powerConverter.md), к примеру), передача энергии внутри одной подсети мгновенна. Наличие энергобуфера полезно в случаях, когда необходимо большое количество энергии, например, для [сборки](assembler.md) или [зарядки](charger.md) [роботов](robot.md) или [дронов](../item/drone.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/case1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/case1.md index 34328b566b..10b34b30b5 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/case1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/case1.md @@ -1,6 +1,6 @@ # Системный блок -![Просто коробка.](oredict:oc:case1) +![Просто коробка.](oredict:opencomputers:case1) Системные блоки бывают 3 различных уровней, различающихся количеством компонентов, которые могут быть в них вставлены. Также есть креативный системный блок. Системные блоки могут быть помещены в [сборщик](assembler.md) для создания [робота](robot.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/chameliumBlock.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/chameliumBlock.md index d630d12432..d6a8e4f586 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/chameliumBlock.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/chameliumBlock.md @@ -1,6 +1,6 @@ # Блок хамелиума -![Такой... чистый.](oredict:oc:chameliumBlock) +![Такой... чистый.](oredict:opencomputers:chameliumBlock) Несколько кусочков [хамелиума](../item/chamelium.md) могут быть объединены для получения одноцветный блок — его можно использовать в качестве декорации. Блоки хамелиума могут быть окрашены в любой из 16 стандартных цветов Minecraft. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/charger.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/charger.md index fd5443d90f..7e9a833371 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/charger.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/charger.md @@ -1,6 +1,6 @@ # Зарядное устройство -![Отлично, давайте сделаем это.](oredict:oc:charger) +![Отлично, давайте сделаем это.](oredict:opencomputers:charger) Зарядное устройство предназначено для зарядки устройств, таких как [роботы](robot.md), [дроны](../item/drone.md) и [планшеты](../item/tablet.md). Зарядное устройство активируется редстоун-сигналом. Скорость заряда зависит от силы редстоун-сигнала: так, при силе 15, скорость заряда будет 100%. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/disassembler.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/disassembler.md index 1984b95c79..ed7ed9862b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/disassembler.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/disassembler.md @@ -1,6 +1,6 @@ # Разборщик -![Построй это, снеси это.](oredict:oc:disassembler) +![Построй это, снеси это.](oredict:opencomputers:disassembler) Разборщик используется, чтобы разбирать большинство блоков OpenComputers на исходные компоненты. Это удобно для сборки новых деталей из частей старых и ненужных, а таже для разборки устройств, которые вам больше не нужны или были собраны неправильно (например, [роботы](robot.md) без [операционной системы](../general/openOS.md)). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/diskDrive.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/diskDrive.md index c87b2c411b..31da6f0614 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/diskDrive.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/diskDrive.md @@ -1,6 +1,6 @@ # Дисковод -![Ходим вокруг да около...](oredict:oc:diskDrive) +![Ходим вокруг да около...](oredict:opencomputers:diskDrive) Дисковод может быть использован для чтения [дискет](../item/floppy.md) при работе с подключённым к нему [компьютером](../general/computer.md). Это удобно на начальных этапах, потому что низкоуровневые [системные блоки](case1.md) не имеют встроенного дисковода, а вам нужна будет операционная система для запуска [компьютера](../general/computer.md). Дискета с [OpenOS](../general/openOS.md) может быть создана из пустой [дискеты](../item/floppy.md) и [данного руководства](../item/manual.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/geolyzer.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/geolyzer.md index cb8197fed2..da25f247fc 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/geolyzer.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/geolyzer.md @@ -1,6 +1,6 @@ # Геоанализатор -![Это камень.](oredict:oc:geolyzer) +![Это камень.](oredict:opencomputers:geolyzer) Геоанализатор используется [компьютерами](../general/computer.md) для сканирования плотностей блоков вокруг него. Это можно использовать для генерации карт местности и показа их на [голографическом проекторе](hologram1.md) или для обнаружения полезных блоков (руды обычно тверже земли и камня). В результаты работы анализатора добавляется некоторое количество шума. Теоретически, можно провести несколько сканирований, чтобы получить более точные результаты. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/hologram1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/hologram1.md index b15b4e2a38..987c940fda 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/hologram1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/hologram1.md @@ -1,6 +1,6 @@ # Голографический проектор -![Это реально? Или это только ваше воображение?](oredict:oc:hologram1) +![Это реально? Или это только ваше воображение?](oredict:opencomputers:hologram1) Голографический проектор отображает трёхмерный массив вокселей, каждый из которых может быть включен или выключен с помощью подключенного [компьютера](../general/computer.md). Проектор второго уровня имеет такое же разрешение, что и у первого уровня, но может окрасить воксели в один из трёх указанных пользователем цветов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/keyboard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/keyboard.md index a733d77cff..2859bc8a49 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/keyboard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/keyboard.md @@ -1,6 +1,6 @@ # Клавиатура -![QWERTY](oredict:oc:keyboard) +![QWERTY](oredict:opencomputers:keyboard) Клавиатура необходима для набора текста на [мониторе](screen1.md), и могут стоять отдельным блоком в мире или быть встроены в такие устройства, как [роботы](robot.md) или [планшеты](../item/tablet.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/motionSensor.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/motionSensor.md index 91a1412f66..0b70f29741 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/motionSensor.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/motionSensor.md @@ -1,6 +1,6 @@ # Датчик движения -![Не двигайся.](oredict:oc:motionSensor) +![Не двигайся.](oredict:opencomputers:motionSensor) Датчик движения позволяет [компьютерам](../general/computer.md) замечать движение живых существ. Если существо двигается быстрее, чем заданный порог чувствительности, то будет подан сигнал в [компьютер](../general/computer.md), соединенный с датчиком движения. Вы можете задать порог срабатывания датчика через API, доступный компьютерам. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/netSplitter.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/netSplitter.md index ae3b43a3db..1ce21141bd 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/netSplitter.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/netSplitter.md @@ -1,6 +1,6 @@ # Сетевой переключатель -![*.net *.split](oredict:oc:netSplitter) +![*.net *.split](oredict:opencomputers:netSplitter) Сетевой переключатель позволяет контролировать соединение между подсетями. В отличие от [ретранслятора](relay.md) или [конвертера энергии](powerConverter.md), позволяет непосредственно соединить подсети, делая при этом доступными все компоненты. Соединение каждой стороны переключается [ключом](../item/wrench.md). При подаче сигнала красного камня все соединения инвертируются. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerConverter.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerConverter.md index c284b3233c..00a120f6f6 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerConverter.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerConverter.md @@ -1,5 +1,5 @@ # Конвертер энергии -![Один из нас? Один из нас!](oredict:oc:powerConverter) +![Один из нас? Один из нас!](oredict:opencomputers:powerConverter) Конвертер энергии - это самый быстрый способ преобразования энергии других модов во внутреннюю энергию OpenComputers. Если вы запустите простой компьютер, вероятно, вам не понадобится конвертер. Если у вас есть большой конденсатор, который разражается только время от времени, вам тоже он может не понадобиться. Однако если вы хотите подвести энергию к [сборщику](assembler.md) или [зарядному устройству](charger.md), хорошей идеей будет подключить их через конвертер, чем напрямую. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerDistributor.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerDistributor.md index 23c2b550cd..4e344d6d7c 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerDistributor.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/powerDistributor.md @@ -1,5 +1,5 @@ # Распределитель энергии -![Энергию в массы.](oredict:oc:powerDistributor) +![Энергию в массы.](oredict:opencomputers:powerDistributor) Распределитель энергии распределяет энергию в хранилищах (таких как [конденсатор](capacitor.md)) между подсетями, что позволяет им обмениваться энергией без подключения компонентов к омьпютерам других подсетей. Это достигается "балансированием" энергии во всех подключенных подсетях, поддерживая одинаковым *относительное* количество энергии в них. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/printer.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/printer.md index 93c423eb60..7dba3c37d7 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/printer.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/printer.md @@ -1,6 +1,6 @@ # 3D принтер -![2D печать - прошлый век.](oredict:oc:printer) +![2D печать - прошлый век.](oredict:opencomputers:printer) 3D принтеры позвляют распечатать любой блок любой формы и текстуры. Для начала работы с 3D принтерами вы должны поместить блок 3D принтера рядом с компьютером. Это предоставит доступ к API компонента `printer3d`, позволяющего создавать и печатать [3D модели](print.md) с использованием предоставленные функции. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/rack.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/rack.md index e0b4be02ce..50de71c993 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/rack.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/rack.md @@ -1,6 +1,6 @@ # Стойка -![Бесплатное жилье.](oredict:oc:rack) +![Бесплатное жилье.](oredict:opencomputers:rack) Стойка может содержать до 4 подключемых устройств, как: [серверы](../item/server1.md), [серверы терминалов](../item/terminalServer.md) и [подключаемые дисководы](../item/diskDriveMountable.md). Соединение между монтируемыми устройствами может быть настроено с помощью интерфейса стойки. В частности, если [серверы](../item/server1.md) содержат компоненты, которые поддерживают работу с сетью, такие как [сетевые карты](../item/lanCard.md), то можно установить стороны сетевых соединений. Такие соединения будут служить только для передачи сетевых сообщений, компоненты через них доступны не будут. Они отличаются более тонкой линией, чем у "основных", которые предоставляют доступ к компонентам. Каждое внутреннее соединение должно быть между монтируемым устройством (или компонентом в нем) и шиной, соединенной со стороной стойки. Для соеднинения вместе нескольких подключаемых устройств подключите их на одну общую шину. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/raid.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/raid.md index cb4fd9dc70..00ff822665 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/raid.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/raid.md @@ -1,6 +1,6 @@ # RAID -![40 человек в 1.](oredict:oc:raid) +![40 человек в 1.](oredict:opencomputers:raid) Блок RAID может содержать 3 [жестких диска](../item/hdd1.md), объединенных в единую файловую систему. Объединённая файловая система имеет размер, равный сумме объемов памяти отдельных [жестких дисков](../item/hdd1.md), и доступна всех [компьютеров](../general/computer.md), подключенных к RAID. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/redstone.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/redstone.md index 73e2ce0c7c..3edc00160c 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/redstone.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/redstone.md @@ -1,6 +1,6 @@ # Красный контроллер -![Привет Ред.](oredict:oc:redstone) +![Привет Ред.](oredict:opencomputers:redstone) Красный контроллер используется для считывания и подачи сигнала красного камня. Он ведет себя как гибрид [плат на красном камне](../item/redstoneCard1.md) 1 и 2 уровней: может работать с обычными аналоговыми сигналами и с многожильными кабелями, но не может считывать и подавать беспроводные сигналы красного камня. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/relay.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/relay.md index 0645ca5229..e7f4b1118f 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/relay.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/relay.md @@ -1,6 +1,6 @@ # Ретранслятор -![Строит мосты.](oredict:oc:relay) +![Строит мосты.](oredict:opencomputers:relay) Ретранслятор используется для передачи сообщений между несколькими подсетями без предоставления компонентов [компьютерам](../general/computer.md) в других подсетях. Как правило, изоляция компонентов - хорошая идея, чтобы не позволить [компьютерам](../general/computer.md) использовать не тот [монитор](screen1.md) или избежать достижения лимита компонентов (в результате чего [компьютеры](../general/computer.md) выключатся и отказываются загружаться). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/screen1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/screen1.md index 7d57166600..39456ae687 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/screen1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/screen1.md @@ -1,6 +1,6 @@ # Монитор -![Вы тоже видите это?](oredict:oc:screen1) +![Вы тоже видите это?](oredict:opencomputers:screen1) Монитор используется совместно с [видеокартой](../item/graphicsCard1.md), чтобы позволить [компьютеру](../general/computer.md) отображатьа текст. Разные уровни мониторов имеют разные возможности, такие как разное разрешение и глубина цвета. Они варьируются от монохромных мониторов низкого разрешения до мониторы высокого разрешения, поддерживающих 256 цветов соответственно. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/switch.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/switch.md index 559d2f3ae2..5e3b8ca1ba 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/switch.md @@ -1,6 +1,6 @@ # Коммутатор -![Строит мосты.](oredict:oc:switch) +![Строит мосты.](oredict:opencomputers:switch) *Этот блок устарел и будет удален в следующих версиях.* Замените его на [ретранслятор](relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/transposer.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/transposer.md index 9ebae6c138..b4e2441175 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/transposer.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/transposer.md @@ -1,6 +1,6 @@ # Транспозер -![Такой позер.](oredict:oc:transposer) +![Такой позер.](oredict:opencomputers:transposer) Транспозер заполняет пробел между воронками, контролируемыми сигналом красного камня, и [роботами](robot.md), позволяя [компьютерам](../general/computer.md) контролировать перемещение предметов и жидкостей между соседними блоками. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/ru_ru/block/waypoint.md index 013f7f9057..4bcfa00a3d 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/block/waypoint.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/block/waypoint.md @@ -1,6 +1,6 @@ # Путевая точка -!["Сюда!" - "Нет, туда!"](oredict:oc:waypoint) +!["Сюда!" - "Нет, туда!"](oredict:opencomputers:waypoint) [Навигационное улучшение](../item/navigationUpgrade.md) может обнаруживать путевые точки, что позволяет устройствам с этим улучшением ориентироваться в игровом мире. Это можно использовать для написания программ для [роботов](robot.md) и [дронов](../item/drone.md), которые просто могут быть переиспользованы. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md b/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md index 6804e5b741..29cf185efc 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md @@ -4,13 +4,13 @@ ![Это всплывающее сообщение...](opencomputers:textures/gui/printer_ink.png) ![Это всплывающее сообщение...](opencomputers:/textures/gui/printer_material.png) *Это* текст *курсивом*, ~~зачеркнутый~~, может быть, **немного** текста **жирным**. Это _подчеркнутый текст_? А, нет, _снова курсив!_ Хорошо, вот [ссылка](../index.md). -![Это рендирится напрямую.](oredict:oc:assembler) +![Это рендирится напрямую.](oredict:opencomputers:assembler) ## Заголовок второго уровня [снова со *ссылкой*, но __немного__ длиннее](../block/adapter.md) -![Это еще одно всплывающее сообщение.](item:OpenComputers:item@23) +![Это еще одно всплывающее сообщение.](item:opencomputers:item@23) какой-то текст прямо над изображением для тестирования отступов -![Все цвета.](oredict:craftingPiston) +![Все цвета.](oredict:forge/piston) какой-то текст сразу под изображением для тестирования отступов Это *две @@ -42,9 +42,9 @@ asdasd ![о нет, рекурсия!](img/example.png) qweqwe И наконец, [это ссылка!](https://avatars1.githubusercontent.com/u/514903). -![Поврежденное изображение](item:повреждено) -![Поврежденное изображение](block:повреждено) -![Поврежденное изображение](oredict:повреждено) +![Поврежденное изображение](item:this_is_broken) +![Поврежденное изображение](block:this_is_broken) +![Поврежденное изображение](oredict:this_is_broken) тест переносов 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 @@ -53,17 +53,17 @@ asdasd ![о нет, рекурсия!](img/example.png) qweqwe * 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 - `123456789012345678901234567890.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890` -Это тест![](oredict:oc:cpu1) картинки в тексте kakakakalalsd 123 as +Это тест![](oredict:opencomputers:cpu1) картинки в тексте kakakakalalsd 123 as -Это один тест![](oredict:oc:cpu1) +Это один тест![](oredict:opencomputers:cpu1) картинки, после которой переход на следующую строку Это один тест -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) изображения между двумя пустыми строками Тест -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) картинками между двумя пустыми строками diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/general/quickstart.md b/src/main/resources/assets/opencomputers/doc/ru_ru/general/quickstart.md index 949024b1c5..2ff4e8499c 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/general/quickstart.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/general/quickstart.md @@ -6,7 +6,7 @@ Вначале вам понадобится [системный блок](../block/case1.md). Этот блок будет содержать все компоненты, определяющие поведение собираемого компьютера. -![Системный блок второго уровня.](oredict:oc:case2) +![Системный блок второго уровня.](oredict:opencomputers:case2) Например, вам потребуется выбрать, какого уровня [видеокарту](../item/graphicsCard1.md) вы будете использовать, нужна ли вам [сетевая](../item/lanCard.md) карта, [плата на красном камне](../item/redstoneCard1.md) или даже [карта отладки](../item/debugCard.md), если вы играете в творческом режиме. @@ -37,7 +37,7 @@ Он ожил! В любом случае, должен был. Если этого не произошло, значит, что-то пошло не так, и нужно искать ошибку с помощью [анализатора](../item/analyzer.md). Но предпологая, что компьютер заработал, вы почти закончили. Самая сложная часть позади. Теперь осталось заставить его принимать ввод и выводить какой-то результат. Чтобы [компьютер](computer.md) мог выводить информацию, вы должны установить [монитор](../block/screen1.md) и [видеокарту](../item/graphicsCard1.md). -![Нет, это не плоский экран.](oredict:oc:screen2) +![Нет, это не плоский экран.](oredict:opencomputers:screen2) Установите [монитор](../block/screen1.md) рядом с [системным блоком](../block/case1.md) или, опять же, подключите его с помощью [кабеля](../block/cable.md). После чего установите [видеокарту](../item/graphicsCard1.md) по вашему выбору в [системный блок](../block/case1.md). Теперь вы должны увидеть мигающий курсор на [мониторе](../block/screen1.md). Наконец, поставьте [клавиатуру](../block/keyboard.md) перед [монитором](../block/screen1.md) или на одну из его сторон, чтобы получить возможность вводить текст с [клавиатуры](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/abstractBusCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/abstractBusCard.md index b1cf6f50ca..a924e6e4af 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/abstractBusCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/abstractBusCard.md @@ -1,5 +1,5 @@ # Карта абстрактной шины -![Нужно больше сетей!](oredict:oc:abstractBusCard) +![Нужно больше сетей!](oredict:opencomputers:abstractBusCard) Карта позволяет [компьютерам](../general/computer.md), [серверам](server1.md) и [роботам](../block/robot.md) взаимодействовать с абстрактной шиной из мода StargateTech2. При установке карты устройства подключаются к абстрактной шине и получают доступ к компонента, который позволяет передавать сообщения через шину. Входящие сообщения шины конвертируются в сигналы, которые получают устройства. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/acid.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/acid.md index 52a2ebf757..d18b613fb8 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/acid.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/acid.md @@ -1,6 +1,6 @@ # Кислота -![Отлив?](oredict:oc:materialAcid) +![Отлив?](oredict:opencomputers:materialAcid) Это вкусное [нет авторитетного источника] варево можно употребить, когда вам когда-либо вдруг захочется немного... веселья. Или пищевого отравления. Или и того и другого. Также может служить ингредиентом в других, более полезных вещах. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/alu.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/alu.md index 82b3ce1769..8d84c5b309 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/alu.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/alu.md @@ -1,5 +1,5 @@ # Арифметико-логическое устройство -![Я могу считать!](oredict:oc:materialALU) +![Я могу считать!](oredict:opencomputers:materialALU) Используется для крафта компонентов, выполняющие вычисления, например, [процессоров](cpu1.md) и [видеокарт](graphicsCard1.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/analyzer.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/analyzer.md index bcee402e6c..bc4e41602d 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/analyzer.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/analyzer.md @@ -1,6 +1,6 @@ # Анализатор -![Что же это такое.](oredict:oc:analyzer) +![Что же это такое.](oredict:opencomputers:analyzer) Анализатор - это ручной инструмент, предназначенный для получения информации о блоках OpenComputers. Просто нажмите по блоку (приседая, если требуется), и информация будет выведена в чат. В зависимости от блока вы можете получить от адреса компонента до уровня энергии в подсети, в которой находится блок, и, например, ошибки, приведшей к сбою [компьютера](../general/computer.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/angelUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/angelUpgrade.md index 81eacb3c64..d8ab8444e2 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/angelUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/angelUpgrade.md @@ -1,5 +1,5 @@ # "Ангельское" улучешение -![Аллилуйя](oredict:oc:angelUpgrade) +![Аллилуйя](oredict:opencomputers:angelUpgrade) Данное улучшение позволяет [роботам](../block/robot.md) ставить блоки прямо в воздухе, без соседнего блока. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/apu1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/apu1.md index ee5d488bd2..7cab16580c 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/apu1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/apu1.md @@ -1,6 +1,6 @@ # Процессор с видеокартой (APU) -![Awesomest Probability Unifier.](oredict:oc:apu1) +![Awesomest Probability Unifier.](oredict:opencomputers:apu1) Это результат объединения [процессора](cpu1.md) и [видеокарты](graphicsCard1.md). Позволяет вам освободить один слот для карт в устройстве. Как и обычный процессор, он определяет архитектуру [компьютера](../general/computer.md) и максимальное количество компонентов, которые могут быть подключены к [компьютеру](../general/computer.md). Помимо этого, он предоставляет функции видеокарты. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/arrowKeys.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/arrowKeys.md index bc2f5434c3..33f8de7455 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/arrowKeys.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/arrowKeys.md @@ -1,5 +1,5 @@ # Клавиши со стрелками -![Будьте благодарны, что они делаются не из стрел.](oredict:oc:materialArrowKey) +![Будьте благодарны, что они делаются не из стрел.](oredict:opencomputers:materialArrowKey) Это необходимый элемент для крафта [клавиатуры](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/batteryUpgrade1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/batteryUpgrade1.md index 4d8aef90be..a53368c876 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/batteryUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/batteryUpgrade1.md @@ -1,5 +1,5 @@ # Улучшение "Ёмкость" -![Сделано из металла.](oredict:oc:batteryUpgrade1) +![Сделано из металла.](oredict:opencomputers:batteryUpgrade1) Данное улучшение увеличивает емкость внутреннего энергохранилища, что позволяет [роботам](../block/robot.md) и [планшетам](tablet.md) работать гораздо дольше без [зарядки](../block/charger.md). Чем выше уровень улучешния, тем больше энергии он может хранить. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/buttonGroup.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/buttonGroup.md index 0774888dfc..442053f360 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/buttonGroup.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/buttonGroup.md @@ -1,5 +1,5 @@ # Группа кнопок -![Нужно больше кнопок.](oredict:oc:materialButtonGroup) +![Нужно больше кнопок.](oredict:opencomputers:materialButtonGroup) Необходимый компонент для крафта [клавиатуры](../block/keyboard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/card.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/card.md index d24d9c28dc..7e714e929b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/card.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/card.md @@ -1,5 +1,5 @@ # Карта -![Не может быть считана.](oredict:oc:materialCard) +![Не может быть считана.](oredict:opencomputers:materialCard) Обычный компонент для крафта карт в OpenComputers (например, [видеокарт](graphicsCard1.md), [сетевых карт](lanCard.md) и т. д.). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cardContainer1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cardContainer1.md index e3d4afb361..2ac73a976a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cardContainer1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cardContainer1.md @@ -1,5 +1,5 @@ # Контейнер для карт -![Может хранить карты!](oredict:oc:cardContainer1) +![Может хранить карты!](oredict:opencomputers:cardContainer1) Контейнер для карт - улучшение-контейнер для [роботов](../block/robot.md), позволящее вставлять и извлекать карты из [роботов](../block/robot.md) без пересборки. Максимальный уровень карты равен уровню контейнера. В отличие от обычных улучшений, требуемая сложность сборки двойная. Подробнее о сложности сборки смотрите [здесь](../block/robot.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chamelium.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chamelium.md index f3fcb6d078..fcbb0aa6af 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chamelium.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chamelium.md @@ -1,6 +1,6 @@ # Хамелиум -![От слова "хамелеон", на всякий случай.](oredict:oc:chamelium) +![От слова "хамелеон", на всякий случай.](oredict:opencomputers:chamelium) Хамелиум - основной материал, используемый для создания [3D моделей](../block/print.md) на [3D принтерах](../block/printer.md). Сам по себе он однороден, а потому очень полезен для создания простых гладких одноцветных поверхностей. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chip1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chip1.md index 9b74bd5493..433547a3c5 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chip1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chip1.md @@ -1,5 +1,5 @@ # Микрочипы -![Несъедобные.](oredict:oc:circuitChip1) +![Несъедобные.](oredict:opencomputers:circuitChip1) Микрочипы - основа всех электронных компонентов в OpenComputers. Они имеют различные уровни для крафта компонентов разных уровней. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chunkloaderUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chunkloaderUpgrade.md index 37153d017d..b18e991646 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/chunkloaderUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/chunkloaderUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Загрузчик чанков" -![Никуда не денется.](oredict:oc:chunkloaderUpgrade) +![Никуда не денется.](oredict:opencomputers:chunkloaderUpgrade) Это улучшение может быть установлено в устройства (например, в [роботов](../block/robot.md) и [микроконтроллеры](../block/microcontroller.md)), чтобы они могли загружать чанки, в которых они находятся, а также соседние чанки. Однако на это требуется энергия. Загрузчик чанков может быть включен или выключен через API компонента. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/circuitBoard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/circuitBoard.md index 8dd3874677..66a0fc8962 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/circuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/circuitBoard.md @@ -1,5 +1,5 @@ # Нетравленая печатная плата -![Нужно больше золота.](oredict:oc:materialCircuitBoard) +![Нужно больше золота.](oredict:opencomputers:materialCircuitBoard) Промежуточный предмет, сделанный из [основы для печатной платы](rawCircuitBoard.md). Используется для создания [печатной платы](printedCircuitBoard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/componentBus1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/componentBus1.md index d3d38c6d15..9e1eb5e392 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/componentBus1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/componentBus1.md @@ -1,6 +1,6 @@ # Компонентая шина -![Мне нужно боооооольше.](oredict:oc:componentBus1) +![Мне нужно боооооольше.](oredict:opencomputers:componentBus1) Компонентная шина - это [серверное](server1.md) улучшение, которое позволяет подключать к [серверу](server1.md) большее количество компонентов. Как и с [процессорами](cpu1.md), чем выше уровень шины, тем большее число компонентов можно подключить. Кроме того, чем выше уровень [сервера](server1.md), тем больше компонентных шин вы сможете вставить, что позволяет работать с еще большим числом компонентов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/controlUnit.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/controlUnit.md index 46ca8cd031..b0b1cdeffb 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/controlUnit.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/controlUnit.md @@ -1,5 +1,5 @@ # Блок управления -![Теперь со встроенным круиз-контролем.](oredict:oc:materialCU) +![Теперь со встроенным круиз-контролем.](oredict:opencomputers:materialCU) Предмет, используемый при создании более продвинутых схем, например [процессоров](cpu1.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cpu1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cpu1.md index 566305e3cc..4d612bfdb3 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cpu1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cpu1.md @@ -1,6 +1,6 @@ # Процессор -![Мозги...](oredict:oc:cpu1) +![Мозги...](oredict:opencomputers:cpu1) Основа всех [компьютеров](../general/computer.md) и [серверов](server1.md). Определяет архитектуру [компьютера](../general/computer.md) и максимальное число компонентов, которые могут быть к нему подключены. Чем выше уровень процессора, тем больше [компьютер](../general/computer.md) может вызывать прямых методов в тик - проще говоря, чем лучше процессор, тем быстрее он работает. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/craftingUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/craftingUpgrade.md index c8fcab4001..edb8f1d583 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/craftingUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/craftingUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Верстак" -![Скрафти это.](oredict:oc:craftingUpgrade) +![Скрафти это.](oredict:opencomputers:craftingUpgrade) Это улучшение позволяет [роботам](../block/robot.md) крафтить предметы с помощью предметов из своего [инвентаря](../item/inventoryUpgrade.md). Для крафта используется сетка слотов 3x3 в левом верхнем углу [инвентаря](../item/inventoryUpgrade.md) [робота](../block/robot.md). Предметы должны быть расположены в соответствии с рецептом. Результаты крафта будут помещены в [инвентарь](../item/inventoryUpgrade.md) [робота](../block/robot.md) — в выбранный слот; если же он заполнен, тогда используется следующий слот. В случае если место в инвентаре закончилось, результаты крафта будут выброшены в мир. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cuttingWire.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cuttingWire.md index cb749af497..046220597d 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/cuttingWire.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/cuttingWire.md @@ -1,5 +1,5 @@ # Проволока -![Не гаррота. Честно.](oredict:oc:materialCuttingWire) +![Не гаррота. Честно.](oredict:opencomputers:materialCuttingWire) Если включены сложные рецепты, нужна для создания [основы для печатной платы](rawCircuitBoard.md) - очень неэффективного. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/dataCard1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/dataCard1.md index 5c68b6aec5..2d5c539c22 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/dataCard1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/dataCard1.md @@ -1,6 +1,6 @@ # Карта данных -![Вопреки распространенному поверью, не хранит данные.](oredict:oc:dataCard1) +![Вопреки распространенному поверью, не хранит данные.](oredict:opencomputers:dataCard1) Карта данных предоставляет несколько алгоритмов, которые труднореализуемы или работают медленнее на архитектуре компьютеров, например хэширование и inflate/deflate. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/databaseUpgrade1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/databaseUpgrade1.md index 79a1149633..4ab094448d 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/databaseUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/databaseUpgrade1.md @@ -1,6 +1,6 @@ # Улучшение "База данных" -![Living in the database.](oredict:oc:databaseUpgrade1) +![Living in the database.](oredict:opencomputers:databaseUpgrade1) Улучшение может быть настроено на хранение списка предметов, которые могут быть использованы другими компонентами. Это особенно полезно для элементов, отличающиеся только NBT-данными, которые не возвращаются методами компонентов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/disk.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/disk.md index 6241df1ba8..90bbb20ac8 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/disk.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/disk.md @@ -1,5 +1,5 @@ # Диск -![Discworld. Памяти Терри Пратчетта.](oredict:oc:materialDisk) +![Discworld. Памяти Терри Пратчетта.](oredict:opencomputers:materialDisk) Базовый компонент для создания устройств хранения информации, таких как [дискеты](floppy.md) и [жесткие диски](hdd1.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/diskDriveMountable.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/diskDriveMountable.md index b9bedfd6dc..2624b86a04 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/diskDriveMountable.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/diskDriveMountable.md @@ -1,5 +1,5 @@ # Дисковод для серверной стойки -![Snuggly](oredict:oc:diskDriveMountable) +![Snuggly](oredict:opencomputers:diskDriveMountable) Аналог обычного [дисковода](../block/diskDrive.md), за исключением того, что он установливается в [стойку](../block/rack.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/droneCase1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/droneCase1.md index 5c2eaa7e9b..205d25973a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/droneCase1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/droneCase1.md @@ -1,6 +1,6 @@ # Корпус дрона -![Полетели.](oredict:oc:droneCase1) +![Полетели.](oredict:opencomputers:droneCase1) Корпус дрона используется для создания [дронов](drone.md) в [сборщике](../block/assembler.md). [Дроны](drone.md) - легковесные, быстрые и очень мобильные устройства с ограниченной функциональностью (доступно меньше слотов для компонентов и улучешний). В отличие от [роботов](../block/robot.md), они не могут использовать инструменты и взаимодействуют с игровым миром ограниченно. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/eeprom.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/eeprom.md index 5401ec2098..912bb35274 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/eeprom.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/eeprom.md @@ -1,6 +1,6 @@ # EEPROM -![Начнем вечеринку.](oredict:oc:eeprom) +![Начнем вечеринку.](oredict:opencomputers:eeprom) EEPROM содержит код, который позволяет загрузить компьютер. Эти данные хранятся в виде простого массива байтов и могут означать разные вещи на [процессорах](cpu1.md) разных архитектур. Например, для Lua это обычно маленький скрипт, который ищет сценарий инициализации в файловой системе, но для других архитектур это может быть сам машинный код. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/experienceUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/experienceUpgrade.md index 86bdcba68e..0a461192ef 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/experienceUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/experienceUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Опыт" -![Почувствуй силу.](oredict:oc:experienceUpgrade) +![Почувствуй силу.](oredict:opencomputers:experienceUpgrade) Данное улучшение особенное, оно позволяет [роботам](../block/robot.md) и [дронам](drone.md) получать опыт за выполнение различных действий, таких как добыча руд и убийство животных. Одно улучшение может хранить до 30 уровней опыта, предоставляя различные бонусы с каждым уровнем, включая увеличение скорости ломания блоков или увеличения энергохранилища. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/floppy.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/floppy.md index 47fec655ff..96a6e234e9 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/floppy.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/floppy.md @@ -1,5 +1,5 @@ # Дискета -![Много дюймов.](oredict:oc:floppy) +![Много дюймов.](oredict:opencomputers:floppy) Дискета - это самое простое и дешевое средство хранения информации в OpenComputers, удобное на начальных этапах для переноса программ между [компьютерами](../general/computer.md) и [роботами](../block/robot.md). Вы также можете найти дискеты с программами в сокровищницах. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/generatorUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/generatorUpgrade.md index f25426218d..774a290ca6 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/generatorUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/generatorUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Генератор" -![Генератор X.](oredict:oc:generatorUpgrade) +![Генератор X.](oredict:opencomputers:generatorUpgrade) Улучшение позволяет устройствам самозаряжаться за счёт твердого топлива, например угля. Внутренний инвентарь генератора может хранить один стак топлива. Топливо может быть убрано из генератора с помощью метода API компонента. Если вынуть генератор из [робота](../block/robot.md), его содержимое выпадает в игровой мир. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/graphicsCard1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/graphicsCard1.md index 16321f466d..494bc42212 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/graphicsCard1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/graphicsCard1.md @@ -1,6 +1,6 @@ # Видеокарта -![Красивые картинки.](oredict:oc:graphicsCard1) +![Красивые картинки.](oredict:opencomputers:graphicsCard1) Видеокарта является неотъемлемой частью большинства [компьютеров](../general/computer.md) и позволяет им выводить изображение на подключенный [монитор](../block/screen1.md). Видеокарты имеют несколько уровней, как и [мониторы](../block/screen1.md), и в зависимости от него поддерживают различные разрешения и глубину цвета. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hdd1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hdd1.md index d1696e3da3..45e9f028ae 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hdd1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hdd1.md @@ -1,5 +1,5 @@ # Жесткий диск -![Spaaaace.](oredict:oc:hdd1) +![Spaaaace.](oredict:opencomputers:hdd1) Жесткие диски - это продвинутое хранилище информации в OpenComputers. Все виды жестких дисков имеют одинаковую скорость работы, но разную емкость. Некоторые устройства могут использовать только жесткие диски (хотя серверы могут, например, использовать внешний [дисковод](../block/diskDrive.md)). Жесткие диски могут быть объединены в [RAID](../block/raid.md), благодаря чему несколько дисков будут работать как один большой. При объединении жестких дисков в [RAID](../block/raid.md) вся информация на них будет стерта. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverBoots.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverBoots.md index 075ed791d6..8ae7c92d0b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverBoots.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverBoots.md @@ -1,6 +1,6 @@ # Парящие ботинки -![Быстрее, выше, сильнее.](oredict:oc:hoverBoots) +![Быстрее, выше, сильнее.](oredict:opencomputers:hoverBoots) Если не хочется программировать [дронов](drone.md), то их можно использовать как роликовые коньки. Что-то вроде такого. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverUpgrade1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverUpgrade1.md index e3b3491998..044f90bf91 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverUpgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/hoverUpgrade1.md @@ -1,6 +1,6 @@ # Улучшение "Парение" -![Легкое, как перышко.](oredict:oc:hoverUpgrade1) +![Легкое, как перышко.](oredict:opencomputers:hoverUpgrade1) Это улучшение позволяет [роботам](../block/robot.md) летать гораздо выше над землёю, чем обычно. В отличие от [дронов](drone.md), максимальная высота их полета ограничена 8 блоками по умолчанию. Обычно это не доставляет больших проблем, потому что они все также могут двигаться по стенам и просто вверх. Правила их передвижения могут быть сведены к следующему: - Робот будет двигаться при условии, что или стартовая, или конечная точки являются разрешёнными. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inkCartridge.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inkCartridge.md index 5e930f44fe..ef5049992a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inkCartridge.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inkCartridge.md @@ -1,5 +1,5 @@ # Картридж с чернилами -![Цвета радуги.](oredict:oc:inkCartridge) +![Цвета радуги.](oredict:opencomputers:inkCartridge) Картридж с чернилами используется для цветной печати в [3D принтерах](../block/printer.md). Также можно перезаправить их, используя краски, но это очень неэффективно. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/internetCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/internetCard.md index 7c690d506e..598ecd71f2 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/internetCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/internetCard.md @@ -1,5 +1,5 @@ # Интернет карта -![Видео с котиками через 3, 2, ...](oredict:oc:internetCard) +![Видео с котиками через 3, 2, ...](oredict:opencomputers:internetCard) Интернет карта позволяет [компьютерам](../general/computer.md) выходить в интернет. Она позволяет выполнять простые HTTP запросы, а также открывать, читать и писать в TCP сокеты. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/interweb.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/interweb.md index 2b1bac91e9..b77c73dbc6 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/interweb.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/interweb.md @@ -1,5 +1,5 @@ # Интерпаутина -![Вебсайт - это место с паутиной.](oredict:oc:materialInterweb) +![Вебсайт - это место с паутиной.](oredict:opencomputers:materialInterweb) Основной компонент для приборов дальней связи. Интерпаутина использует странную механику, основанную на квантовой передаче сообщений через мир Края. В основном используется при создании [интернет карт](internetCard.md) и [соединенных карт](linkedCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryControllerUpgrade.md index f5495bd6cb..200dfb7c2a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryControllerUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryControllerUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Контроллер инвентаря" -![Я контролирую все.](oredict:oc:inventoryControllerUpgrade) +![Я контролирую все.](oredict:opencomputers:inventoryControllerUpgrade) Контроллер инвентаря предоставляет [роботам](../block/robot.md) и [дронам](drone.md) возможность выполнять еще больше действий с инвентарем. Это позволяет устройству явно указывать слоты при перемещении предметов из или во внешний инвентарь, получать подробную информацию о стаках предметов и, наконец, позволяет [роботам](../block/robot.md) самим менять инструмент. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryUpgrade.md index d63e13487c..ba711e7501 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/inventoryUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Инвентарь" -![Где же он хранит все эти вещи?..](oredict:oc:inventoryUpgrade) +![Где же он хранит все эти вещи?..](oredict:opencomputers:inventoryUpgrade) Улучшение позволяет добавить слоты инвентаря для [роботов](../block/robot.md) и [дронов](drone.md). Каждое улучшение дает [роботу](../block/robot.md) 16 дополнительных слотов - до 64 слотов максимум; [дронам](drone.md) даёт 4 слота с каждым апгрейдом - максимально до 8 слотов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/lanCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/lanCard.md index 7e9cee5fcd..728a12f22e 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/lanCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/lanCard.md @@ -1,5 +1,5 @@ # Сетевая карта -![Войди в сеть.](oredict:oc:lanCard) +![Войди в сеть.](oredict:opencomputers:lanCard) Сетевая карта позволяет [компьютерам](../general/computer.md) отправлять и получать сетевые сообщения. Сообщения (или пакеты) могут быть отправлены либо всем принимающим устройствам в подсети, либо конкретной сетевой карте с определённым адресом. [Ретрансляторы](../block/relay.md) могут быть использованы для связи нескольких подсетей друг с другом и передачи сообщений. Также возможно отправить письмо получателю в другой подсети, если сети соединены одним или несколькими [ретрансляторами](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/leashUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/leashUpgrade.md index 5fdfd693be..384189818e 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/leashUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/leashUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Поводок" -![-отредактировано- ~ Vexatos 2015](oredict:oc:leashUpgrade) +![-отредактировано- ~ Vexatos 2015](oredict:opencomputers:leashUpgrade) Позволяет привязывать животных к различным устройствам, например [дронам](drone.md). Одновременно можно привязать несколько животных, что делает это довольно удобным для перемещения стад. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/linkedCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/linkedCard.md index e52f7545ef..b2b7bf651b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/linkedCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/linkedCard.md @@ -1,5 +1,5 @@ # Соединенные карты -![Я чувствую, что мы связаны.](oredict:oc:linkedCard) +![Я чувствую, что мы связаны.](oredict:opencomputers:linkedCard) Соединенная карта - это специализированная, но продвинутая версия [сетевой карты](lanCard.md). Они могут работать только в паре, образуя прямое соединение между картами. Соединенные карты могут передавать данные через большие (точнее, бесконечные) расстояния, а также между измерениями. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/manual.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/manual.md index 863f53a9bf..9e3c9ba0a0 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/manual.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/manual.md @@ -1,6 +1,6 @@ # Руководство -![Стоит прочитать.](oredict:oc:manual) +![Стоит прочитать.](oredict:opencomputers:manual) То, что вы сейчас читаете. В руководстве собрана информация об OpenComputers. Если вы хотите что-либо узнать о блоке или предмете из этого мода, это то, что вам нужно! Пролистните вниз с помощью колёсика мыши, чтобы узнать, как пользоваться руководством. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/mfu.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/mfu.md index 4d186fe01c..e90af6d64a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/mfu.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/mfu.md @@ -1,6 +1,6 @@ # МФУ -![Вы никогда не узнаете истинного смысла этой аббревиатуры.](oredict:oc:mfu) +![Вы никогда не узнаете истинного смысла этой аббревиатуры.](oredict:opencomputers:mfu) Это улучшение работает как удаленный [адаптер](../block/adapter.md). Приседая, кликните на любую сторону какого-либо блока, чтобы привязать МФУ к нему. Потом поместите его в адаптер (расстояние очень ограничено), и он будет действовать так, будто адаптер расположен рядом со стороной блока, к которой вы его привязали. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/microcontrollerCase1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/microcontrollerCase1.md index 7003d6414b..7daf4d553b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/microcontrollerCase1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/microcontrollerCase1.md @@ -1,6 +1,6 @@ # Корпус микроконтроллера -![Он такой милый.](oredict:oc:microcontrollerCase1) +![Он такой милый.](oredict:opencomputers:microcontrollerCase1) Корпус микроконтроллера используется для создания [микроконтроллеров](../block/microcontroller.md) в [сборщике](../block/assembler.md). [Микроконтроллеры](../block/microcontroller.md) - это очень примитивные [компьютеры](../general/computer.md). Они содержат только очень ограниченный набор компонентов и используются в специфических задачах: реагирование на сигнал красного камня или обработка сетевых сообщений. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/nanomachines.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/nanomachines.md index 6a4dffe3f5..4a4f7298c2 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/nanomachines.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/nanomachines.md @@ -1,6 +1,6 @@ # Нанороботы -![Нанороботы, Карл.](oredict:oc:nanomachines) +![Нанороботы, Карл.](oredict:opencomputers:nanomachines) Это миниатюрные устройства, которые интегрируются с вашей нервной системой, чтобы сделать вас сильнее, лучше и быстрее или убить вас. Иногда все вместе. Проще говоря, нанороботы предоставляют систему, работающую на энергии и дающие игроку положительными (и отрицательными) эффектами. Съешьте нанороботов, чтобы ввести их в организм. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/navigationUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/navigationUpgrade.md index cf557b5fce..98efaf2110 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/navigationUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/navigationUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Навигация" -![Я потерялся. Снова.](oredict:oc:navigationUpgrade) +![Я потерялся. Снова.](oredict:opencomputers:navigationUpgrade) Данное улучшение предоставляет информацию о местоположении и ориентации устройствам. Получаемые координаты относительны к центру карты, использованной при создании улучшения, а радиус функционирования зависит от размера карты. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/numPad.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/numPad.md index c3e4387d0c..3f74ad7613 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/numPad.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/numPad.md @@ -1,5 +1,5 @@ # Цифровой блок клавиш -![Проверьте на отпечатки пальцев.](oredict:oc:materialNumPad) +![Проверьте на отпечатки пальцев.](oredict:opencomputers:materialNumPad) Цифровая клавиатура - часть любой [клавиатуры](../block/keyboard.md). Позволяет вводить цифры. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/pistonUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/pistonUpgrade.md index 7e53fa66ec..fef1f2050b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/pistonUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/pistonUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Поршень" -![Толкни.](oredict:oc:pistonUpgrade) +![Толкни.](oredict:opencomputers:pistonUpgrade) Улучшение "Поршень" позволяет некоторым устройствам работать так же, как и обычный поршень. После установки станет доступен компонент с одним методом - `push()`. При вызове данного метода устройство попытается сдвинуть блок, находящийся перед ним. Для [роботов](../block/robot.md) и [микроконтроллеров](../block/microcontroller.md) это передняя сторона; для [планшетов](tablet.md) будет использовано направление, куда смотрит игрок. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/printedCircuitBoard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/printedCircuitBoard.md index e6a70c6eea..f3b756afd0 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/printedCircuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/printedCircuitBoard.md @@ -1,5 +1,5 @@ # Печатная плата -![Также известна как PCB](oredict:oc:materialCircuitBoardPrinted) +![Также известна как PCB](oredict:opencomputers:materialCircuitBoardPrinted) Один из самых базовых компонентов крафта в OpenComputers вместе с [транзистором](transistor.md). Используется как основа для множества компонентов: [карт](card.md) и большого количества [блоков](../block/index.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/ram1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/ram1.md index 4333ea6416..1dcc2f568a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/ram1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/ram1.md @@ -1,6 +1,6 @@ # Оперативная память -![Do you remember, dancing in September~](oredict:oc:ram1) +![Do you remember, dancing in September~](oredict:opencomputers:ram1) Память, как и [процессор](cpu1.md), - неотъемлемая часть всех [компьютеров](../general/computer.md). В зависимости от архитектуры [процессора](cpu1.md), именно память отвечает за то, что [компьютеры](../general/computer.md) могут делать, а что нет. Для стандартной архитектуры Lua, например, это позволяет контролировать количество памяти, которое могут использовать Lua-скрипты. Это значит, что для запуска больших и требовательных программ вам потребуется больше памяти. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/rawCircuitBoard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/rawCircuitBoard.md index 79622624e1..58c0b80376 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/rawCircuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/rawCircuitBoard.md @@ -1,5 +1,5 @@ # Основа для печатной платы -![Не суши.](oredict:oc:materialCircuitBoardRaw) +![Не суши.](oredict:opencomputers:materialCircuitBoardRaw) Компонент крафта, использующийся для создания [печатной платы](circuitBoard.md) (или [отпечатанной печатной платы](printedCircuitBoard.md), в зависимости от используемых набора рецептов). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/redstoneCard1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/redstoneCard1.md index 8eeb418aba..4dcd219067 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/redstoneCard1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/redstoneCard1.md @@ -1,6 +1,6 @@ # Плата на красном камне -![Вижу красный.](oredict:oc:redstoneCard1) +![Вижу красный.](oredict:opencomputers:redstoneCard1) Карта на красном камне позволяет [компьютерам](../general/computer.md) считывать и подавать аналоговый сигнал красного камня в прилегающие блоки. Когда сила входящего сигнала изменяется, [компьютер](../general/computer.md) получает соответствующий сигнал. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/server1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/server1.md index 7b3813cccd..35518881f8 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/server1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/server1.md @@ -1,6 +1,6 @@ # Сервер -![Так тебе и надо.](oredict:oc:server1) +![Так тебе и надо.](oredict:opencomputers:server1) Серверы - это форма [компьютеров](../general/computer.md) более высокого уровня. Они могут быть настроены кликом, держа в руке, - как при открытии рюкзака, например, - а также после установки в [стойку](../block/rack.md) и клику (нацелившись на лицевую сторону сервера в [стойке](../block/rack.md)). Для работы сервер должен быть помещен в [стойку](../block/rack.md). Подробнее читайте на странице о [стойке](../block/rack.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/signUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/signUpgrade.md index 99cc364443..02123d4ebb 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/signUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/signUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Контроллер табличек" -![Я вижу таблички на стенах.](oredict:oc:signUpgrade) +![Я вижу таблички на стенах.](oredict:opencomputers:signUpgrade) Данное улучшение позволяет устройствам взаимодействовать с табличками в мире: читать текст на табличке, а также менять его, если разрешено. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/solarGeneratorUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/solarGeneratorUpgrade.md index 6fdc5f4687..aecbd5d086 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/solarGeneratorUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/solarGeneratorUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Солнечная панель" -![Я на солнышке лежу.](oredict:oc:solarGeneratorUpgrade) +![Я на солнышке лежу.](oredict:opencomputers:solarGeneratorUpgrade) Данное улучшение может быть установлено в такие устройства, как: [роботы](../block/robot.md), [дроны](drone.md), [планшеты](tablet.md) - для пассивной генерации энергии. Генерация энергии происходит только под прямыми лучами солнца и не происходит в дождь или в помещении. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tabletCase1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tabletCase1.md index f75bf0ada8..ea4820704c 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tabletCase1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tabletCase1.md @@ -1,6 +1,6 @@ # Корпус планшета -![Не сгибается.](oredict:oc:tabletCase1) +![Не сгибается.](oredict:opencomputers:tabletCase1) Корпус планшета используется для создания [планшетов](tablet.md) в [сборщике](../block/assembler.md). [Планшеты](tablet.md) - миниатюрные и переносимые [компьютеры](../general/computer.md). В них можно установить некоторые улучшения, но они не могут взаимодействовать с игровым миром, как [системный блок](../block/case1.md) (использовать простые [сетевые карты](lanCard.md) или [плату на красном камне](redstoneCard1.md)). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankControllerUpgrade.md index e8f85b1413..050a827e54 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankControllerUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankControllerUpgrade.md @@ -1,6 +1,6 @@ # Улучшение "Контроллер бака" -![Маршрутизация жидкостей.](oredict:oc:tankControllerUpgrade) +![Маршрутизация жидкостей.](oredict:opencomputers:tankControllerUpgrade) Контроллер бака аналогичен [контроллеру инвентаря](inventoryControllerUpgrade.md), но для жидкостей. Позволяет устройствам получать подробную информацию о баках и их содержимом. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankUpgrade.md index 3d12be6303..3e8be279b9 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tankUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Бак для жидкостей" -![Высоси это.](oredict:oc:tankUpgrade) +![Высоси это.](oredict:opencomputers:tankUpgrade) Позволяет устройствам хранить жидкости. Каждый бак может хранить жидкость только одного типа и объемом не более 16 ведер (16000mB). [Роботы](../block/robot.md) и [дроны](drone.md) могут выкачивать жидкости из игрового мира, а также из других баков. Затем они могут снова заполнять баки жидкостями и обратно выливать их (если жидкость поддерживает выливание в мир). Вы можете установить любое количество улучшений этого вида в устройство. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminal.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminal.md index 9d078bfb72..a904c6aee9 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminal.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminal.md @@ -1,6 +1,6 @@ # Удаленный терминал -![Удаленный доступ.](oredict:oc:terminal) +![Удаленный доступ.](oredict:opencomputers:terminal) Терминал может быть использован для контроля компьютеров через [сервер терминалов](terminalServer.md). Перед использованием кликните по [серверу терминалов](terminalServer.md), установленному в [стойку](../block/rack.md) (кликнув по блоку [стойки](../block/rack.md) в мире, направляя курсор на [сервер терминалов](terminalServer.md)), чтобы привязать терминал к серверу терминалов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminalServer.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminalServer.md index ed65044e2e..73fcf3c81b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminalServer.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/terminalServer.md @@ -1,5 +1,5 @@ # Сервер терминалов -![Удаленный просмотр](oredict:oc:terminalServer) +![Удаленный просмотр](oredict:opencomputers:terminalServer) Серверы терминалов предоставляют виртуальный [монитор](../block/screen1.md) и [клавиатуру](../block/keyboard.md), которыми можно управлять через [удаленные терминалы](terminal.md). Подробнее читайте на странице о [терминале](terminal.md). Для работы установите сервер терминалов в [стойку](../block/rack.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/texturePicker.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/texturePicker.md index b4531ed3a5..cdc0a66694 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/texturePicker.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/texturePicker.md @@ -1,6 +1,6 @@ # Определитель текстур -![Что это за цвет?](oredict:oc:texturePicker) +![Что это за цвет?](oredict:opencomputers:texturePicker) Данный предмет будет в первую очередь удобен при создании моделей для [3D принтеров](../block/printer.md): он позволяет узнать название текстуры блока, установленного в мире, - достаточно просто кликнуть на него. Обратите внимание, что для блоков с особым рендером, таких как сундуки, это может не сработать. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tractorBeamUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tractorBeamUpgrade.md index 3b4683d8fe..cb23b17cf7 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tractorBeamUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tractorBeamUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Притягивающий луч" -![Пошли со мной.](oredict:oc:tractorBeamUpgrade) +![Пошли со мной.](oredict:opencomputers:tractorBeamUpgrade) Данное улучшение позволяет устройствам подбирать предметы в радиусе 3 блоков вокруг себя. Это очень удобно использовать для [роботов](../block/robot.md)-фермеров, а также если они используют инструменты, ломающие сразу несколько блоков вокруг себя (как инструменты из Tinker's Construct). Каждая операция пытается подобрать один стак предметов вокруг себя, на что затрачивает некоторое количество энергии. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tradingUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tradingUpgrade.md index 3096648992..c77b9d6dbf 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tradingUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tradingUpgrade.md @@ -1,5 +1,5 @@ # Улучшение "Торговля" -![Эквивалентный обмен](oredict:oc:tradingUpgrade) +![Эквивалентный обмен](oredict:opencomputers:tradingUpgrade) Данное улучшение позволяет организовать автоматическую торговлю с торговцами - например, с жителями. В качестве покупателей могут выступать как [роботы](../block/robot.md), так и [дроны](drone.md). Они могут получать информацию о доступных сделках у продавцов, а также собственно торговать. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/transistor.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/transistor.md index 234cb23f13..e3a6b19b8a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/transistor.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/transistor.md @@ -1,5 +1,5 @@ # Транзистор -![Кажется, я использовал все отсылки.](oredict:oc:materialTransistor) +![Кажется, я использовал все отсылки.](oredict:opencomputers:materialTransistor) Самый простой элемент крафта в OpenComputers. Он используется для крафта [микрочипов](chip1.md) и иных электронных компонентов. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/upgradeContainer1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/upgradeContainer1.md index 62252b4b63..4c826b9162 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/upgradeContainer1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/upgradeContainer1.md @@ -1,5 +1,5 @@ # Контейнер для улучшения -![Может хранить улучшение.](oredict:oc:upgradeContainer1) +![Может хранить улучшение.](oredict:opencomputers:upgradeContainer1) Это улучшение-контейнер для [роботов](../block/robot.md), добавляющее собранным [роботам](../block/robot.md) слот под установку еще одного улучшения. Максимальный уровень улучшения, которое может быть установлено, равен уровню контейнера. Прочтите документацию о [роботах](../block/robot.md) и [сборщике](../block/assembler.md) для информации о требуемой сложности сборки. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/wlanCard1.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/wlanCard1.md index 7f007d1f88..dcf99fed13 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/wlanCard1.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/wlanCard1.md @@ -1,6 +1,6 @@ # Беспроводная сетевая карта -![Может вызвать рак. Не может.](oredict:oc:wlanCard2) +![Может вызвать рак. Не может.](oredict:opencomputers:wlanCard2) Беспроводная сетевая карта - это улучшенная версия [сетевой карты](lanCard.md), которая в дополнение к сообщениям по проводной линии также может принимать сообщения и по беспроводной сети. Сила сигнала напрямую контролирует, на каком расстоянии может быть получено отправленное сообщение, и равна расстоянию в блоках. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/worldSensorCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/worldSensorCard.md index 8bdbbd0160..5c03c3daa2 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/worldSensorCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/worldSensorCard.md @@ -1,4 +1,4 @@ # Карта-мировой сенсор -![Для смелых...](oredict:oc:worldSensorCard) +![Для смелых...](oredict:opencomputers:worldSensorCard) Данная карта позволяет получать информацию об атмосфере и гравитации на планетах, добавляемых модом GalactiCraft. Может быть полезно для [роботов](../block/robot.md) и [дронов](drone.md), работающих в космосе. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/wrench.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/wrench.md index f5e4910f50..c9d3b9d3c4 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/wrench.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/wrench.md @@ -1,5 +1,5 @@ # Ключ -![Сделано в Швейцарии.](oredict:oc:wrench) +![Сделано в Швейцарии.](oredict:opencomputers:wrench) Как и другие технологические моды, OpenComputers имеет свой ключ. В данном случае это гибрид ключа и отвертки, который выглядит, будто его очень неудобно использовать. Ключ может поворачивать большинство блоков; кроме того, его можно использовать вместо других ключеподобных инструментов. diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/accesspoint.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/accesspoint.md index 968d07ff90..114439f7ea 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/accesspoint.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/accesspoint.md @@ -1,6 +1,6 @@ # 接入点 -![AAA](oredict:oc:accessPoint) +![AAA](oredict:opencomputers:accessPoint) *本方块已废弃,将会在未来版本被移除* 请在工作台中将其合成为[中继器](relay.md)以避免丢失。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/adapter.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/adapter.md index 10625ff252..fe91645d2e 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/adapter.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/adapter.md @@ -1,6 +1,6 @@ # 适配器 -![Now with 100% more everything.](oredict:oc:adapter) +![Now with 100% more everything.](oredict:opencomputers:adapter) 适配器令[电脑](../general/computer.md)能与原版或其他 Mod 的方块交互。支持适配器的方块在连接适配器后,将会在与适配器连接的[电脑](../general/computer.md)上以组件的形式显示出来。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/assembler.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/assembler.md index 3043669fb7..5a2ed46b2c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/assembler.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/assembler.md @@ -1,6 +1,6 @@ # 装配器 -![更硬,更好,更快,更强。](oredict:oc:assembler) +![更硬,更好,更快,更强。](oredict:opencomputers:assembler) 装配器是个用来制作精密电子设备的高级工作台,像是[机器人](robot.md)、[无人机](../item/drone.md)和[平板](../item/tablet.md)这样的设备都需要在这里制作。组装电子设备的过程需要消耗大量能源,所以在此推荐使用[电容](capacitor.md)保证其能源供应。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/cable.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/cable.md index 5fc4ed909d..69ad2afd44 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/cable.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/cable.md @@ -1,6 +1,6 @@ # 线缆 -![沙拉。](oredict:oc:cable) +![沙拉。](oredict:opencomputers:cable) 用于连接相距甚远的[电脑](../general/computer.md)和设备。如果你的设备群足够紧凑,你大概不需要这玩意。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/capacitor.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/capacitor.md index 20ba42f83a..99380578fe 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/capacitor.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/capacitor.md @@ -1,6 +1,6 @@ # 电容 -![超 9000!](oredict:oc:capacitor) +![超 9000!](oredict:opencomputers:capacitor) 电容存储了计算机网络需要的能源,实际上就是一个以备不时之需的缓存。和使用[能源转换器](powerConverter.md)将转换其他 Mod 的能量到 OpenComputer 使用的电力不一样的是,电容传输电力的过程是瞬时的。这样一个缓存对一些能耗大的任务比较有用,比如[组装](assembler.md)或[充能](charger.md)[机器人](robot.md)和[无人机](../item/drone.md)这样的设备。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/case1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/case1.md index 7d72bf0457..44453df734 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/case1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/case1.md @@ -1,6 +1,6 @@ # 机箱 -![Just in case.](oredict:oc:case1) +![Just in case.](oredict:opencomputers:case1) 机箱有数个型号,决定了能装什么配件。相比于其他配件的等级,机箱还多一个用于创造模式下调试的等级。机箱还用于在[装配器](assembler.md)中组装[机器人](robot.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/chameliumblock.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/chameliumblock.md index 56e5fa6358..084465234d 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/chameliumblock.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/chameliumblock.md @@ -1,6 +1,6 @@ # 变色块 -![So... blank.](oredict:oc:chameliumBlock) +![So... blank.](oredict:opencomputers:chameliumBlock) 几块[变色材料](../item/chamelium.md)合成出装饰用的单色方块。可用染料染成原版的十六种颜色。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/charger.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/charger.md index b939722d9d..b574503cd9 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/charger.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/charger.md @@ -1,6 +1,6 @@ # 充电机 -![All right, let's do this.](oredict:oc:charger) +![All right, let's do this.](oredict:opencomputers:charger) 充电机可为[机器人](robot.md)、[无人机](../item/drone.md)和[平板](../item/tablet.md)这样的设备充能。需要用红石信号激活方能工作。充能速度被红石信号强度决定,信号强度为 15 时它会全速为设备充能。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/disassembler.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/disassembler.md index dad952de5a..51b9be9473 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/disassembler.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/disassembler.md @@ -1,6 +1,6 @@ # 拆解器 -![造了拆,拆了造。](oredict:oc:disassembler) +![造了拆,拆了造。](oredict:opencomputers:disassembler) 拆解器可以将 OpenComputers 中的绝大多数设备拆成零件。常用来回收不需要的或者制造出错的设备,比如忘了装[系统](../general/openOS.md)的[机器人](robot.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/diskdrive.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/diskdrive.md index 5a1111633a..2809745bda 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/diskdrive.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/diskdrive.md @@ -1,6 +1,6 @@ # 软盘驱动器 -![转啊转啊转啊转……](oredict:oc:diskDrive) +![转啊转啊转啊转……](oredict:opencomputers:diskDrive) 软盘驱动器与[电脑](../general/computer.md)连接后就能读[软盘](../item/floppy.md)了。这东西在初期很有用,因为低级别的[机箱](case1.md)没有内建的软盘插槽,但你需要用软盘装系统。[OpenOS](../general/openOS.md) 的安装盘可以通过用空的[软盘](../item/floppy.md) 和[手册](../item/manual.md)在工作台中合成获得。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/geolyzer.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/geolyzer.md index ddc4dddb98..2bac3174f1 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/geolyzer.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/geolyzer.md @@ -1,6 +1,6 @@ # 地质分析仪 -![It rocks.](oredict:oc:geolyzer) +![It rocks.](oredict:opencomputers:geolyzer) 地质分析仪让电脑能扫描其周边的地形,并描述周边的方块硬度分布。其扫描结果可借助[全息投影机](hologram1.md)显示成地图,或用来寻找有价值的方块,因为矿石的硬度往往比石头大。地质分析仪的扫描结果中有些许干扰,理论上可以通过反复扫描来提高精确度。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/hologram1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/hologram1.md index 11d7a95554..8623cdfbdf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/hologram1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/hologram1.md @@ -1,6 +1,6 @@ # 全息投影机 -![这是真实的生活吗?还是只是幻想?](oredict:oc:hologram1) +![这是真实的生活吗?还是只是幻想?](oredict:opencomputers:hologram1) 全息投影仪是个立体成像显示器,说穿了就是一个可以显示三维图形的显示器,还能被[电脑](../general/computer.md)控制。T2 的全息投影仪和 T1 的相比,分辨率不变,但是支持电脑控制每个像素的颜色。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/keyboard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/keyboard.md index 0b5df63262..8b991073bf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/keyboard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/keyboard.md @@ -1,6 +1,6 @@ # 键盘 -![QWERTY](oredict:oc:keyboard) +![QWERTY](oredict:opencomputers:keyboard) 键盘自然是用来在[屏幕](screen1.md)上打字的,或者作为输入设备嵌入[机器人](robot.md)和[平板](../item/tablet.md)中。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/motionsensor.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/motionsensor.md index 52a141ace0..aeda23e0a7 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/motionsensor.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/motionsensor.md @@ -1,6 +1,6 @@ # 运动传感器 -![不,要,眨,眼。](oredict:oc:motionSensor) +![不,要,眨,眼。](oredict:opencomputers:motionSensor) 运动传感器允许[电脑](../general/computer.md)探测生物的移动。如果生物的移动速度超过阈值,相连的[电脑](../general/computer.md)就会收到一个信号。阈值可在相连电脑上用其组件 API 调整。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/netsplitter.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/netsplitter.md index db9bce2180..5787fdbee6 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/netsplitter.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/netsplitter.md @@ -1,6 +1,6 @@ # 网络分配器 -![*.net *.split](oredict:oc:netSplitter) +![*.net *.split](oredict:opencomputers:netSplitter) 网络分配器用于控制子网的连接。和[中继器](relay.md)或者[能源转换](powerConverter.md)不一样,网络分配器会与相邻的子网直接连接。每个面上的连接都可以用[螺丝刀扳手](../item/wrench.md)这样的扳手控制。红石信号可令所有连接反转。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerconverter.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerconverter.md index 6f8922b366..8436b394d4 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerconverter.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerconverter.md @@ -1,5 +1,5 @@ # 能量转换器 -![自己人?自己人!](oredict:oc:powerConverter) +![自己人?自己人!](oredict:opencomputers:powerConverter) 能量转换器是让 OpenComputers 使用其他 Mod 能源的最快捷的办法了。如果你只是运行一台电脑,或是一个用不了几次的大电容,你大概不需要造这个。然而如果你想直接驱动[装配机](assembler.md)或者[充电机](charger.md),那你就应该造一台这个了。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerdistributor.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerdistributor.md index 11d8b1310d..2e1a4ac4ab 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerdistributor.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/powerdistributor.md @@ -1,5 +1,5 @@ # 能量分配器 -![Power to the masses.](oredict:oc:powerDistributor) +![Power to the masses.](oredict:opencomputers:powerDistributor) 能量分配器能在不将组件暴露于子网中的前提下,将一个共享能源池(如[电容](capacitor.md))里的能量分配到各个组件上去。它依照负载均衡的原则工作,因此你会发现每个子网的能量供应都“差不多”。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/printer.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/printer.md index ada5c88c7b..d2e1bb2947 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/printer.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/printer.md @@ -1,6 +1,6 @@ # 3D 打印机 -![2D 打印过时了。](oredict:oc:printer) +![2D 打印过时了。](oredict:opencomputers:printer) 3D 打印机允许你在任何方块上用任何纹理打印出你想要的样子。首先你需要在电脑边放一台打印机,这样电脑就能使用 `printer3d` 这个 API,通过这个 API 就能控制打印机打印出[模型](print.md)了。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/rack.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/rack.md index 928c7a6c98..5e57e35785 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/rack.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/rack.md @@ -1,6 +1,6 @@ # 机架 -![免费住房。](oredict:oc:rack) +![免费住房。](oredict:opencomputers:rack) 机架中可以存放四台像是[服务器](../item/server1.md)、[终端服务器](../item/terminalServer.md)和[可挂载磁盘驱动器](../item/diskDriveMountable.md)这样的设备。你可以通过 GUI 来设定机架中设备的连接关系;特别的,如果服务器里面安装了网卡等组件,那么你可以设定这些服务器只暴露网络连接,而不暴露其内部组件。这样的连接在 GUI 中会以一种更细的连接表示,以与允许访问内部设备的普通连接相区分。内部连接只能在设备(或设备中的组件)与总线之间建立;若需连接多个设备,连接到一条总线上即可。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/raid.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/raid.md index efabd666d8..4f582ef222 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/raid.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/raid.md @@ -1,6 +1,6 @@ # Raid -![40 man instance.](oredict:oc:raid) +![40 man instance.](oredict:opencomputers:raid) Raid 磁盘阵列可将三块[硬盘](../item/hdd1.md)组成一个文件系统,组合的文件系统拥有所有硬盘容量之和的大小,并且所有与其相连的电脑都能访问。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/redstone.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/redstone.md index 82ca6084ae..d622e0385c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/redstone.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/redstone.md @@ -1,6 +1,6 @@ # 红石 I/O 端口 -![Hi Red.](oredict:oc:redstone) +![Hi Red.](oredict:opencomputers:redstone) 红石 I/O 端口可用来远程读取和发射红石信号。它就像是 T1 和 T2 [红石卡](../item/redstoneCard1.md)的合体,可以收发简单的模拟信号及捆绑信号,但是无法收发无线红石信号。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/relay.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/relay.md index 23bbcbe0cd..5ad9b80faf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/relay.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/relay.md @@ -1,6 +1,6 @@ # 中继器 -![桥接。](oredict:oc:relay) +![桥接。](oredict:opencomputers:relay) 中继器能在不把组件暴露给其他网络的[电脑](../general/computer.md)的前提下,让子网间进行网络通信。把组件限制在本地网络是个好主意,这样就不用担心[电脑](../general/computer.md)接错屏幕,或者电脑因组件过多崩溃并拒绝启动这样的怪事发生了。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/screen1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/screen1.md index b32b23fad4..a6e9aab2c0 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/screen1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/screen1.md @@ -1,6 +1,6 @@ # 显示屏 -![看见没?](oredict:oc:screen1) +![看见没?](oredict:opencomputers:screen1) 显示屏需要和[显卡](../item/graphicsCard1.md)一起使用,这样电脑才能显示文本。不同型号的屏幕能支持的分辨率和色深不尽相同,从低分单色屏到高分 256 色屏都有。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/switch.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/switch.md index d8d8b291da..1ddd71b491 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/switch.md @@ -1,6 +1,6 @@ # Switch -![Building bridges.](oredict:oc:switch) +![Building bridges.](oredict:opencomputers:switch) *本方块已废弃,将会在未来版本被移除* 请在工作台中将其合成为[中继器](relay.md)以避免丢失。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/transposer.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/transposer.md index d21ba56f1e..4d317259d1 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/transposer.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/transposer.md @@ -1,6 +1,6 @@ # 转运器 -![Such a poser.](oredict:oc:transposer) +![Such a poser.](oredict:opencomputers:transposer) 转运器连接了红石控制的漏斗和[机器人](robot.md),这样[电脑](../general/computer.md)就能控制物品和流体在相邻方块之间的传输了。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/zh_cn/block/waypoint.md index b337317f1d..647a11eeab 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/block/waypoint.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/block/waypoint.md @@ -1,6 +1,6 @@ # 路径点 -![“这边!”“不,那边!”](oredict:oc:waypoint) +![“这边!”“不,那边!”](oredict:opencomputers:waypoint) 路径点重点不在本身,而是如何使用。[导航升级](../item/navigationUpgrade.md)可以探测路径点,因此安装了这种升级的设备就可以通过路径点来导航。这在编写适用于[机器人](robot.md)和[无人机](../item/drone.md)的高度可重用程序时很有用。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md b/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md index 3bdda70bc6..c381cc82b5 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md @@ -4,13 +4,13 @@ This is some test text for the subset of Markdown supported by the planned ingam ![This is a tooltip...](opencomputers:textures/gui/printer_ink.png) ![This is a tooltip...](opencomputers:/textures/gui/printer_material.png) *This* is *italic* text, ~~strikethrough~~ maybe abc-ter **some** text **in bold**. Is _this underlined_? Oh, no, _it's also italic!_ Well, this [a link](../index.md). -![This is rendered live.](oredict:oc:assembler) +![This is rendered live.](oredict:opencomputers:assembler) ## Smaller headline [also with *link* but this __one__ longer](../block/adapter.md) -![This is another tooltip.](item:OpenComputers:item@23) +![This is another tooltip.](item:opencomputers:item@23) some text directly above the item stack renderer to test spacing -![All the colors.](oredict:craftingPiston) +![All the colors.](oredict:forge/piston) some text directly below the item stack renderer to test spacing This is *italic @@ -42,9 +42,9 @@ asdasd ![oh my god, the recursion!](img/example.png) qweqwe And finally, [this is a link!](https://avatars1.githubusercontent.com/u/514903). -![broken item image](item:this is broken) -![broken item image](block:this is broken) -![broken item image](oredict:this is broken) +![broken item image](item:this_is_broken) +![broken item image](block:this_is_broken) +![broken item image](oredict:this_is_broken) wrap testing 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 @@ -53,17 +53,17 @@ wrap testing * 12345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 - `123456789012345678901234567890.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890` -this is a test for an![](oredict:oc:cpu1)an inline image kakakakalalsd 123 as +this is a test for an![](oredict:opencomputers:cpu1)an inline image kakakakalalsd 123 as -this is a test for an![](oredict:oc:cpu1) +this is a test for an![](oredict:opencomputers:cpu1) an image with a break after it this is a test for an -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) an image between two lines this is a test for an -![](oredict:oc:cpu1) +![](oredict:opencomputers:cpu1) an image between two blank lines diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/general/quickstart.md b/src/main/resources/assets/opencomputers/doc/zh_cn/general/quickstart.md index 4673873740..3b3c5a537d 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/general/quickstart.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/general/quickstart.md @@ -6,7 +6,7 @@ 首先你需要一个[机箱](../block/case1.md)。你所有的电脑配件都要装这里面,它将决定你电脑的行为。 -![一个 T2 机箱](oredict:oc:case2) +![一个 T2 机箱](oredict:opencomputers:case2) 比如你要挑一个适合你的[显卡](../item/graphicsCard1.md),还可能需要一个[网卡](../item/lanCard.md)、一块[红石卡](../item/redstoneCard1.md)、甚至是创造模式下调试时需要的[调试卡](../item/debugCard.md)。 @@ -37,7 +37,7 @@ 好的,它启动了。如果还有什么问题的话,可以使用[分析仪](../item/analyzer.md)排查。不过我们的电脑应该跑起来了。最难的部分已经过去了,剩下就是如何让电脑输出信息,并且让电脑接受输入。 你需要给电脑配[屏幕](../block/screen1.md)和[显卡](../item/graphicsCard1.md)。 -![不是平板屏幕哦](oredict:oc:screen2) +![不是平板屏幕哦](oredict:opencomputers:screen2) [屏幕](../block/screen1.md)可以直接放在机箱一侧,或是通过[线缆](../block/cable.md)相连。[显卡](../item/graphicsCard1.md)自然是要装机箱里。现在你应该能看到[屏幕](../block/screen1.md)上闪烁的光标了。最后,[键盘](../block/keyboard.md)应安装在[屏幕](../block/screen1.md)上,或直接冲着[屏幕](../block/screen1.md)放置。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/abstractbuscard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/abstractbuscard.md index 47e3204e67..c8df1eff3a 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/abstractbuscard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/abstractbuscard.md @@ -1,5 +1,5 @@ # 抽象总线卡 -![更多的网络连接!](oredict:oc:abstractBusCard) +![更多的网络连接!](oredict:opencomputers:abstractBusCard) 这张卡允许[电脑](../general/computer.md)、[服务器](server1.md)和[机器人](../block/robot.md)与 StargateTech2 的抽象类总线交互。当它安装好时,这些方块会连接到抽象类总线,总线上的组件将会对机器可用,且机器可以通过抽象类总线发送信息。传入抽象类总线的信息将会被转换为发送给机器的信号。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/acid.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/acid.md index 3d473e3c21..ddc4c3cf8f 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/acid.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/acid.md @@ -1,6 +1,6 @@ # 酸液 -![Reflux?](oredict:oc:materialAcid) +![Reflux?](oredict:opencomputers:materialAcid) 可口的[来源请求]混合液体,如果你想……找点乐子,或者烧了你的食管,亦或者都想的话,就喝下去吧。同时也是制作多种物品的原料。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/alu.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/alu.md index a48ce9e892..f929a57fa1 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/alu.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/alu.md @@ -1,5 +1,5 @@ # 算术逻辑单元 -![我有逻辑!](oredict:oc:materialALU) +![我有逻辑!](oredict:opencomputers:materialALU) 用来合成 [CPU](cpu1.md) 和[显卡](graphicsCard1.md)这样需要执行计算的元件。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/analyzer.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/analyzer.md index 0b190777c4..36f2c40097 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/analyzer.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/analyzer.md @@ -1,6 +1,6 @@ # 分析仪 -![必须。抵制。不当的。玩笑。](oredict:oc:analyzer) +![必须。抵制。不当的。玩笑。](oredict:opencomputers:analyzer) 一个小巧的,用来探测 OpenComputers 的方块信息的仪器。只要在潜行时右键就可以把方块信息输出到你的聊天栏里。可以导出的信息从设备地址,到子网内的能量水平,再到让电脑宕机的错误信息应有尽有。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/angelupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/angelupgrade.md index 169ad750fe..6465319e29 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/angelupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/angelupgrade.md @@ -1,5 +1,5 @@ # 天使升级 -![哈利路亚!](oredict:oc:angelUpgrade) +![哈利路亚!](oredict:opencomputers:angelUpgrade) 允许[机器人](../block/robot.md)凭空放置方块,而不用靠着别的方块。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/apu1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/apu1.md index b6b3e22a42..b4a3ce3a50 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/apu1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/apu1.md @@ -1,6 +1,6 @@ # APU -![Awesomest Probability Unifier.](oredict:oc:apu1) +![Awesomest Probability Unifier.](oredict:opencomputers:apu1) [CPU](cpu1.md) 和[显卡](graphicsCard1.md)的完美结合,可以为你省下整整一个插槽。和普通的 CPU 一样,它决定了一台[电脑](../general/computer.md)的架构和这台[电脑](../general/computer.md)最多能够支持多少组件。同时,APU 还提供了基本的图形功能。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/arrowkeys.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/arrowkeys.md index 8c27faf93e..20c1eff785 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/arrowkeys.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/arrowkeys.md @@ -1,5 +1,5 @@ # 方向键 -![感谢它们不是箭头做的。(方向键 arrow key,arrow 有箭头的意思)](oredict:oc:materialArrowKey) +![感谢它们不是箭头做的。(方向键 arrow key,arrow 有箭头的意思)](oredict:opencomputers:materialArrowKey) At the risk of repeating myself: it's a button sink. Because the button economy has seen some serious inflation in recent years. 言归正传,方向键自然是制造[键盘](../block/keyboard.md)的配件。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/batteryupgrade1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/batteryupgrade1.md index 95efc25860..20526a821d 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/batteryupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/batteryupgrade1.md @@ -1,5 +1,5 @@ # 电池升级 -![金属打造。](oredict:oc:batteryUpgrade1) +![金属打造。](oredict:opencomputers:batteryUpgrade1) 用于提升[机器人](../block/robot.md)和[平板](tablet.md)等设备的电池容量,提升它们的续航能力。等级越高存得越多。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/buttongroup.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/buttongroup.md index 97b1bfa113..ec109af877 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/buttongroup.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/buttongroup.md @@ -1,5 +1,5 @@ # 按钮组 -![需要更多的按钮。](oredict:oc:materialButtonGroup) +![需要更多的按钮。](oredict:opencomputers:materialButtonGroup) 你总是把按钮弄得到处都是,我们已经不知 Shift 点击那个按钮配方多少次了。言归正传,按钮组用于制造[键盘](../block/keyboard.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/card.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/card.md index 8b2f55d293..6b65664f6b 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/card.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/card.md @@ -1,5 +1,5 @@ # 基板 -![不可读。](oredict:oc:materialCard) +![不可读。](oredict:opencomputers:materialCard) 用于合成 OpenComputers 中诸如[显卡](graphicsCard1.md)、[网卡](lanCard.md)这样的卡状组件的基础材料。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cardcontainer1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cardcontainer1.md index 23e4d6dfaf..05f3c3cc9d 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cardcontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cardcontainer1.md @@ -1,5 +1,5 @@ # 卡槽 -![Can haz cards!](oredict:oc:cardContainer1) +![Can haz cards!](oredict:opencomputers:cardContainer1) 卡槽是[机器人](../block/robot.md)的物品栏升级之一,安装后机器人就能热插拔卡。卡槽最高只能支持同级别的卡片。卡槽的复杂度是一般组件的两倍。关于复杂度的说明可参考[这里](../block/robot.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chamelium.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chamelium.md index 4d0e885016..41306c6b9e 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chamelium.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chamelium.md @@ -1,6 +1,6 @@ # 变色材料 -![如果你想知道:这是变色龙(Chameleon)做的。](oredict:oc:chamelium) +![如果你想知道:这是变色龙(Chameleon)做的。](oredict:opencomputers:chamelium) 变色材料是一种可塑材料,用于在 [3D 打印机](../block/printer.md)中打印出[各种东西](../block/print.md)。它本身没什么特性,可以拿来盖单色的墙。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chip1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chip1.md index bfb6189c4f..784586defb 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chip1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chip1.md @@ -1,5 +1,5 @@ # 微芯片 -![不是吃的。(chip 也可以指薯片这样的东西)](oredict:oc:circuitChip1) +![不是吃的。(chip 也可以指薯片这样的东西)](oredict:opencomputers:circuitChip1) 微芯片是电子配件的重要元件,分若干级别,用于不同级别的配件。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chunkloaderupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chunkloaderupgrade.md index 2529933e99..c420728edf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/chunkloaderupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/chunkloaderupgrade.md @@ -1,6 +1,6 @@ # 区块加载升级 -![哪儿也不去。](oredict:oc:chunkloaderUpgrade) +![哪儿也不去。](oredict:opencomputers:chunkloaderUpgrade) 可以装在像是[机器人](../block/robot.md)和[微控制器](../block/microcontroller.md))的设备上,以保证其所在区块及相邻的区块的加载。这个功能自然是耗电的。可以透过其组件 API 来控制开关。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/circuitboard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/circuitboard.md index b40fbd16a9..4f05abb03b 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/circuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/circuitboard.md @@ -1,5 +1,5 @@ # 电路板 -![需要更多黄金。](oredict:oc:materialCircuitBoard) +![需要更多黄金。](oredict:opencomputers:materialCircuitBoard) 这是从[未加工电路板](rawCircuitBoard.md)到[印刷电路板](printedCircuitBoard.md)的中间体。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/componentbus1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/componentbus1.md index bfb92169c8..d4924d4ba3 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/componentbus1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/componentbus1.md @@ -1,6 +1,6 @@ # 组件总线 -![再——来——一——打——](oredict:oc:componentBus1) +![再——来——一——打——](oredict:opencomputers:componentBus1) 组件总线是[服务器](server1.md)专用的升级,允许[服务器](server1.md)和更多的组件同时通讯。和 CPU 一样,高级的组件总线可以连接更多的组件。更高级的[服务器](server1.md)能连接的组件总线也越多,这样一来服务器能连接的组件也就更多了。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/controlunit.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/controlunit.md index de41b4017f..ae7e327c89 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/controlunit.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/controlunit.md @@ -1,5 +1,5 @@ # 控制单元 -![内置巡航系统。](oredict:oc:materialCU) +![内置巡航系统。](oredict:opencomputers:materialCU) 合成 [CPU](cpu1.md) 等高级电路的元件。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cpu1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cpu1.md index 5e24255d1d..2292a284fb 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cpu1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cpu1.md @@ -1,6 +1,6 @@ # CPU -![脑——子——](oredict:oc:cpu1) +![脑——子——](oredict:opencomputers:cpu1) 中央处理器是[电脑](../general/computer.md)和[服务器](server1.md)的核心,定义了[电脑](../general/computer.md)的架构,并决定了可连接组件的数量。级别越高,每 tick 可以进行的函数调用越多。一言以蔽之,级别越高跑得越快。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/craftingupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/craftingupgrade.md index 3985ce817f..b46fb09636 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/craftingupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/craftingupgrade.md @@ -1,5 +1,5 @@ # 合成升级 -![Crafty.](oredict:oc:craftingUpgrade) +![Crafty.](oredict:opencomputers:craftingUpgrade) 合成升级能让[机器人](../block/robot.md)在自己的[物品栏](../item/inventoryUpgrade.md)进行各种合成。[机器人](../block/robot.md)左上的 3X3 格子将会用作合成网格,物品需要根据配方来摆放。合成产物会返回机器人物品栏。捡起物品后,首先会尝试放入指定格子中;若失败,则尝试放在下一个空格子里。如果没有格子了,物品会被丢到地上。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cuttingwire.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cuttingwire.md index 6acfca8a2a..2e34f40bb8 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/cuttingwire.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/cuttingwire.md @@ -1,5 +1,5 @@ # 切割线 -![真的,不是上吊绳。](oredict:oc:materialCuttingWire) +![真的,不是上吊绳。](oredict:opencomputers:materialCuttingWire) 在困难模式合成中会用到的东西,用于合成[未加工电路板](rawCircuitBoard.md)。很低效。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/databaseupgrade1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/databaseupgrade1.md index d4782483d2..4b26c157e0 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/databaseupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/databaseupgrade1.md @@ -1,6 +1,6 @@ # 数据库升级 -![住在数据库里。](oredict:oc:databaseUpgrade1) +![住在数据库里。](oredict:opencomputers:databaseUpgrade1) 数据库升级可以通过配置来存储一系列物品信息,进而被其他组件所使用。对于仅仅通过 NBT 数据来区分的物品极为有用,因为它们在回调中并不会作为物品描述符的一部分。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/datacard1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/datacard1.md index 19b13c8ddb..ca238588f9 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/datacard1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/datacard1.md @@ -1,6 +1,6 @@ # 数据卡 -![和大众的认知不同,它根本不存数据。](oredict:oc:dataCard1) +![和大众的认知不同,它根本不存数据。](oredict:opencomputers:dataCard1) 数据卡提供了多个难以在架构上实现,或者是在那里跑得很慢的算法,例如散列函数、压缩解压缩等。和网卡一样,数据卡也内嵌有一个文件系统,存有一些需要用到这些功能的程序。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/disk.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/disk.md index cf00e955d8..a46dd2435c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/disk.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/disk.md @@ -1,5 +1,5 @@ # 磁碟 -![这个世界。愿特里·普拉切特安息。(特里·普拉切特的代表作“Discworld”(《碟形世界》)标题亦可理解成“磁碟的世界”)](oredict:oc:materialDisk) +![这个世界。愿特里·普拉切特安息。(特里·普拉切特的代表作“Discworld”(《碟形世界》)标题亦可理解成“磁碟的世界”)](oredict:opencomputers:materialDisk) 用于合成[软盘](floppy.md)和[硬盘](hdd1.md)的基础零件。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/diskdrivemountable.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/diskdrivemountable.md index 6c39cf44a7..0f4c680431 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/diskdrivemountable.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/diskdrivemountable.md @@ -1,5 +1,5 @@ # 可挂载软盘驱动器 -![舒服](oredict:oc:diskDriveMountable) +![舒服](oredict:opencomputers:diskDriveMountable) 这玩意相当于装进[机架](../block/rack.md)里的[软盘驱动器](../block/diskDrive.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/dronecase1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/dronecase1.md index 2a4d76d303..9c469627c3 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/dronecase1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/dronecase1.md @@ -1,6 +1,6 @@ # 无人机外壳 -![无人机:嗡嗡嗡,嗡嗡嗡](oredict:oc:droneCase1) +![无人机:嗡嗡嗡,嗡嗡嗡](oredict:opencomputers:droneCase1) 无人机外壳是在[装配机](../block/assembler.md)中组装[无人机](drone.md)时所用的容器。[无人机](drone.md)是种轻量、快速、功能受限(只有少量的升级和插槽)的移动设备。和[机器人](../block/robot.md)不一样,无人机不能使用工具,也只能和世界进行有限的交互。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/eeprom.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/eeprom.md index 06b71ba86a..6e04445bb7 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/eeprom.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/eeprom.md @@ -1,6 +1,6 @@ # EEPROM -![让我们开始吧。](oredict:oc:eeprom) +![让我们开始吧。](oredict:opencomputers:eeprom) EEPROM 中包含了在电脑启动后引导其完成初始化的代码。这些代码以字节数组的形式保存,在不同的 [CPU](cpu1.md) 架构上有不同的含义,比如对于 Lua BIOS 来说这可能是搜索可用文件系统的初始化代码,对于其他设备这可能是机器码。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/experienceupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/experienceupgrade.md index 25154378a0..ab56754703 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/experienceupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/experienceupgrade.md @@ -1,6 +1,6 @@ # 经验升级 -![这根本解释不通,但它酷,这就够了。](oredict:oc:experienceUpgrade) +![这根本解释不通,但它酷,这就够了。](oredict:opencomputers:experienceUpgrade) 经验升级是一种允许[机器人](../block/robot.md)和[无人机](drone.md)通过杀死怪物、挖矿等操作收集经验球的特种升级。每个升级能存储 30 级经验,并且每一级都可以带来诸如挖矿加速、能量缓存提升等加成。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/floppy.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/floppy.md index d90032f590..bfcb59dcbb 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/floppy.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/floppy.md @@ -1,5 +1,5 @@ # 软盘 -![Many inches.](oredict:oc:floppy) +![Many inches.](oredict:opencomputers:floppy) 是 OpenComputers 中最便宜的存储设备,用于游戏初期在[电脑](../general/computer.md)和[机器人](../block/robot.md)间交换数据。你还可以在地牢中找到一些实用程序的安装软盘(比如 OpenPrograms Package Manager,一个能让你轻松从某个 GitHub 仓库中下载并安装程序的包管理器)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/generatorupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/generatorupgrade.md index 818435ef46..7e852cea08 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/generatorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/generatorupgrade.md @@ -1,6 +1,6 @@ # 发电机升级 -![Generator X.](oredict:oc:generatorUpgrade) +![Generator X.](oredict:opencomputers:generatorUpgrade) 发电机升级是个给设备内嵌发电设备的升级。目前它仅支持煤这样的固体燃料。内部有存储燃料的物品栏。多余燃料可以通过对应的组件 API 取出。从[机器人](../block/robot.md)上移除升级时,会使里面的东西掉出来。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/graphicscard1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/graphicscard1.md index 2820e52c1b..45ac695e8b 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/graphicscard1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/graphicscard1.md @@ -1,6 +1,6 @@ # 显卡 -![炫彩图像](oredict:oc:graphicsCard1) +![炫彩图像](oredict:opencomputers:graphicsCard1) 显卡对大多数计算机都非常重要[computers](../general/computer.md),因为显卡允许[计算机](../general/computer.md)在相连的[屏幕](../block/screen1.md)上显示字符。显卡分若干等级,如同[显示屏](../block/screen1.md)那样支持不同的分辨率和色深。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hdd1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hdd1.md index f860229000..d415f8f83c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hdd1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hdd1.md @@ -1,5 +1,5 @@ # 硬盘驱动器 -![空——间!](oredict:oc:hdd1) +![空——间!](oredict:opencomputers:hdd1) 硬盘是 OpenComputers 中的高级存储器,不同等级的硬盘速度一样,只有容量不同,级别越高容量越大。有的设备只能用硬盘存储数据(尽管服务器还可以用外置[软盘驱动器](../block/diskDrive.md))。硬盘还可在 [RAID](../block/raid.md) 中与其他磁盘组成共享文件系统的阵列,不过要注意,硬盘放进 RAID 里的时候之前的数据都会抹除。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverboots.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverboots.md index 8481da65b1..d7240ac6a0 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverboots.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverboots.md @@ -1,6 +1,6 @@ # 悬浮靴 -![一脚油门。](oredict:oc:hoverBoots) +![一脚油门。](oredict:opencomputers:hoverBoots) 如果不想去编程[无人机](drone.md),有个变通的办法:垫脚石!或者说那是美化的内联冰鞋的名字。总之就是这个意思。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverupgrade1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverupgrade1.md index 2195a8bc85..09e62e53b3 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverupgrade1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/hoverupgrade1.md @@ -1,6 +1,6 @@ # 悬浮升级 -![像羽毛一样漂浮。](oredict:oc:hoverUpgrade1) +![像羽毛一样漂浮。](oredict:opencomputers:hoverUpgrade1) 悬浮升级让[机器人](../block/robot.md)飞得更高。默认,机器人只能往上飞 8 格。平常因为机器人能爬墙所以这并不是什么大问题。机器人移动的规律如下: - 机器人只会在起点和终点都有效的情况下才会动(比如允许搭桥)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inkcartridge.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inkcartridge.md index 295c90c4ab..39c4b7823b 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inkcartridge.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inkcartridge.md @@ -1,5 +1,5 @@ # 墨盒 -![七彩。](oredict:oc:inkCartridge) +![七彩。](oredict:opencomputers:inkCartridge) 墨盒可以很方便地给 [3D 打印机](../block/printer.md)供墨。当然你可以手动用染料来供墨,但这样一不太方便,二没有效率。所以我们强烈建议您今天购买一个真正的 OC 墨盒™!(免责声明:墨盒的价格有可能比打印机还高。) diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/internetcard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/internetcard.md index 17c4d83903..6d1d2da514 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/internetcard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/internetcard.md @@ -1,6 +1,6 @@ # 因特网卡 -![猫片播放倒数,3,2,……](oredict:oc:internetCard) +![猫片播放倒数,3,2,……](oredict:opencomputers:internetCard) 装了这个[电脑](../general/computer.md)就能联网了。它能承载简单的 HTTP 请求以及读写普通的 TCP 客户端套接字。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/interweb.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/interweb.md index cdc12d6f4c..0b94d794dc 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/interweb.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/interweb.md @@ -1,5 +1,5 @@ # 因特网 -![网站就是有蜘蛛网的站点。](oredict:oc:materialInterweb) +![网站就是有蜘蛛网的站点。](oredict:opencomputers:materialInterweb) 因特网是长距离通信设备用到的基本元件,基本原理是利用各种末影物质的奇怪性质以进行某种量子通讯。主要用在[因特网卡](internetCard.md)和[连接卡](linkedCard.md)上。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventorycontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventorycontrollerupgrade.md index 713aa9d653..94116b68a6 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventorycontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventorycontrollerupgrade.md @@ -1,6 +1,6 @@ # 物品栏控制器 -![尽在我的控制之中。](oredict:oc:inventoryControllerUpgrade) +![尽在我的控制之中。](oredict:opencomputers:inventoryControllerUpgrade) 物品栏控制器为[机器人](../block/robot.md)和[无人机](drone.md)提供了额外的物品栏交互能力。它允许设备在从外部容器存取物品时指定具体的格子、读取详细的物品信息以及允许[机器人](../block/robot.md) 不借助外力更换自身装备。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventoryupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventoryupgrade.md index 525019802a..4f55ebb99c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventoryupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/inventoryupgrade.md @@ -1,6 +1,6 @@ # 物品栏升级 -![它把物品存哪去了……](oredict:oc:inventoryUpgrade) +![它把物品存哪去了……](oredict:opencomputers:inventoryUpgrade) 物品栏升级让[机器人](../block/robot.md)和[无人机](drone.md)有了物品栏。每个升级可为[机器人](../block/robot.md)提供 16 个格子,最大扩展到 64;对于[无人机](drone.md)则是提供 4 个格子,最大扩展到 8。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/lancard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/lancard.md index ba691c1389..5647d3f173 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/lancard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/lancard.md @@ -1,5 +1,5 @@ # 网卡 -![联网。](oredict:oc:lanCard) +![联网。](oredict:opencomputers:lanCard) 网卡允许[电脑](../general/computer.md)在本地网络内收发消息。消息(或者叫封包)可向子网广播,或者是发送至特定地址上的设备。[中继器](../block/relay.md)可以用来桥接不同的子网,使之互相通信。若两个网络间有若干[中继器](../block/relay.md),发送跨越网络到指定设备上的数据包也是可能的。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/leashupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/leashupgrade.md index 8637a9e027..eedb3d0147 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/leashupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/leashupgrade.md @@ -1,5 +1,5 @@ # 拴绳升级 -![-redacted- ~ Vexatos 2015](oredict:oc:leashUpgrade) +![-redacted- ~ Vexatos 2015](oredict:opencomputers:leashUpgrade) 拴绳升级允许[无人机](drone.md)这样的设备给动物拴上拴绳,这样一来这些装了该升级的设备就可以一次性拉走一群动物。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/linkedcard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/linkedcard.md index 36fb17a08e..3472550172 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/linkedcard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/linkedcard.md @@ -1,5 +1,5 @@ # 连接卡 -![我觉得我们之间有种微妙的关系。](oredict:oc:linkedCard) +![我觉得我们之间有种微妙的关系。](oredict:opencomputers:linkedCard) 连接卡是特化的高级[网卡](lanCard.md),只能成对使用,用于点对点(P2P)通讯。连接卡可以进行跨维度无限距离的通讯。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/manual.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/manual.md index b5bfeb6d84..c6e916a220 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/manual.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/manual.md @@ -1,6 +1,6 @@ # OpenComputers 手册 -![值得一读。](oredict:oc:manual) +![值得一读。](oredict:opencomputers:manual) 没错就是你正在读的东西!本手册涵盖了 OpenComputers 的所有内容(甚至还有别的东西)。如果你需要关于 OpenComputers 的某个方块或物品的信息,查手册就可以了!用鼠标滚轮往下翻(或者右边的滑块往下拉)来学习如何使用本手册。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/mfu.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/mfu.md index 59ab66c633..6410065ce1 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/mfu.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/mfu.md @@ -1,6 +1,6 @@ # MFU -![你永远也不会知道这个缩写是什么意思。](oredict:oc:mfu) +![你永远也不会知道这个缩写是什么意思。](oredict:opencomputers:mfu) 这个升级相当于远程[适配器](../block/adapter.md)。潜行时,对任意方块的任意面使用该升级以将其与这个面绑定。然后,将其安装到附近的适配器里面(这个“附近”很有限)。好了,现在这个加了 MFU 的适配器就能访问到与 MFU 绑定的方块了! diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/microcontrollercase1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/microcontrollercase1.md index 515cab46ea..72949c6fbe 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/microcontrollercase1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/microcontrollercase1.md @@ -1,6 +1,6 @@ # 微控制器盒 -![超可爱的。](oredict:oc:microcontrollerCase1) +![超可爱的。](oredict:opencomputers:microcontrollerCase1) 微控制器盒是在[装配机](../block/assembler.md)中制造[微控制器](../block/microcontroller.md)的基础。 [微控制器](../block/microcontroller.md)是种极其简化的[电脑](../general/computer.md),只有少量组件,通常设计为特定用途,比如转发或者处理红石信号以及处理网路消息。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/nanomachines.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/nanomachines.md index 9f39d3b8af..d14096eff9 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/nanomachines.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/nanomachines.md @@ -1,6 +1,6 @@ # 纳米机器 -![这是纳米机器,我的儿子。](oredict:oc:nanomachines) +![这是纳米机器,我的儿子。](oredict:opencomputers:nanomachines) 这些跟你的神经系统打交道的玩意能让你变得更快、更高、更强,或者干掉你。甚至有时候这些是同时发生的!简单来说,纳米机器的用途就是往宿主玩家上加 buff 和 debuff。“安装”方法:吃下去! diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/navigationupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/navigationupgrade.md index 3f1d716243..3ae1348fed 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/navigationupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/navigationupgrade.md @@ -1,6 +1,6 @@ # 导航升级 -![我,又,走丢了。](oredict:oc:navigationUpgrade) +![我,又,走丢了。](oredict:opencomputers:navigationUpgrade) 导航升级可提供宿主设备的位置和朝向。坐标是相对用于合成这个升级的地图的中心而言的,它的作用范围也限制在那张地图的大小中。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/numpad.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/numpad.md index 8dc25a6b84..4587ebc09a 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/numpad.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/numpad.md @@ -1,5 +1,5 @@ # 数字键 -![指纹检查。](oredict:oc:materialNumPad) +![指纹检查。](oredict:opencomputers:materialNumPad) 数字键是每块[键盘](../block/keyboard.md)都有的东西,用来输入数字用的。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/pistonupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/pistonupgrade.md index 651c77f519..d21bda0884 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/pistonupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/pistonupgrade.md @@ -1,6 +1,6 @@ # 活塞升级 -![推他。](oredict:oc:pistonUpgrade) +![推他。](oredict:opencomputers:pistonUpgrade) 活塞升级能让某些设备的行为变得像原版活塞那样。安装后会暴露一个只有一个 `push()` 方法的组件。调用此方法时,设备将会试图将它面前的方块推出去。对于[机器人](../block/robot.md)和[单片机](../block/microcontroller.md)来说这就是它们的正面;对于[平板](tablet.md)来说,是玩家视角的那个方向。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/printedcircuitboard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/printedcircuitboard.md index 50167890de..8693313899 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/printedcircuitboard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/printedcircuitboard.md @@ -1,5 +1,5 @@ # 印刷电路板 -![又叫 PCB](oredict:oc:materialCircuitBoardPrinted) +![又叫 PCB](oredict:opencomputers:materialCircuitBoardPrinted) 印刷电路板和[晶体管](transistor.md)一样都是 OpenComputers 中的基础合成材料,用于制作多种元件,例如[卡片](card.md)和为数众多[方块](../block/index.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/ram1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/ram1.md index 78ee629f76..fe839f951e 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/ram1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/ram1.md @@ -1,6 +1,6 @@ # 内存 -![你可曾记得,在九月中起舞~](oredict:oc:ram1) +![你可曾记得,在九月中起舞~](oredict:opencomputers:ram1) 内存和 [CPU](cpu1.md) 一样都是[电脑](../general/computer.md)的核心部件。根据 [CPU](cpu1.md) 的架构的不同,内存很大程度上直接决定了电脑能做什么以及不能做什么。以标准 Lua 架构为例,内存条决定了 Lua 脚本能用多少内存。这意味着你要安装更大的内存条跑更大更复杂的程序。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/rawCircuitBoard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/rawCircuitBoard.md index d0a703fc99..f10836f829 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/rawCircuitBoard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/rawCircuitBoard.md @@ -1,5 +1,5 @@ # 未加工电路板 -![不是寿司。](oredict:oc:materialCircuitBoardRaw) +![不是寿司。](oredict:opencomputers:materialCircuitBoardRaw) 未加工电路板是合成[电路板](circuitBoard.md)及[印刷电路板](printedCircuitBoard.md)的中间材料(以游戏内使用的合成表为准)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/redstonecard1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/redstonecard1.md index e335377a09..18113d9dbb 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/redstonecard1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/redstonecard1.md @@ -1,6 +1,6 @@ # 红石卡 -![红牌。](oredict:oc:redstoneCard1) +![红牌。](oredict:opencomputers:redstoneCard1) 红石卡让[电脑](../general/computer.md)可以收发邻近方块的红石信号。输入信号强度变化时,信号将输入[电脑](../general/computer.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/server1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/server1.md index ae5d83d463..79722f5fdf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/server1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/server1.md @@ -1,6 +1,6 @@ # 服务器 -![为你服务。](oredict:oc:server1) +![为你服务。](oredict:opencomputers:server1) 服务器是种高级[电脑](../general/computer.md)。你可以通过把它拿在手上像打开背包一样打开来进行配置,也可以放在[机架](../block/rack.md)里面,通过站在机架的正面打开机架操作界面来配置它。详细信息请参见[机架](../block/rack.md)条目。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/signupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/signupgrade.md index e1bcc5a504..b5337b2639 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/signupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/signupgrade.md @@ -1,5 +1,5 @@ # 告示牌 I/O -![我看见墙上的牌子了。](oredict:oc:signUpgrade) +![我看见墙上的牌子了。](oredict:opencomputers:signUpgrade) 这个升级允许设备和告示牌交互,用于读取或改写(如果有权限)告示牌上的信息。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/solargeneratorupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/solargeneratorupgrade.md index 4cea2d8f48..9ae7e3ad7f 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/solargeneratorupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/solargeneratorupgrade.md @@ -1,6 +1,6 @@ # 太阳能发电机升级 -![在太阳上行走。](oredict:oc:solarGeneratorUpgrade) +![在太阳上行走。](oredict:opencomputers:solarGeneratorUpgrade) 太阳能发电机升级可以安装到[机器人](../block/robot.md)、[无人机](drone.md)和[平板](tablet.md)上。它只会在阳光直射的时候工作,不是晴天或者在密闭环境下它是不会产生一丁点能量的。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tabletCase1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tabletCase1.md index 531c05e679..443cb9dad0 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tabletCase1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tabletCase1.md @@ -1,6 +1,6 @@ # 平板电脑外壳 -![不能折弯。](oredict:oc:tabletCase1) +![不能折弯。](oredict:opencomputers:tabletCase1) 平板电脑外壳是在[装配机](../block/assembler.md)中组装[平板电脑](tablet.md)的基础。[平板电脑](tablet.md)是种小型可移动[电脑](../general/computer.md),可加装少量升级,但无法像[机箱](../block/case1.md)那样简单的使用红石卡和网卡和世界交互。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankcontrollerupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankcontrollerupgrade.md index d4c2fced29..627789fadf 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankcontrollerupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankcontrollerupgrade.md @@ -1,6 +1,6 @@ # 储罐控制器 -![流体路由。](oredict:oc:tankControllerUpgrade) +![流体路由。](oredict:opencomputers:tankControllerUpgrade) 储罐控制器可用来控制储罐,用于查询内部储罐和外部的信息。实际上它就是流体版的[物品栏控制器升级](inventoryControllerUpgrade.md)。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankupgrade.md index a9b5bc1920..d98caa5e22 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tankupgrade.md @@ -1,5 +1,5 @@ # 储罐升级 -![Suck it.](oredict:oc:tankUpgrade) +![Suck it.](oredict:opencomputers:tankUpgrade) 储罐升级允许设备存储流体。每个升级仅能存储一种流体,并提供 16 桶容量(即 16000 mB)。[机器人](../block/robot.md)和[无人机](drone.md)可以从世界中和其他储罐汲取液体,也可以倒回世界或者储罐中。单台设备可安装的储罐数没有限制。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminal.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminal.md index 0053efaf1f..68c4499980 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminal.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminal.md @@ -1,6 +1,6 @@ # 终端 -![远程访问。](oredict:oc:terminal) +![远程访问。](oredict:opencomputers:terminal) 终端可用于远程访问[终端服务器](terminalServer.md)。只需对准[机架](../block/rack.md)里安装的[终端服务器](terminalServer.md)手持远程终端右击即可完成绑定。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminalserver.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminalserver.md index af56c51c55..340f754317 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminalserver.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/terminalserver.md @@ -1,5 +1,5 @@ # 终端服务器 -![远程查看](oredict:oc:terminalServer) +![远程查看](oredict:opencomputers:terminalServer) 终端服务器可向外界提供虚拟[屏幕](../block/screen1.md)和[键盘](../block/keyboard.md),可以通过绑定[终端](terminal.md)来控制机器。关于终端的更多信息可参阅[终端](terminal.md)条目。终端服务器必须安装在[机架](../block/rack.md)里面。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/texturepicker.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/texturepicker.md index 55a9e4ed84..594fb9d0b5 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/texturepicker.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/texturepicker.md @@ -1,6 +1,6 @@ # 纹理选择器 -![你说啥,这就是换了个皮肤?](oredict:oc:texturePicker) +![你说啥,这就是换了个皮肤?](oredict:opencomputers:texturePicker) 纹理选择器在制作 [3D 打印机](../block/printer.md)使用的模型时很有用。只需要潜行时对着某个方块使用它,就能获得世界上某个方块的纹理名。免责声明:对于箱子这种有特殊渲染器的方块无效。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tractorbeamupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tractorbeamupgrade.md index c9c6181fcb..6cc2c9374c 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tractorbeamupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tractorbeamupgrade.md @@ -1,5 +1,5 @@ # 牵引光束 -![把我传送过来。](oredict:oc:tractorBeamUpgrade) +![把我传送过来。](oredict:opencomputers:tractorBeamUpgrade) 牵引光束升级能让设备捡起 3 格内的物品。对树场或农场里面工作,或使用像是匠魂的某些工具那样有范围破坏能力的工具的[机器人](../block/robot.md)来说十分有用。每次操作都会尝试吸取范围内的一个物品实体,并消耗一定能量。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tradingupgrade.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tradingupgrade.md index 9a0d20ee39..9aaf00ac55 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tradingupgrade.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tradingupgrade.md @@ -1,5 +1,5 @@ # 交易升级 -![等价交换](oredict:oc:tradingUpgrade) +![等价交换](oredict:opencomputers:tradingUpgrade) 交易升级让设备能自动和村民这样的商人交易。它可以装在[机器人](../block/robot.md)和[无人机](drone.md)里面,安装后其能够探测附近商人的存在,获取可用交易信息并完成交易。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/transistor.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/transistor.md index 5c9e975a8a..89a9ffa109 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/transistor.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/transistor.md @@ -1,5 +1,5 @@ # 晶体管 -![我觉得我已无梗可用](oredict:oc:materialTransistor) +![我觉得我已无梗可用](oredict:opencomputers:materialTransistor) 晶体管是 OpenComputers 中最基础的元件,主要用于合成[芯片](chip1.md)等电子产品。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/upgradecontainer1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/upgradecontainer1.md index e932a9fb08..acdf83d984 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/upgradecontainer1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/upgradecontainer1.md @@ -1,5 +1,5 @@ # 升级组件容器 -![Can haz upgrade.](oredict:oc:upgradeContainer1) +![Can haz upgrade.](oredict:opencomputers:upgradeContainer1) 升级组件容器是一种特殊的[机器人](../block/robot.md)升级,它提供一个支持热插拔的升级槽位。它支持的升级的等级和它自身的等级一致。其复杂度是其等级的二倍,参阅[机器人](../block/robot.md)和[装配机](../block/assembler.md)的说明以了解复杂度的设定。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/wlancard1.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/wlancard1.md index 895d768072..a87ad22254 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/wlancard1.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/wlancard1.md @@ -1,6 +1,6 @@ # 无线网卡 -![可能致癌,也可能并不。](oredict:oc:wlanCard2) +![可能致癌,也可能并不。](oredict:opencomputers:wlanCard2) 无线网卡是支持无线网络的升级版[网卡](lanCard.md),有无线收发数据的能力。T2 的无线网卡还能收发有线数据。无线信号发射强度决定了信息可以传多远,这里信号强度等于方块距离。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/worldsensorcard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/worldsensorcard.md index a46b9a7752..121fcab321 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/worldsensorcard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/worldsensorcard.md @@ -1,5 +1,5 @@ # 世界传感器卡 -![勇敢前进……](oredict:oc:worldSensorCard) +![勇敢前进……](oredict:opencomputers:worldSensorCard) 世界传感器卡让设备能读取大气、(GalactiCraft 的)重力等世界信息,对于在空间中[机器人](../block/robot.md)和[无人机](drone.md)来说有用。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/wrench.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/wrench.md index 3b44fe43a5..f1e7374ced 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/wrench.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/wrench.md @@ -1,5 +1,5 @@ # 螺丝刀扳手 -![瑞士制造。](oredict:oc:wrench) +![瑞士制造。](oredict:opencomputers:wrench) 和绝大多数科技主题的 Mod 一样,OpenComputers 也有它自己的扳手——一把融合了螺丝刀功能的扳手。看上去很难用。可以拿来旋转方块,也与其他很多 Mod 中支持各种扳手的设备兼容。 diff --git a/src/main/resources/data/forge/tags/blocks/pistons.json b/src/main/resources/data/forge/tags/blocks/pistons.json new file mode 100644 index 0000000000..1fb4a6d786 --- /dev/null +++ b/src/main/resources/data/forge/tags/blocks/pistons.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:piston", + "minecraft:sticky_piston" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/forge/tags/items/pistons.json b/src/main/resources/data/forge/tags/items/pistons.json new file mode 100644 index 0000000000..1fb4a6d786 --- /dev/null +++ b/src/main/resources/data/forge/tags/items/pistons.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:piston", + "minecraft:sticky_piston" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/adapter.json b/src/main/resources/data/opencomputers/tags/blocks/adapter.json new file mode 100644 index 0000000000..40ea055820 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/adapter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:adapter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/assembler.json b/src/main/resources/data/opencomputers/tags/blocks/assembler.json new file mode 100644 index 0000000000..ce253c7d63 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/assembler.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:assembler" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/cable.json b/src/main/resources/data/opencomputers/tags/blocks/cable.json new file mode 100644 index 0000000000..ddf88f324c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/cable.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cable" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/capacitor.json b/src/main/resources/data/opencomputers/tags/blocks/capacitor.json new file mode 100644 index 0000000000..d5c5604646 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/capacitor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:capacitor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/carpetedcapacitor.json b/src/main/resources/data/opencomputers/tags/blocks/carpetedcapacitor.json new file mode 100644 index 0000000000..3b0f159796 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/carpetedcapacitor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:carpetedcapacitor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/case1.json b/src/main/resources/data/opencomputers/tags/blocks/case1.json new file mode 100644 index 0000000000..36fe770a86 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/case1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/case2.json b/src/main/resources/data/opencomputers/tags/blocks/case2.json new file mode 100644 index 0000000000..731dfce4f6 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/case2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/case3.json b/src/main/resources/data/opencomputers/tags/blocks/case3.json new file mode 100644 index 0000000000..81cf6d2e6f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/case3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/chameliumblock.json b/src/main/resources/data/opencomputers/tags/blocks/chameliumblock.json new file mode 100644 index 0000000000..96191a5dfa --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/chameliumblock.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chameliumblock" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/charger.json b/src/main/resources/data/opencomputers/tags/blocks/charger.json new file mode 100644 index 0000000000..1d72aca9d4 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/charger.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:charger" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/disassembler.json b/src/main/resources/data/opencomputers/tags/blocks/disassembler.json new file mode 100644 index 0000000000..b8edc0505a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/disassembler.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:disassembler" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/diskdrive.json b/src/main/resources/data/opencomputers/tags/blocks/diskdrive.json new file mode 100644 index 0000000000..c672b364d5 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/diskdrive.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:diskdrive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/geolyzer.json b/src/main/resources/data/opencomputers/tags/blocks/geolyzer.json new file mode 100644 index 0000000000..b5dee04f2f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/geolyzer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:geolyzer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/hologram1.json b/src/main/resources/data/opencomputers/tags/blocks/hologram1.json new file mode 100644 index 0000000000..f86e9ea22f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/hologram1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hologram1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/hologram2.json b/src/main/resources/data/opencomputers/tags/blocks/hologram2.json new file mode 100644 index 0000000000..8cd7c2c5fa --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/hologram2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hologram2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/keyboard.json b/src/main/resources/data/opencomputers/tags/blocks/keyboard.json new file mode 100644 index 0000000000..46c3a95875 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/keyboard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:keyboard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/motionsensor.json b/src/main/resources/data/opencomputers/tags/blocks/motionsensor.json new file mode 100644 index 0000000000..5bedd10526 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/motionsensor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:motionsensor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/netsplitter.json b/src/main/resources/data/opencomputers/tags/blocks/netsplitter.json new file mode 100644 index 0000000000..1a34747e99 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/netsplitter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:netsplitter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/powerconverter.json b/src/main/resources/data/opencomputers/tags/blocks/powerconverter.json new file mode 100644 index 0000000000..f1c25c0541 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/powerconverter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:powerconverter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/powerdistributor.json b/src/main/resources/data/opencomputers/tags/blocks/powerdistributor.json new file mode 100644 index 0000000000..327ec64106 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/powerdistributor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:powerdistributor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/printer.json b/src/main/resources/data/opencomputers/tags/blocks/printer.json new file mode 100644 index 0000000000..e519cbe8b0 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/printer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:printer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/rack.json b/src/main/resources/data/opencomputers/tags/blocks/rack.json new file mode 100644 index 0000000000..5c21c51ef2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/rack.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:rack" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/raid.json b/src/main/resources/data/opencomputers/tags/blocks/raid.json new file mode 100644 index 0000000000..94d790f968 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/raid.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:raid" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/redstone.json b/src/main/resources/data/opencomputers/tags/blocks/redstone.json new file mode 100644 index 0000000000..b02efc6970 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:redstone" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/relay.json b/src/main/resources/data/opencomputers/tags/blocks/relay.json new file mode 100644 index 0000000000..8777de68ce --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/relay.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:relay" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/screen1.json b/src/main/resources/data/opencomputers/tags/blocks/screen1.json new file mode 100644 index 0000000000..3715f2df5a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/screen1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/screen2.json b/src/main/resources/data/opencomputers/tags/blocks/screen2.json new file mode 100644 index 0000000000..43611f12cc --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/screen2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/screen3.json b/src/main/resources/data/opencomputers/tags/blocks/screen3.json new file mode 100644 index 0000000000..7747d82efb --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/screen3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json b/src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json new file mode 100644 index 0000000000..55c89af86e --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:endstone" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/transposer.json b/src/main/resources/data/opencomputers/tags/blocks/transposer.json new file mode 100644 index 0000000000..b27df92e33 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/transposer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:transposer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/waypoint.json b/src/main/resources/data/opencomputers/tags/blocks/waypoint.json new file mode 100644 index 0000000000..9096c6243d --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/blocks/waypoint.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:waypoint" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/analyzer.json b/src/main/resources/data/opencomputers/tags/items/analyzer.json new file mode 100644 index 0000000000..af2238c3ec --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/analyzer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:analyzer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/angelupgrade.json b/src/main/resources/data/opencomputers/tags/items/angelupgrade.json new file mode 100644 index 0000000000..e8875be1e9 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/angelupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:angelupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/apu1.json b/src/main/resources/data/opencomputers/tags/items/apu1.json new file mode 100644 index 0000000000..f358b9b068 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/apu1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:apu1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/apu2.json b/src/main/resources/data/opencomputers/tags/items/apu2.json new file mode 100644 index 0000000000..5b2bd1e547 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/apu2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:apu2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/batteryupgrade1.json b/src/main/resources/data/opencomputers/tags/items/batteryupgrade1.json new file mode 100644 index 0000000000..dd85c6333f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/batteryupgrade1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:batteryupgrade1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/batteryupgrade2.json b/src/main/resources/data/opencomputers/tags/items/batteryupgrade2.json new file mode 100644 index 0000000000..68f6cf92e7 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/batteryupgrade2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:batteryupgrade2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/batteryupgrade3.json b/src/main/resources/data/opencomputers/tags/items/batteryupgrade3.json new file mode 100644 index 0000000000..3ede5aa156 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/batteryupgrade3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:batteryupgrade3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cardcontainer1.json b/src/main/resources/data/opencomputers/tags/items/cardcontainer1.json new file mode 100644 index 0000000000..66725d2e88 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cardcontainer1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cardcontainer1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cardcontainer2.json b/src/main/resources/data/opencomputers/tags/items/cardcontainer2.json new file mode 100644 index 0000000000..ee5802a3fe --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cardcontainer2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cardcontainer2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cardcontainer3.json b/src/main/resources/data/opencomputers/tags/items/cardcontainer3.json new file mode 100644 index 0000000000..5448319176 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cardcontainer3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cardcontainer3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/chamelium.json b/src/main/resources/data/opencomputers/tags/items/chamelium.json new file mode 100644 index 0000000000..c01615e648 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/chamelium.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chamelium" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/chipdiamond.json b/src/main/resources/data/opencomputers/tags/items/chipdiamond.json new file mode 100644 index 0000000000..87db4e1169 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/chipdiamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chipdiamond" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/chunkloaderupgrade.json b/src/main/resources/data/opencomputers/tags/items/chunkloaderupgrade.json new file mode 100644 index 0000000000..d430fc32dd --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/chunkloaderupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chunkloaderupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/circuitchip1.json b/src/main/resources/data/opencomputers/tags/items/circuitchip1.json new file mode 100644 index 0000000000..9dccfbae3f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/circuitchip1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chip1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/circuitchip2.json b/src/main/resources/data/opencomputers/tags/items/circuitchip2.json new file mode 100644 index 0000000000..c897187abb --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/circuitchip2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chip2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/circuitchip3.json b/src/main/resources/data/opencomputers/tags/items/circuitchip3.json new file mode 100644 index 0000000000..f5ba9efd4a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/circuitchip3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chip3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/componentbus1.json b/src/main/resources/data/opencomputers/tags/items/componentbus1.json new file mode 100644 index 0000000000..7f2ca6f071 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/componentbus1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:componentbus1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/componentbus2.json b/src/main/resources/data/opencomputers/tags/items/componentbus2.json new file mode 100644 index 0000000000..90a40eafa3 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/componentbus2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:componentbus2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/componentbus3.json b/src/main/resources/data/opencomputers/tags/items/componentbus3.json new file mode 100644 index 0000000000..098b9f43f1 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/componentbus3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:componentbus3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cpu1.json b/src/main/resources/data/opencomputers/tags/items/cpu1.json new file mode 100644 index 0000000000..a05cbf708f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cpu1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cpu1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cpu2.json b/src/main/resources/data/opencomputers/tags/items/cpu2.json new file mode 100644 index 0000000000..3d4e829a36 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cpu2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cpu2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cpu3.json b/src/main/resources/data/opencomputers/tags/items/cpu3.json new file mode 100644 index 0000000000..5205e031a2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cpu3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cpu3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/craftingupgrade.json b/src/main/resources/data/opencomputers/tags/items/craftingupgrade.json new file mode 100644 index 0000000000..d830ebe4f9 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/craftingupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:craftingupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/databaseupgrade1.json b/src/main/resources/data/opencomputers/tags/items/databaseupgrade1.json new file mode 100644 index 0000000000..be39cac481 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/databaseupgrade1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:databaseupgrade1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/databaseupgrade2.json b/src/main/resources/data/opencomputers/tags/items/databaseupgrade2.json new file mode 100644 index 0000000000..96e6e155bb --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/databaseupgrade2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:databaseupgrade2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/databaseupgrade3.json b/src/main/resources/data/opencomputers/tags/items/databaseupgrade3.json new file mode 100644 index 0000000000..b58c34e9d2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/databaseupgrade3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:databaseupgrade3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/datacard1.json b/src/main/resources/data/opencomputers/tags/items/datacard1.json new file mode 100644 index 0000000000..8eab0f3c6f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/datacard1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:datacard1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/datacard2.json b/src/main/resources/data/opencomputers/tags/items/datacard2.json new file mode 100644 index 0000000000..d654568b40 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/datacard2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:datacard2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/datacard3.json b/src/main/resources/data/opencomputers/tags/items/datacard3.json new file mode 100644 index 0000000000..7d458b23c6 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/datacard3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:datacard3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/diskdrivemountable.json b/src/main/resources/data/opencomputers/tags/items/diskdrivemountable.json new file mode 100644 index 0000000000..8fff553ef6 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/diskdrivemountable.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:diskdrivemountable" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/dronecase1.json b/src/main/resources/data/opencomputers/tags/items/dronecase1.json new file mode 100644 index 0000000000..7c190a7b5d --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/dronecase1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:dronecase1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/dronecase2.json b/src/main/resources/data/opencomputers/tags/items/dronecase2.json new file mode 100644 index 0000000000..aa73252c2e --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/dronecase2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:dronecase2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/eeprom.json b/src/main/resources/data/opencomputers/tags/items/eeprom.json new file mode 100644 index 0000000000..047c279d2b --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/eeprom.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:eeprom" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/experienceupgrade.json b/src/main/resources/data/opencomputers/tags/items/experienceupgrade.json new file mode 100644 index 0000000000..5a1b53a755 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/experienceupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:experienceupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/floppy.json b/src/main/resources/data/opencomputers/tags/items/floppy.json new file mode 100644 index 0000000000..b8a10369a6 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/floppy.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:floppy" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/generatorupgrade.json b/src/main/resources/data/opencomputers/tags/items/generatorupgrade.json new file mode 100644 index 0000000000..97bb3d82dd --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/generatorupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:generatorupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/graphicscard1.json b/src/main/resources/data/opencomputers/tags/items/graphicscard1.json new file mode 100644 index 0000000000..26d1c3e174 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/graphicscard1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:graphicscard1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/graphicscard2.json b/src/main/resources/data/opencomputers/tags/items/graphicscard2.json new file mode 100644 index 0000000000..b5ea28761a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/graphicscard2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:graphicscard2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/graphicscard3.json b/src/main/resources/data/opencomputers/tags/items/graphicscard3.json new file mode 100644 index 0000000000..30137a5fd7 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/graphicscard3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:graphicscard3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hdd1.json b/src/main/resources/data/opencomputers/tags/items/hdd1.json new file mode 100644 index 0000000000..d1ecab3167 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hdd1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hdd1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hdd2.json b/src/main/resources/data/opencomputers/tags/items/hdd2.json new file mode 100644 index 0000000000..817933c135 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hdd2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hdd2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hdd3.json b/src/main/resources/data/opencomputers/tags/items/hdd3.json new file mode 100644 index 0000000000..5330fcb450 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hdd3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hdd3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hoverboots.json b/src/main/resources/data/opencomputers/tags/items/hoverboots.json new file mode 100644 index 0000000000..838b71f60c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hoverboots.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hoverboots" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hoverupgrade1.json b/src/main/resources/data/opencomputers/tags/items/hoverupgrade1.json new file mode 100644 index 0000000000..8473c87e8f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hoverupgrade1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hoverupgrade1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hoverupgrade2.json b/src/main/resources/data/opencomputers/tags/items/hoverupgrade2.json new file mode 100644 index 0000000000..c801020560 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hoverupgrade2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hoverupgrade2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/inkcartridge.json b/src/main/resources/data/opencomputers/tags/items/inkcartridge.json new file mode 100644 index 0000000000..9d3dd3a212 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/inkcartridge.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:inkcartridge" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/inkcartridgeempty.json b/src/main/resources/data/opencomputers/tags/items/inkcartridgeempty.json new file mode 100644 index 0000000000..d67e451ba5 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/inkcartridgeempty.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:inkcartridgeempty" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/internetcard.json b/src/main/resources/data/opencomputers/tags/items/internetcard.json new file mode 100644 index 0000000000..29a889c3f3 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/internetcard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:internetcard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/inventorycontrollerupgrade.json b/src/main/resources/data/opencomputers/tags/items/inventorycontrollerupgrade.json new file mode 100644 index 0000000000..7f9fd69e43 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/inventorycontrollerupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:inventorycontrollerupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/inventoryupgrade.json b/src/main/resources/data/opencomputers/tags/items/inventoryupgrade.json new file mode 100644 index 0000000000..b9fdb194fb --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/inventoryupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:inventoryupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/lancard.json b/src/main/resources/data/opencomputers/tags/items/lancard.json new file mode 100644 index 0000000000..9562ae20d9 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/lancard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:lancard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/leashupgrade.json b/src/main/resources/data/opencomputers/tags/items/leashupgrade.json new file mode 100644 index 0000000000..a83ef51852 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/leashupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:leashupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/linkedcard.json b/src/main/resources/data/opencomputers/tags/items/linkedcard.json new file mode 100644 index 0000000000..1066cce0d2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/linkedcard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:linkedcard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/manual.json b/src/main/resources/data/opencomputers/tags/items/manual.json new file mode 100644 index 0000000000..d9b481c608 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/manual.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:manual" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialacid.json b/src/main/resources/data/opencomputers/tags/items/materialacid.json new file mode 100644 index 0000000000..e8ccf9c9fd --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialacid.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:acid" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialalu.json b/src/main/resources/data/opencomputers/tags/items/materialalu.json new file mode 100644 index 0000000000..5685dd87f8 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialalu.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:alu" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialarrowkey.json b/src/main/resources/data/opencomputers/tags/items/materialarrowkey.json new file mode 100644 index 0000000000..3f5f54fbed --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialarrowkey.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:arrowkeys" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialbuttongroup.json b/src/main/resources/data/opencomputers/tags/items/materialbuttongroup.json new file mode 100644 index 0000000000..e94376c866 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialbuttongroup.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:buttongroup" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcard.json b/src/main/resources/data/opencomputers/tags/items/materialcard.json new file mode 100644 index 0000000000..d3ebfaeb8a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:card" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcircuitboard.json b/src/main/resources/data/opencomputers/tags/items/materialcircuitboard.json new file mode 100644 index 0000000000..88fb275eaa --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcircuitboard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:circuitboard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcircuitboardprinted.json b/src/main/resources/data/opencomputers/tags/items/materialcircuitboardprinted.json new file mode 100644 index 0000000000..4c9fe89560 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcircuitboardprinted.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:printedcircuitboard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcircuitboardraw.json b/src/main/resources/data/opencomputers/tags/items/materialcircuitboardraw.json new file mode 100644 index 0000000000..f14a4adfce --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcircuitboardraw.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:rawcircuitboard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcu.json b/src/main/resources/data/opencomputers/tags/items/materialcu.json new file mode 100644 index 0000000000..91d0fbdca8 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcu.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cu" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialcuttingwire.json b/src/main/resources/data/opencomputers/tags/items/materialcuttingwire.json new file mode 100644 index 0000000000..4eb1e57db2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialcuttingwire.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cuttingwire" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialdisk.json b/src/main/resources/data/opencomputers/tags/items/materialdisk.json new file mode 100644 index 0000000000..3e078d2187 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialdisk.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:disk" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialinterweb.json b/src/main/resources/data/opencomputers/tags/items/materialinterweb.json new file mode 100644 index 0000000000..fc870e7668 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialinterweb.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:interweb" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialnumpad.json b/src/main/resources/data/opencomputers/tags/items/materialnumpad.json new file mode 100644 index 0000000000..086fe84d12 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialnumpad.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:numpad" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/materialtransistor.json b/src/main/resources/data/opencomputers/tags/items/materialtransistor.json new file mode 100644 index 0000000000..7c9a852165 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/materialtransistor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:transistor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/mfu.json b/src/main/resources/data/opencomputers/tags/items/mfu.json new file mode 100644 index 0000000000..b50de136d8 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/mfu.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:mfu" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/microcontrollercase1.json b/src/main/resources/data/opencomputers/tags/items/microcontrollercase1.json new file mode 100644 index 0000000000..43f32db8c9 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/microcontrollercase1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:microcontrollercase1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/microcontrollercase2.json b/src/main/resources/data/opencomputers/tags/items/microcontrollercase2.json new file mode 100644 index 0000000000..b153013c2c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/microcontrollercase2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:microcontrollercase2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/nanomachines.json b/src/main/resources/data/opencomputers/tags/items/nanomachines.json new file mode 100644 index 0000000000..a332667aa0 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/nanomachines.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:nanomachines" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/navigationupgrade.json b/src/main/resources/data/opencomputers/tags/items/navigationupgrade.json new file mode 100644 index 0000000000..7ac0d12787 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/navigationupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:navigationupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/pistonupgrade.json b/src/main/resources/data/opencomputers/tags/items/pistonupgrade.json new file mode 100644 index 0000000000..20eda93a1c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/pistonupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:pistonupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram1.json b/src/main/resources/data/opencomputers/tags/items/ram1.json new file mode 100644 index 0000000000..a4dcf07983 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram2.json b/src/main/resources/data/opencomputers/tags/items/ram2.json new file mode 100644 index 0000000000..6c87031c7b --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram3.json b/src/main/resources/data/opencomputers/tags/items/ram3.json new file mode 100644 index 0000000000..7f610e2893 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram4.json b/src/main/resources/data/opencomputers/tags/items/ram4.json new file mode 100644 index 0000000000..a51a2f174a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram4.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram4" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram5.json b/src/main/resources/data/opencomputers/tags/items/ram5.json new file mode 100644 index 0000000000..f9db65e3d0 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram5.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram5" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/ram6.json b/src/main/resources/data/opencomputers/tags/items/ram6.json new file mode 100644 index 0000000000..fdc3a46ab4 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/ram6.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:ram6" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/redstonecard1.json b/src/main/resources/data/opencomputers/tags/items/redstonecard1.json new file mode 100644 index 0000000000..f3b377bdcf --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/redstonecard1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:redstonecard1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/redstonecard2.json b/src/main/resources/data/opencomputers/tags/items/redstonecard2.json new file mode 100644 index 0000000000..99f07d8657 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/redstonecard2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:redstonecard2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/server1.json b/src/main/resources/data/opencomputers/tags/items/server1.json new file mode 100644 index 0000000000..4d02054e2f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/server1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:server1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/server2.json b/src/main/resources/data/opencomputers/tags/items/server2.json new file mode 100644 index 0000000000..bec50adc68 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/server2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:server2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/server3.json b/src/main/resources/data/opencomputers/tags/items/server3.json new file mode 100644 index 0000000000..513579991a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/server3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:server3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/signupgrade.json b/src/main/resources/data/opencomputers/tags/items/signupgrade.json new file mode 100644 index 0000000000..a23e2aa3dd --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/signupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:signupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/solargeneratorupgrade.json b/src/main/resources/data/opencomputers/tags/items/solargeneratorupgrade.json new file mode 100644 index 0000000000..10b9e99dca --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/solargeneratorupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:solargeneratorupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/stickypistonupgrade.json b/src/main/resources/data/opencomputers/tags/items/stickypistonupgrade.json new file mode 100644 index 0000000000..4c5f04cff8 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/stickypistonupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:stickypistonupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tabletcase1.json b/src/main/resources/data/opencomputers/tags/items/tabletcase1.json new file mode 100644 index 0000000000..5e42ce3d02 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tabletcase1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tabletcase1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tabletcase2.json b/src/main/resources/data/opencomputers/tags/items/tabletcase2.json new file mode 100644 index 0000000000..d971384a20 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tabletcase2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tabletcase2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tankcontrollerupgrade.json b/src/main/resources/data/opencomputers/tags/items/tankcontrollerupgrade.json new file mode 100644 index 0000000000..31fd709c36 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tankcontrollerupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tankcontrollerupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tankupgrade.json b/src/main/resources/data/opencomputers/tags/items/tankupgrade.json new file mode 100644 index 0000000000..29f0047b03 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tankupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tankupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/terminal.json b/src/main/resources/data/opencomputers/tags/items/terminal.json new file mode 100644 index 0000000000..aa9109ac64 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/terminal.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:terminal" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/terminalserver.json b/src/main/resources/data/opencomputers/tags/items/terminalserver.json new file mode 100644 index 0000000000..ce1e0bd0cf --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/terminalserver.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:terminalserver" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/texturepicker.json b/src/main/resources/data/opencomputers/tags/items/texturepicker.json new file mode 100644 index 0000000000..12d8e59604 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/texturepicker.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:texturepicker" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tractorbeamupgrade.json b/src/main/resources/data/opencomputers/tags/items/tractorbeamupgrade.json new file mode 100644 index 0000000000..2d1d93a476 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tractorbeamupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tractorbeamupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/tradingupgrade.json b/src/main/resources/data/opencomputers/tags/items/tradingupgrade.json new file mode 100644 index 0000000000..5fc253cc65 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/tradingupgrade.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:tradingupgrade" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/upgradecontainer1.json b/src/main/resources/data/opencomputers/tags/items/upgradecontainer1.json new file mode 100644 index 0000000000..d4d4f8100c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/upgradecontainer1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:upgradecontainer1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/upgradecontainer2.json b/src/main/resources/data/opencomputers/tags/items/upgradecontainer2.json new file mode 100644 index 0000000000..ed7a840a9e --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/upgradecontainer2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:upgradecontainer2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/upgradecontainer3.json b/src/main/resources/data/opencomputers/tags/items/upgradecontainer3.json new file mode 100644 index 0000000000..50a255cca5 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/upgradecontainer3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:upgradecontainer3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/wlancard1.json b/src/main/resources/data/opencomputers/tags/items/wlancard1.json new file mode 100644 index 0000000000..50b4f85d5f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/wlancard1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:wlancard1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/wlancard2.json b/src/main/resources/data/opencomputers/tags/items/wlancard2.json new file mode 100644 index 0000000000..df25029370 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/wlancard2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:wlancard2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/wrench.json b/src/main/resources/data/opencomputers/tags/items/wrench.json new file mode 100644 index 0000000000..af355bef28 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/wrench.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:wrench" + ] +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala index c73a967d55..cb334056a5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemStackImageRenderer.scala @@ -24,8 +24,12 @@ private[markdown] class ItemStackImageRenderer(val stacks: Array[ItemStack]) ext val stack = stacks(index) matrix.scale(getWidth / 16, getHeight / 16, getWidth / 16) + // Translate manually because ItemRenderer generally can't take a MatrixStack. + RenderSystem.pushMatrix() + RenderSystem.multMatrix(matrix.last().pose()) RenderSystem.enableRescaleNormal() RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240, 240) mc.getItemRenderer.renderAndDecorateItem(stack, 0, 0) + RenderSystem.popMatrix() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala index 82f08afc5b..220ef8358c 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/OreDictImageProvider.scala @@ -4,6 +4,8 @@ import li.cil.oc.api.manual.ImageProvider import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer import li.cil.oc.client.Textures +import net.minecraft.block.Block +import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.tags._ import net.minecraft.util.ResourceLocation @@ -15,11 +17,15 @@ object OreDictImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { val desired = new ResourceLocation(data.toLowerCase) val stacks = mutable.ArrayBuffer.empty[ItemStack] - ItemTags.getWrappers.find(t => desired.equals(t.getName)).foreach { - stacks ++= _.getValues.map(new ItemStack(_)) + ItemTags.getAllTags.getTag(desired) match { + case tag: ITag[Item] => stacks ++= tag.getValues.map(new ItemStack(_)) + case _ => } - ItemTags.getWrappers.find(t => desired.equals(t.getName)).foreach { - stacks ++= _.getValues.map(new ItemStack(_)) + if (stacks.isEmpty) { + BlockTags.getAllTags.getTag(desired) match { + case tag: ITag[Block] => stacks ++= tag.getValues.map(new ItemStack(_)) + case _ => + } } if (stacks.nonEmpty) new ItemStackImageRenderer(stacks.toArray) else new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { From 7d07ad2665f06073d33fe5a1301091338f7d17c3 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 18:11:50 +0200 Subject: [PATCH 054/159] Fix Manual rendering clipping incorrectly --- .../scala/li/cil/oc/client/renderer/markdown/Document.scala | 2 ++ .../cil/oc/client/renderer/markdown/segment/RenderSegment.scala | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala index 4624f80a6b..90965064b1 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala @@ -139,6 +139,8 @@ object Document { if (mouseX < x || mouseX > x + maxWidth || mouseY < y || mouseY > y + maxHeight) hovered = None hovered.foreach(_.notifyHover()) + // Remove the depth mask so tooltips render properly. + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, false) RenderState.popAttrib() RenderSystem.bindTexture(0) diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala index a106ed6726..d8ace50bc5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala @@ -53,6 +53,8 @@ private[markdown] class RenderSegment(val parent: Segment, val title: String, va RenderSystem.enableBlend() RenderSystem.enableAlphaTest() + // Disabled by text rendering above it (default state is disabled). + RenderSystem.enableDepthTest() if (hovered.isDefined) { RenderSystem.color4f(1, 1, 1, 0.15f) From 1eaa73795b85729272a3c6e24997b01b64b4d58f Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 21:26:57 +0200 Subject: [PATCH 055/159] Fix Manual scrollbar not being visible --- src/main/scala/li/cil/oc/client/gui/traits/Window.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/gui/traits/Window.scala b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala index 18487bded8..f3b3778a61 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/Window.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala @@ -31,10 +31,11 @@ trait Window extends Screen { } override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float): Unit = { - super.render(stack, mouseX, mouseY, dt) Minecraft.getInstance.getTextureManager.bind(backgroundImage) // Texture width and height are intentionally backwards. AbstractGui.blit(stack, leftPos, topPos, getBlitOffset, 0, 0, imageWidth, imageHeight, windowHeight, windowWidth) + + super.render(stack, mouseX, mouseY, dt) } } From 291f269ca56962369ff4b1e4168f8c25385f6bb2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 21:51:21 +0200 Subject: [PATCH 056/159] Fix Manual buttons not working --- .../scala/li/cil/oc/client/gui/Manual.scala | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index 09860395e3..3fe2c65bce 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -40,6 +40,7 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window override def backgroundImage = Textures.GUI.Manual + var isScrolling = false var document: Segment = null var documentHeight = 0 var currentSegment = None: Option[InteractiveSegment] @@ -109,7 +110,7 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window super.render(stack, mouseX, mouseY, dt) scrollButton.active = canScroll - scrollButton.hoverOverride = isDragging + scrollButton.hoverOverride = isScrolling for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { val button = buttons.get(i).asInstanceOf[ImageButton] @@ -125,7 +126,7 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window seqAsJavaList(lines.toSeq) } - if (!isDragging) currentSegment match { + if (!isScrolling) currentSegment match { case Some(segment) => segment.tooltip match { case Some(text) if text.nonEmpty => renderWrappedToolTip(stack, localizeAndWrap(text), mouseX, mouseY, font) @@ -134,14 +135,14 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window case _ => } - if (!isDragging) for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { + if (!isScrolling) for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { val button = buttons.get(i).asInstanceOf[ImageButton] if (mouseX > button.x && mouseX < button.x + tabWidth && mouseY > button.y && mouseY < button.y + tabHeight) tab.tooltip.foreach(text => { renderWrappedToolTip(stack, localizeAndWrap(text), mouseX, mouseY, font) }) } - if (canScroll && (isCoordinateOverScrollBar(mouseX - leftPos, mouseY - topPos) || isDragging)) { + if (canScroll && (isCoordinateOverScrollBar(mouseX - leftPos, mouseY - topPos) || isScrolling)) { val lines = seqAsJavaList(Seq(new StringTextComponent(s"${100 * offset / maxOffset}%"))) renderWrappedToolTip(stack, lines, leftPos + scrollPosX + scrollWidth, scrollButton.y + scrollButton.getHeight + 1, font) } @@ -167,14 +168,16 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window } override def mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean = { - if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverScrollBar(mouseX.asInstanceOf[Int] - leftPos, mouseY.asInstanceOf[Int] - topPos)) { - setDragging(true) + val (mcx, mcy) = (mouseX.asInstanceOf[Int] - leftPos, mouseY.asInstanceOf[Int] - topPos) + if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverScrollBar(mcx, mcy)) { + isScrolling = true scrollMouse(mouseY) return true } - if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT) { - currentSegment.foreach(_.onMouseClick(mouseX.asInstanceOf[Int], mouseY.asInstanceOf[Int])) - return true + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverContent(mcx, mcy)) { + if (currentSegment.exists(_.onMouseClick(mouseX.asInstanceOf[Int], mouseY.asInstanceOf[Int]))) { + return true + } } if (button == GLFW.GLFW_MOUSE_BUTTON_RIGHT) { popPage() @@ -184,12 +187,12 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window } override def mouseMoved(mouseX: Double, mouseY: Double): Unit = { - if (isDragging) scrollMouse(mouseY) + if (isScrolling) scrollMouse(mouseY) super.mouseMoved(mouseX, mouseY) } override def mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean = { - if (isDragging) { + if (isScrolling) { scrollMouse(mouseY) return true } @@ -197,8 +200,8 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window } override def mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean = { - if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT) { - setDragging(false) + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isScrolling) { + isScrolling = false return true } super.mouseReleased(mouseX, mouseY, button) @@ -223,7 +226,11 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window } } + private def isCoordinateOverContent(x: Int, y: Int) = + x >= 8 && x < 8 + documentMaxWidth && + y >= 8 && y < 8 + documentMaxHeight + private def isCoordinateOverScrollBar(x: Int, y: Int) = - x > scrollPosX && x < scrollPosX + scrollWidth && + x >= scrollPosX && x < scrollPosX + scrollWidth && y >= scrollPosY && y < scrollPosY + scrollHeight } From 16d6f88570ef2d9f5a57bbf0a4e11bddf4170eb4 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:13:44 +0200 Subject: [PATCH 057/159] Fix DynamicFontRenderer crash --- .../li/cil/oc/client/renderer/font/DynamicFontRenderer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala index 6bf0a4cc55..6d5e36a069 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala @@ -45,13 +45,13 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa } textures.clear() charMap.clear() + glyphProvider.initialize() textures += new DynamicFontRenderer.CharTexture(this) activeTexture = textures.head generateChars(basicChars.toCharArray) } def onResourceManagerReload(manager: IResourceManager) { - glyphProvider.initialize() initialize() } From 9bccfc9d029821e71be141aae5847db5878bd09b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:26:34 +0200 Subject: [PATCH 058/159] Fix crash due to unfinished batch rendering --- src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala index 11e9734c32..aa32642b81 100644 --- a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala @@ -92,14 +92,15 @@ abstract class CustomGuiContainer[C <: Container](val inventoryContainer: C, inv stack.pushPose() stack.translate(0, 0, 400) - val renderType = IRenderTypeBuffer.immediate(Tessellator.getInstance.getBuilder()) + val buffer = IRenderTypeBuffer.immediate(Tessellator.getInstance.getBuilder()) for ((line, index) <- text.zipWithIndex) { - font.drawInBatch(LanguageMap.getInstance.getVisualOrder(line), posX, posY, -1, true, stack.last.pose, renderType, false, 0, 15728880) + font.drawInBatch(LanguageMap.getInstance.getVisualOrder(line), posX, posY, -1, true, stack.last.pose, buffer, false, 0, 15728880) if (index == 0) { posY += 2 } posY += 10 } + buffer.endBatch() stack.popPose() setBlitOffset(0) itemRenderer.blitOffset = 0f From f8fb67897c6f884ce497d2d75f1c80aa310c293d Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 5 Sep 2022 23:43:53 +0200 Subject: [PATCH 059/159] Remove damage-based item system from Manual --- .../opencomputers/doc/de_de/item/debugcard.md | 2 +- .../assets/opencomputers/doc/de_de/item/drone.md | 2 +- .../assets/opencomputers/doc/de_de/item/tablet.md | 2 +- .../opencomputers/doc/en_us/general/example.md | 2 +- .../opencomputers/doc/en_us/item/debugcard.md | 2 +- .../assets/opencomputers/doc/en_us/item/drone.md | 2 +- .../assets/opencomputers/doc/en_us/item/tablet.md | 2 +- .../opencomputers/doc/fr_fr/general/example.md | 2 +- .../opencomputers/doc/fr_fr/item/debugCard.md | 2 +- .../assets/opencomputers/doc/fr_fr/item/drone.md | 2 +- .../assets/opencomputers/doc/fr_fr/item/tablet.md | 2 +- .../opencomputers/doc/ru_ru/general/example.md | 2 +- .../opencomputers/doc/ru_ru/item/debugCard.md | 2 +- .../assets/opencomputers/doc/ru_ru/item/drone.md | 2 +- .../assets/opencomputers/doc/ru_ru/item/tablet.md | 2 +- .../opencomputers/doc/zh_cn/general/example.md | 2 +- .../opencomputers/doc/zh_cn/item/debugcard.md | 2 +- .../assets/opencomputers/doc/zh_cn/item/drone.md | 2 +- .../assets/opencomputers/doc/zh_cn/item/tablet.md | 2 +- .../segment/render/BlockImageProvider.scala | 14 ++------------ .../segment/render/ItemImageProvider.scala | 12 ++---------- 21 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/debugcard.md b/src/main/resources/assets/opencomputers/doc/de_de/item/debugcard.md index 2e7cd6dae7..0cc40e7999 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/debugcard.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/debugcard.md @@ -1,6 +1,6 @@ # Debugkarte -![Warte - Wenn ich... oooooh.](item:OpenComputers:item@73) +![Warte - Wenn ich... oooooh.](item:opencomputers:debugcard) Die Debugkarte ist ein Nur-Kreativ-Item das ursprünglich entwickelt wurde, um einige Vorgänge zu vereinfachen in dem sie einige Prozesse automatisieren. Es hat seitdem eine Vielfalt an Zusatzfunktionen erhalten. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/drone.md b/src/main/resources/assets/opencomputers/doc/de_de/item/drone.md index 81a24f77ad..4e1bda25dd 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/drone.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/drone.md @@ -1,5 +1,5 @@ # Drohne -![Hat nichts mit der NSA zu tun..](item:OpenComputers:item@84) +![Hat nichts mit der NSA zu tun..](item:opencomputers:drone) Drohnen werden mit einem [Drohnengehäuse](droneCase1.md) in der [Elektronik-Werkbank](../block/assembler.md) gebaut. Sie sind entitybasierende [Roboter](../block/robot.md), allerdings ein bisschen günstiger und mit eingeschränkter Funktionalität. Sie können sich zudem weitaus schneller bewegen als [Roboter](../block/robot.md) und werden über ein Clientprogramm auf einem Computer gesteuert. Die Drohne benötigt ein konfiguriertes [EEPROM](eeprom.md) um Befehle zu empfangen oder selbst zu arbeiten. diff --git a/src/main/resources/assets/opencomputers/doc/de_de/item/tablet.md b/src/main/resources/assets/opencomputers/doc/de_de/item/tablet.md index ca3ec45db0..b8339f353a 100644 --- a/src/main/resources/assets/opencomputers/doc/de_de/item/tablet.md +++ b/src/main/resources/assets/opencomputers/doc/de_de/item/tablet.md @@ -1,6 +1,6 @@ # Tablet -![Berühr' mich, wenn du kannst.](item:OpenComputers:item@68) +![Berühr' mich, wenn du kannst.](item:opencomputers:tablet) Tablets werden gebaut, indem ein [Tabletgehäuse](tabletCase1.md) in einer [Elektronik-Werkbank](../block/assembler.md) eingesetzt, konfiguriert und montiert wird. Tablets sind tragbare Computer die nicht direkt mit der Welt interagieren können. Das bedeutet, dass beispielsweise [Redstonekarten](redstoneCard1.md) nicht darin funktionieren. Eine Vielfalt von Upgrades funktionieren schon, wie das [Schild-I/O](signUpgrade.md) oder das [Kolbenupgrade](pistonUpgrade.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_us/general/example.md b/src/main/resources/assets/opencomputers/doc/en_us/general/example.md index c381cc82b5..8db963f6c1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/general/example.md @@ -7,7 +7,7 @@ This is some test text for the subset of Markdown supported by the planned ingam ![This is rendered live.](oredict:opencomputers:assembler) ## Smaller headline [also with *link* but this __one__ longer](../block/adapter.md) -![This is another tooltip.](item:opencomputers:item@23) +![This is another tooltip.](item:opencomputers:transistor) some text directly above the item stack renderer to test spacing ![All the colors.](oredict:forge/piston) diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/debugcard.md b/src/main/resources/assets/opencomputers/doc/en_us/item/debugcard.md index aa35df5a79..ee40ba57b1 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/debugcard.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/debugcard.md @@ -1,6 +1,6 @@ # Debug Card -![Wait, if I- oooooh.](item:OpenComputers:item@73) +![Wait, if I- oooooh.](item:opencomputers:debugcard) The debug card is a creative-only item that was originally only intended to make debugging things easier, by automating some processes. It has since gotten a bunch more functionality, making it quite useful for custom map-making. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/drone.md b/src/main/resources/assets/opencomputers/doc/en_us/item/drone.md index e639c1982a..981f6cbd35 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/drone.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/drone.md @@ -1,5 +1,5 @@ # Drone -![Big brother is trying to watch you.](item:OpenComputers:item@84) +![Big brother is trying to watch you.](item:opencomputers:drone) Drones are built using a [drone case](droneCase1.md) in the [assembler](../block/assembler.md). They are entity-based [robots](../block/robot.md), a little cheaper with limited functionality. They are also capable of moving differently and much quicker than [robots](../block/robot.md), and are usually controlled using a client program on a [computer](../general/computer.md). The drone will need a configured [EEPROM](eeprom.md) to listen for commands to be executed or act on its own. diff --git a/src/main/resources/assets/opencomputers/doc/en_us/item/tablet.md b/src/main/resources/assets/opencomputers/doc/en_us/item/tablet.md index eab94a8166..3d66013e9c 100644 --- a/src/main/resources/assets/opencomputers/doc/en_us/item/tablet.md +++ b/src/main/resources/assets/opencomputers/doc/en_us/item/tablet.md @@ -1,6 +1,6 @@ # Tablet -![Touch me if you can.](item:OpenComputers:item@68) +![Touch me if you can.](item:opencomputers:tablet) Tablets are built by placing a [tablet case](tabletCase1.md) into an [assembler](../block/assembler.md), configuring as desired and assembling it. Tablets act as portable computers that cannot directly interact with the world - for example, basic [redstone cards](redstoneCard1.md) do not work in them. A number of upgrades do, such as the [sign i/o](signUpgrade.md) upgrade or the [piston](pistonUpgrade.md) upgrade. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md b/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md index d76a890c27..8c17aeea08 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/general/example.md @@ -7,7 +7,7 @@ Voici un peu de texte de test pour la version de Markdown supportée par le syst ![C'est rendu en direct.](oredict:opencomputers:assembler) ## Entête plus petite [avec un *lien* aussi mais cette __fois__ plus long](../block/adapter.md) -![Ceci est une autre info-bulle.](item:opencomputers:item@23) +![Ceci est une autre info-bulle.](item:opencomputers:transistor) un peu de texte directement au dessus de l'afficheur d'objet pour tester l'espacement ![Toutes ces couleurs.](oredict:forge/piston) diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/debugCard.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/debugCard.md index 092dfc1e93..9fc2084468 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/debugCard.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/debugCard.md @@ -1,6 +1,6 @@ # Carte de débogueur -![Attends, si je... oooooh.](item:OpenComputers:item@73) +![Attends, si je... oooooh.](item:opencomputers:debugcard) La carte de débogueur est un objet à usage créatif seulement, qui a été créé à l'origine pour rendre le débogage plus facile, en automatisant certains processus. Depuis, elle a obtenu beaucoup d'autres fonctionnalités, ce qui la rend très utile pour la création de cartes personnalisées. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/drone.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/drone.md index b917b54fbc..0953a7a15e 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/drone.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/drone.md @@ -1,5 +1,5 @@ # Drone -![Big brother essaye de vous regarder.](item:OpenComputers:item@84) +![Big brother essaye de vous regarder.](item:opencomputers:drone) Les drones sont faits en mettant un [boîtier de drone](droneCase1.md) dans un [assembleur](../block/assembler.md). Ce sont des [robots](../block/robot.md) sous forme d'entité, moins chers mais avec moins de fonctionnalités. Ils sont également capables de se déplacer différemment et beaucoup plus vite que les [robots](../block/robot.md), et ils sont généralement controlés en utilisant un programme client sur un [ordinateur](../general/computer.md). Le drone devra avoir une [EEPROM](eeprom.md) configurée pour écouter les commandes entrantes à exécuter, ou pour agir de lui même. diff --git a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tablet.md b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tablet.md index e200eb873c..646f36923f 100644 --- a/src/main/resources/assets/opencomputers/doc/fr_fr/item/tablet.md +++ b/src/main/resources/assets/opencomputers/doc/fr_fr/item/tablet.md @@ -1,6 +1,6 @@ # Tablette -![Touche moi si tu peux.](item:OpenComputers:item@68) +![Touche moi si tu peux.](item:opencomputers:tablet) Les tablettes sont faites en mettant un [boîtier de tablette](tabletCase1.md) dans un [assembleur](../block/assembler.md), en le configurant comme vous le voulez et en assemblant. Les tablettes se comportent comme des ordinateurs portables qui ne peuvent pas interagir directement avec le monde - par exemple, les [cartes de redstone](redstoneCard1.md) de base ne fonctionnent pas avec une tablette. Cependant, un certain nombre d'améliorations fonctionne, comme l'[amélioration de panneau d'E/S](signUpgrade.md) ou l'[amélioration de piston](pistonUpgrade.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md b/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md index 29cf185efc..dac2b44b6a 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/general/example.md @@ -7,7 +7,7 @@ ![Это рендирится напрямую.](oredict:opencomputers:assembler) ## Заголовок второго уровня [снова со *ссылкой*, но __немного__ длиннее](../block/adapter.md) -![Это еще одно всплывающее сообщение.](item:opencomputers:item@23) +![Это еще одно всплывающее сообщение.](item:opencomputers:transistor) какой-то текст прямо над изображением для тестирования отступов ![Все цвета.](oredict:forge/piston) diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/debugCard.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/debugCard.md index 11100abfd0..31d6af7079 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/debugCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/debugCard.md @@ -1,6 +1,6 @@ # Отладочная карта -![Подождите.](item:OpenComputers:item@73) +![Подождите.](item:opencomputers:debugcard) Отладочная карта - это творческий предмет, который изначально был предназначен для упрощения отладки мода за счет автоматизации некоторых процессов. С тех пор она обрела большую функциональность, что делает ее полезным инструментом для создания карт. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/drone.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/drone.md index 48679a7305..2451397a04 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/drone.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/drone.md @@ -1,5 +1,5 @@ # Дрон -![Большой брат пытается следить за тобой.](item:OpenComputers:item@84) +![Большой брат пытается следить за тобой.](item:opencomputers:drone) Дроны собираются из [корпуса дрона](droneCase1.md) в [сборщике](../block/assembler.md). По сути, они являются [роботами](../block/robot.md) в виде сущностей, но с меньшим функционалом. Они также могут перемещаться по диагонали и гораздо быстрее, чем [роботы](../block/robot.md). Они обычно контролируются с помощью программы на [компьютере](../general/computer.md). Дроны могут быть сконфигурированы с помощью [EEPROM](eeprom.md) для выполнения различных комманд. diff --git a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tablet.md b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tablet.md index 0c6ee06439..39dc35d73b 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_ru/item/tablet.md +++ b/src/main/resources/assets/opencomputers/doc/ru_ru/item/tablet.md @@ -1,6 +1,6 @@ # Планшет -![Дотронься, если сможешь.](item:OpenComputers:item@68) +![Дотронься, если сможешь.](item:opencomputers:tablet) Планшеты можно создать, если поместить [корпус планшета](tabletCase1.md) в [сборщик](../block/assembler.md), добавить компоненты и собрать. Планшеты работают как переносные компьютеры, которые не могут напрямую взаимодействовать с игровым миром: например, простые [платы на красном камне](redstoneCard1.md) не работают с ними. Также не могут с ними работать некоторые улучшения: [контроллер табличек](signUpgrade.md) или [поршень](pistonUpgrade.md), для примера. diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md b/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md index c381cc82b5..8db963f6c1 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/general/example.md @@ -7,7 +7,7 @@ This is some test text for the subset of Markdown supported by the planned ingam ![This is rendered live.](oredict:opencomputers:assembler) ## Smaller headline [also with *link* but this __one__ longer](../block/adapter.md) -![This is another tooltip.](item:opencomputers:item@23) +![This is another tooltip.](item:opencomputers:transistor) some text directly above the item stack renderer to test spacing ![All the colors.](oredict:forge/piston) diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/debugcard.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/debugcard.md index 2384a7b259..4170e36bd7 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/debugcard.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/debugcard.md @@ -1,6 +1,6 @@ # 调试卡 -![等等,如果…… 哦。](item:OpenComputers:item@73) +![等等,如果…… 哦。](item:opencomputers:debugcard) 调试卡本身是一种仅用于在创造模式下对设备进行快速调试的卡片。有鉴于其丰富的功能,它对于地图制作也很有用处。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/drone.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/drone.md index 7895611cf5..a6697cc7c6 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/drone.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/drone.md @@ -1,5 +1,5 @@ # 无人机 -![老大哥正试图看着你。](item:OpenComputers:item@84) +![老大哥正试图看着你。](item:opencomputers:drone) 无人机需通过在[装配器](../block/assembler.md)中装配[无人机箱](droneCase1.md)制造。他们是实体形态的[机器人](../block/robot.md),造价便宜但功能受限。无人机通常由[电脑](../general/computer.md)程序控制移动,移动速度比[机器人](../block/robot.md)快,而且方向也可以不一样。无人机需要 [EEPROM](eeprom.md) 来监听指令或独自行动。 diff --git a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tablet.md b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tablet.md index b413fe34f5..d6685a3905 100644 --- a/src/main/resources/assets/opencomputers/doc/zh_cn/item/tablet.md +++ b/src/main/resources/assets/opencomputers/doc/zh_cn/item/tablet.md @@ -1,6 +1,6 @@ # 平板电脑 -![如果可以,请摸我。](item:OpenComputers:item@68) +![如果可以,请摸我。](item:opencomputers:tablet) 平板电脑是在[装配机](../block/assembler.md)中通过配置[平板外壳](tabletCase1.md)并组装而成的。平板电脑本身是种无法直接与世界交互的移动计算机,比如[基础红石卡](redstoneCard1.md)就不能在平板上用,但像是[告示牌升级](signUpgrade.md)和[活塞升级](pistonUpgrade.md)这样的可以使用。 diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala index 6c632acba4..7329e253e0 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/BlockImageProvider.scala @@ -1,27 +1,17 @@ package li.cil.oc.client.renderer.markdown.segment.render -import com.google.common.base.Strings import li.cil.oc.api.manual.ImageProvider import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer -import li.cil.oc.client.Textures import net.minecraft.block.Block -import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation import net.minecraftforge.registries.ForgeRegistries object BlockImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { - val splitIndex = data.lastIndexOf('@') - val (name, optMeta) = if (splitIndex > 0) data.toLowerCase.splitAt(splitIndex) else (data.toLowerCase, "") - val meta = if (Strings.isNullOrEmpty(optMeta)) 0 else Integer.parseInt(optMeta.drop(1)) - ForgeRegistries.BLOCKS.getValue(new ResourceLocation(name)) match { - case block: Block if block.asItem() != null => { - val stack = new ItemStack(block, 1) - if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) - new ItemStackImageRenderer(Array(stack)) - } + ForgeRegistries.BLOCKS.getValue(new ResourceLocation(data.toLowerCase)) match { + case block: Block if block.asItem() != null => new ItemStackImageRenderer(Array(new ItemStack(block))) case _ => new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.BlockMissing" diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala index d909855149..9c3ebacec7 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/render/ItemImageProvider.scala @@ -1,10 +1,8 @@ package li.cil.oc.client.renderer.markdown.segment.render -import com.google.common.base.Strings import li.cil.oc.api.manual.ImageProvider import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer -import li.cil.oc.client.Textures import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation @@ -12,14 +10,8 @@ import net.minecraftforge.registries.ForgeRegistries object ItemImageProvider extends ImageProvider { override def getImage(data: String): ImageRenderer = { - val splitIndex = data.lastIndexOf('@') - val (name, optMeta) = if (splitIndex > 0) data.toLowerCase.splitAt(splitIndex) else (data.toLowerCase, "") - ForgeRegistries.ITEMS.getValue(new ResourceLocation(name)) match { - case item: Item => { - val stack = new ItemStack(item, 1) - if (!Strings.isNullOrEmpty(optMeta)) stack.setDamageValue(Integer.parseInt(optMeta.drop(1))) - new ItemStackImageRenderer(Array(stack)) - } + ForgeRegistries.ITEMS.getValue(new ResourceLocation(data.toLowerCase)) match { + case item: Item => new ItemStackImageRenderer(Array(new ItemStack(item))) case _ => new TextureImageRenderer(TextureImageProvider.ManualMissingItem) with InteractiveImageRenderer { override def getTooltip(tooltip: String): String = "oc:gui.Manual.Warning.ItemMissing" From 864ae2b80f8398ef0fb37601b38988ae8b0fb591 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 6 Sep 2022 00:30:32 +0200 Subject: [PATCH 060/159] Fix duplicate title rendering in inventories --- src/main/scala/li/cil/oc/client/gui/Assembler.scala | 2 ++ src/main/scala/li/cil/oc/client/gui/Database.scala | 2 ++ src/main/scala/li/cil/oc/client/gui/Disassembler.scala | 4 ++++ src/main/scala/li/cil/oc/client/gui/Drone.scala | 2 ++ .../li/cil/oc/client/gui/DynamicGuiContainer.scala | 10 ++++++---- src/main/scala/li/cil/oc/client/gui/Robot.scala | 2 ++ 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 2ce7fbad45..1ab3d63535 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -53,6 +53,8 @@ class Assembler(state: container.Assembler, playerInventory: PlayerInventory, na addButton(runButton) } + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int): Unit = { RenderState.pushAttrib() if (!inventoryContainer.isAssembling) { diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 54be9c2f7e..dc56882ce5 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -16,6 +16,8 @@ class Database(state: container.Database, playerInventory: PlayerInventory, name override def lockedStack = inventoryContainer.container + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) {} override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index f48e52235f..005fa9f7b8 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -13,6 +13,10 @@ class Disassembler(state: container.Disassembler, playerInventory: PlayerInvento val progress = addCustomWidget(new ProgressBar(18, 65)) + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { + font.draw(stack, title, titleLabelX, titleLabelY, 0x404040); + } + override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { RenderSystem.color3f(1, 1, 1) Textures.bind(Textures.GUI.Disassembler) diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 945d0c502e..8ccfc08a54 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -88,6 +88,8 @@ class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITex override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0 + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) RenderState.pushAttrib() diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index 5e68b4cd63..4a72240176 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -30,12 +30,14 @@ abstract class DynamicGuiContainer[C <: Container](container: C, inv: PlayerInve protected var hoveredStackNEI: StackOption = EmptyStack - protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { - font.drawShadow(stack, - Localization.localizeImmediately("container.inventory"), - 8, imageHeight - 96 + 2, 0x404040) + override protected def init() { + super.init() + // imageHeight is set in the body of the extending class, so it's not available in ours. + inventoryLabelY = imageHeight - 96 + 2 } + protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { super.renderLabels(stack, mouseX, mouseY) RenderState.pushAttrib() diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 79ac20c97f..6c522c859a 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -132,6 +132,8 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex } } + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) RenderState.pushAttrib() From 0823889529476fe4b0a5153af9cac8f17793e01b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 6 Sep 2022 00:47:39 +0200 Subject: [PATCH 061/159] Fix drone rendering crash --- .../li/cil/oc/client/renderer/entity/DroneRenderer.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala index f4ad998fd9..a6a7132373 100644 --- a/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/entity/DroneRenderer.scala @@ -11,6 +11,7 @@ import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.entity.EntityRenderer import net.minecraft.client.renderer.entity.EntityRendererManager import net.minecraft.client.renderer.texture.OverlayTexture +import net.minecraft.util.math.MathHelper import net.minecraftforge.fml.client.registry.IRenderFactory object DroneRenderer extends IRenderFactory[Drone] { @@ -26,6 +27,10 @@ class DroneRenderer(manager: EntityRendererManager) extends EntityRenderer[Drone stack.pushPose() stack.translate(0, 2f / 16f, 0) val builder = buffer.getBuffer(renderType) + DroneRenderer.model.prepareMobModel(entity, 0, 0, dt) + val xRot = MathHelper.rotLerp(dt, entity.xRotO, entity.xRot) + val yRot = MathHelper.rotLerp(dt, entity.yRotO, entity.yRot) + DroneRenderer.model.setupAnim(entity, 0, 0, entity.tickCount, yRot, xRot) DroneRenderer.model.renderToBuffer(stack, builder, light, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1) stack.popPose() } From 5abd0a7c1db9a55aa6c37c75748b86622ac071c1 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 6 Sep 2022 19:49:02 +0200 Subject: [PATCH 062/159] Fix Rack interconnect rendering and buttons --- .../li/cil/oc/client/gui/ImageButton.scala | 25 +++++++++---------- .../scala/li/cil/oc/client/gui/Rack.scala | 5 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/ImageButton.scala b/src/main/scala/li/cil/oc/client/gui/ImageButton.scala index 7e68bfdbb3..7306483be0 100644 --- a/src/main/scala/li/cil/oc/client/gui/ImageButton.scala +++ b/src/main/scala/li/cil/oc/client/gui/ImageButton.scala @@ -1,6 +1,7 @@ package li.cil.oc.client.gui import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures import net.minecraft.client.Minecraft @@ -57,22 +58,20 @@ class ImageButton(xPos: Int, yPos: Int, w: Int, h: Int, r.vertex(stack.last.pose, x1, y1, getBlitOffset).uv(u1, v1).endVertex() r.vertex(stack.last.pose, x1, y0, getBlitOffset).uv(u1, v0).endVertex() r.vertex(stack.last.pose, x0, y0, getBlitOffset).uv(u0, v0).endVertex() + t.end() } else { - if (drawHover) { - RenderSystem.color4f(1, 1, 1, 0.8f) - } - else { - RenderSystem.color4f(1, 1, 1, 0.4f) - } - - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION) - r.vertex(stack.last.pose, x0, y1, getBlitOffset).endVertex() - r.vertex(stack.last.pose, x1, y1, getBlitOffset).endVertex() - r.vertex(stack.last.pose, x1, y0, getBlitOffset).endVertex() - r.vertex(stack.last.pose, x0, y0, getBlitOffset).endVertex() + RenderSystem.enableBlend() + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA) + val alpha = if (drawHover) 0.8f else 0.4f + r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR) + r.vertex(stack.last.pose, x0, y1, getBlitOffset).color(1, 1, 1, alpha).endVertex() + r.vertex(stack.last.pose, x1, y1, getBlitOffset).color(1, 1, 1, alpha).endVertex() + r.vertex(stack.last.pose, x1, y0, getBlitOffset).color(1, 1, 1, alpha).endVertex() + r.vertex(stack.last.pose, x0, y0, getBlitOffset).color(1, 1, 1, alpha).endVertex() + t.end() + RenderSystem.disableBlend() } - t.end() if (getMessage != StringTextComponent.EMPTY) { val color = diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index 4861af022e..c2bac7f5b6 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -144,7 +144,7 @@ class Rack(state: container.Rack, playerInventory: PlayerInventory, name: ITextC for (connectable <- 0 until 3) { val button = new ImageButton(leftPos + bx, topPos + by + offset + 1 + mbh + sbh * connectable, sw, sh, new Button.IPressable { - override def onPress(b: Button) = onRackButton(mountable, connectable, bus) + override def onPress(b: Button) = onRackButton(mountable, connectable + 1, bus) }) addButton(button) wireButtons(mountable)(connectable + 1)(bus) = button @@ -157,7 +157,8 @@ class Rack(state: container.Rack, playerInventory: PlayerInventory, name: ITextC super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) RenderState.pushAttrib() // Prevents NEI render glitch. - RenderSystem.color3f(1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) + RenderState.makeItBlend() minecraft.getTextureManager.bind(Textures.GUI.Rack) if (inventoryContainer.isRelayEnabled) { From 700bacd0a170f2a3637811d0c07af294878122d7 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 8 Sep 2022 01:04:44 +0200 Subject: [PATCH 063/159] Fix wrong highlighting and item ghosts in GUIs --- .../li/cil/oc/client/gui/Assembler.scala | 8 +++++- .../scala/li/cil/oc/client/gui/Database.scala | 3 ++- .../li/cil/oc/client/gui/Disassembler.scala | 7 ++++- .../scala/li/cil/oc/client/gui/Drone.scala | 3 ++- .../oc/client/gui/DynamicGuiContainer.scala | 4 +-- .../li/cil/oc/common/container/Adapter.scala | 15 ++++++++++- .../cil/oc/common/container/Assembler.scala | 27 +++++++++++++++++++ .../li/cil/oc/common/container/Charger.scala | 12 +++++++-- .../oc/common/container/ComponentSlot.scala | 15 ++++++++++- .../oc/common/container/Disassembler.scala | 20 +++++++++++++- .../li/cil/oc/common/container/Printer.scala | 19 +++++++++++-- .../li/cil/oc/common/container/Relay.scala | 17 +++++++++++- 12 files changed, 136 insertions(+), 14 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 1ab3d63535..991b960f9e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -53,7 +53,13 @@ class Assembler(state: container.Assembler, playerInventory: PlayerInventory, na addButton(runButton) } - override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { + drawSecondaryForegroundLayer(stack, mouseX, mouseY) + + for (slot <- 0 until menu.slots.size()) { + drawSlotHighlight(stack, menu.getSlot(slot)) + } + } override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int): Unit = { RenderState.pushAttrib() diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index dc56882ce5..805adf2ab7 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -16,7 +16,8 @@ class Database(state: container.Database, playerInventory: PlayerInventory, name override def lockedStack = inventoryContainer.container - override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) = + drawSecondaryForegroundLayer(stack, mouseX, mouseY) override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) {} diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 005fa9f7b8..3b8fea9840 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -14,7 +14,12 @@ class Disassembler(state: container.Disassembler, playerInventory: PlayerInvento val progress = addCustomWidget(new ProgressBar(18, 65)) override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { - font.draw(stack, title, titleLabelX, titleLabelY, 0x404040); + font.draw(stack, title, titleLabelX, titleLabelY, 0x404040) + drawSecondaryForegroundLayer(stack, mouseX, mouseY) + + for (slot <- 0 until menu.slots.size()) { + drawSlotHighlight(stack, menu.getSlot(slot)) + } } override def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 8ccfc08a54..94c426c30f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -88,7 +88,8 @@ class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITex override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0 - override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) = + drawSecondaryForegroundLayer(stack, mouseX, mouseY) override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index 4a72240176..b78d0ee57f 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -114,7 +114,7 @@ abstract class DynamicGuiContainer[C <: Container](container: C, inv: PlayerInve RenderSystem.disableBlend() } - protected def drawSlotHighlight(stack: MatrixStack, slot: Slot) { + protected def drawSlotHighlight(matrix: MatrixStack, slot: Slot) { if (minecraft.player.inventory.getCarried.isEmpty) slot match { case component: ComponentSlot if component.slot == common.Slot.None || component.tier == common.Tier.None => // Ignore. case _ => @@ -132,7 +132,7 @@ abstract class DynamicGuiContainer[C <: Container](container: C, inv: PlayerInve } if (drawHighlight) { setBlitOffset(getBlitOffset + 100) - fillGradient(stack, + fillGradient(matrix, slot.x, slot.y, slot.x + 16, slot.y + 16, 0x80FFFFFF, 0x80FFFFFF) diff --git a/src/main/scala/li/cil/oc/common/container/Adapter.scala b/src/main/scala/li/cil/oc/common/container/Adapter.scala index f1922d2f6c..99bd34b33f 100644 --- a/src/main/scala/li/cil/oc/common/container/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/container/Adapter.scala @@ -1,13 +1,26 @@ package li.cil.oc.common.container +import li.cil.oc.api.Driver import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType class Adapter(selfType: ContainerType[_ <: Adapter], id: Int, playerInventory: PlayerInventory, adapter: IInventory) extends Player(selfType, id, playerInventory, adapter) { - addSlotToContainer(80, 35, Slot.Upgrade) + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, Slot.Upgrade, Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + Option(Driver.driverFor(stack, classOf[tileentity.Adapter])) match { + case Some(driver) => driver.slot(stack) == Slot.Upgrade + case _ => false + } + } + }) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index e17b5906c0..868eb2d99c 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -5,6 +5,7 @@ import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.common.tileentity +import net.minecraft.item.ItemStack import net.minecraft.inventory.container.ContainerType import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory @@ -22,6 +23,12 @@ class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventor @OnlyIn(Dist.CLIENT) override def isActive = !isAssembling && super.isActive + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + AssemblerTemplates.select(stack).isDefined + } + override def getBackgroundLocation = if (isAssembling) Textures.Icons.get(common.Tier.None) else super.getBackgroundLocation }) } @@ -40,6 +47,26 @@ class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventor } } + override def addSlotToContainer(x: Int, y: Int, info: DynamicComponentSlot => InventorySlot) { + val index = slots.size + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => common.Tier.One) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!super.mayPlace(stack)) return false + AssemblerTemplates.select(getSlot(0).getItem) match { + case Some(template) => + val index = getSlotIndex + val tplSlot = + if ((1 until 4) contains index) template.containerSlots(index - 1) + else if ((4 until 13) contains index) template.upgradeSlots(index - 4) + else if ((13 until 21) contains index) template.componentSlots(index - 13) + else AssemblerTemplates.NoSlot + tplSlot.validate(assembler, getSlotIndex, stack) + case _ => false + } + } + }) + } + // Component containers. for (i <- 0 until 3) { addSlotToContainer(34 + i * slotSize, 70, slotInfo _) diff --git a/src/main/scala/li/cil/oc/common/container/Charger.scala b/src/main/scala/li/cil/oc/common/container/Charger.scala index 1decd65607..0a1320b868 100644 --- a/src/main/scala/li/cil/oc/common/container/Charger.scala +++ b/src/main/scala/li/cil/oc/common/container/Charger.scala @@ -1,13 +1,21 @@ package li.cil.oc.common.container -import li.cil.oc.common.tileentity +import li.cil.oc.common.Tier +import li.cil.oc.integration.util.ItemCharge import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType class Charger(selfType: ContainerType[_ <: Charger], id: Int, playerInventory: PlayerInventory, charger: IInventory) extends Player(selfType, id, playerInventory, charger) { - addSlotToContainer(80, 35, "tablet") + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, "tablet", Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + ItemCharge.canCharge(stack) + } + }) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index 348c68c8be..6c6922eb12 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -1,5 +1,7 @@ package li.cil.oc.common.container +import li.cil.oc.api.Driver +import li.cil.oc.api.internal import li.cil.oc.common import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory @@ -33,7 +35,18 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) @OnlyIn(Dist.CLIENT) override def isActive = slot != common.Slot.None && tier != common.Tier.None && super.isActive - override def mayPlace(stack: ItemStack) = inventory.canPlaceItem(getSlotIndex, stack) + override def mayPlace(stack: ItemStack): Boolean = { + if (!inventory.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + for (driver <- Driver.itemDrivers) { + if (driver.worksWith(stack)) { + val slotOk = (slot == common.Slot.Any || driver.slot(stack) == slot) + val tierOk = (tier == common.Tier.Any || driver.tier(stack) <= tier) + if (slotOk && tierOk) return true + } + } + false + } override def onTake(player: PlayerEntity, stack: ItemStack) = { for (slot <- agentContainer.slots) slot match { diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index b1ea2831e8..6eadd09ef4 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -1,7 +1,14 @@ package li.cil.oc.common.container +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.common.template.DisassemblerTemplates import li.cil.oc.common.tileentity +import li.cil.oc.util.ItemUtils import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT @@ -9,7 +16,18 @@ import net.minecraft.nbt.CompoundNBT class Disassembler(selfType: ContainerType[_ <: Disassembler], id: Int, playerInventory: PlayerInventory, val disassembler: IInventory) extends Player(selfType, id, playerInventory, disassembler) { - addSlotToContainer(80, 35, "ocitem") + private def allowDisassembling(stack: ItemStack) = !stack.isEmpty && (!stack.hasTag || !stack.getTag.getBoolean(Settings.namespace + "undisassemblable")) + + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, "ocitem", Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + allowDisassembling(stack) && + (((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && + ItemUtils.getIngredients(playerInventory.player.level.getRecipeManager, stack).nonEmpty) || + DisassemblerTemplates.select(stack).isDefined) + } + }) addPlayerInventorySlots(8, 84) def disassemblyProgress = synchronizedData.getDouble("disassemblyProgress") diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index 0cc3df66bd..b6d78596b4 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -1,8 +1,11 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT @@ -10,8 +13,20 @@ import net.minecraft.nbt.CompoundNBT class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: PlayerInventory, val printer: IInventory) extends Player(selfType, id, playerInventory, printer) { - addSlotToContainer(18, 19, Slot.Filtered) - addSlotToContainer(18, 51, Slot.Filtered) + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 19, Slot.Filtered, Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + PrintData.materialValue(stack) > 0 + } + }) + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 51, Slot.Filtered, Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (!container.canPlaceItem(getSlotIndex, stack)) return false + if (!isActive) return false + PrintData.inkValue(stack) > 0 + } + }) addSlotToContainer(152, 35) // Show the player's inventory. diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala index 30e519ad9e..40c0118ee5 100644 --- a/src/main/scala/li/cil/oc/common/container/Relay.scala +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -1,8 +1,13 @@ package li.cil.oc.common.container +import li.cil.oc.Constants +import li.cil.oc.api +import li.cil.oc.api.detail.ItemInfo import li.cil.oc.common.Slot +import li.cil.oc.common.Tier import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType import net.minecraft.nbt.CompoundNBT @@ -10,10 +15,20 @@ import net.minecraft.nbt.CompoundNBT class Relay(selfType: ContainerType[_ <: Relay], id: Int, playerInventory: PlayerInventory, relay: IInventory) extends Player(selfType, id, playerInventory, relay) { + lazy final val WirelessNetworkCardTier1: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier1) + lazy final val WirelessNetworkCardTier2: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier2) + lazy final val LinkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) + addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) addSlotToContainer(151, 53, Slot.HDD) - addSlotToContainer(178, 15, Slot.Card) + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 178, 15, Slot.Card, Tier.Any) { + override def mayPlace(stack: ItemStack): Boolean = { + if (api.Items.get(stack) != WirelessNetworkCardTier1 && api.Items.get(stack) != WirelessNetworkCardTier2 && + api.Items.get(stack) != LinkedCard) return false + super.mayPlace(stack) + } + }) addPlayerInventorySlots(8, 84) def relayDelay = synchronizedData.getInt("relayDelay") From 651b5ae9b49e6611cb834e59cbc22d081450dec2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 8 Sep 2022 22:52:32 +0200 Subject: [PATCH 064/159] Fix tooltips not appearing in GUIs --- .../scala/li/cil/oc/client/KeyBindings.scala | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/KeyBindings.scala b/src/main/scala/li/cil/oc/client/KeyBindings.scala index 5607693da6..f357ff7cb1 100644 --- a/src/main/scala/li/cil/oc/client/KeyBindings.scala +++ b/src/main/scala/li/cil/oc/client/KeyBindings.scala @@ -1,9 +1,11 @@ package li.cil.oc.client import li.cil.oc.OpenComputers +import li.cil.oc.client.gui.traits.InputBuffer import net.minecraft.client.Minecraft import net.minecraft.client.settings.KeyBinding import net.minecraft.client.util.InputMappings +import net.minecraftforge.client.settings.IKeyConflictContext import net.minecraftforge.client.settings.KeyConflictContext import net.minecraftforge.client.settings.KeyModifier import org.lwjgl.glfw.GLFW @@ -11,30 +13,44 @@ import org.lwjgl.glfw.GLFW import scala.collection.mutable object KeyBindings { - def showExtendedTooltips: Boolean = extendedTooltip.isDown + private def isActive(input: InputMappings.Input): Boolean = { + val window = Minecraft.getInstance.getWindow.getWindow + input.getType match { + case InputMappings.Type.MOUSE => GLFW.glfwGetMouseButton(window, input.getValue) == GLFW.GLFW_PRESS + case InputMappings.Type.SCANCODE => false // GLFW doesn't have a glfwGetScancode method to test these. + case InputMappings.Type.KEYSYM => GLFW.glfwGetKey(window, input.getValue) == GLFW.GLFW_PRESS + } + } + + def showExtendedTooltips: Boolean = { + if (extendedTooltip.isDown) return true + // We have to know if the keybind is pressed even if the active screen doesn't pass events. + if (!extendedTooltip.getKeyConflictContext.isActive) return false + if (extendedTooltip.getKey == InputMappings.UNKNOWN) return false + extendedTooltip.getKeyModifier match { + // KeyModifier.NONE does not accept pure modifier keys by default, so check for that. + case KeyModifier.NONE if KeyModifier.isKeyCodeModifier(extendedTooltip.getKey) => isActive(extendedTooltip.getKey) + case mod if mod.isActive(extendedTooltip.getKeyConflictContext) => isActive(extendedTooltip.getKey) + case _ => false + } + } def isAnalyzeCopyingAddress: Boolean = analyzeCopyAddr.isDown def getKeyBindingName(keyBinding: KeyBinding) = keyBinding.getTranslatedKeyMessage.getString - val extendedTooltip = new KeyBinding("key.opencomputers.extendedTooltip", KeyConflictContext.GUI, - InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_SHIFT, OpenComputers.Name) { + val textInputConflict = new IKeyConflictContext { + override def isActive: Boolean = Minecraft.getInstance.screen.isInstanceOf[InputBuffer] - override def isActiveAndMatches(input: InputMappings.Input): Boolean = { - input != InputMappings.UNKNOWN && input.equals(getKey) && isConflictContextAndModifierActive - } - - override def isConflictContextAndModifierActive: Boolean = { - // KeyModifier.NONE does not accept pure modifier keys by default (except for IN_GAME conflict contexts). - val modifierActive = getKeyModifier.isActive(getKeyConflictContext) || - (getKeyModifier == KeyModifier.NONE && KeyModifier.isKeyCodeModifier(getKey)) - getKeyConflictContext.isActive && modifierActive - } + override def conflicts(other: IKeyConflictContext): Boolean = this == other } + val extendedTooltip = new KeyBinding("key.opencomputers.extendedTooltip", KeyConflictContext.GUI, + InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_SHIFT, OpenComputers.Name) + val analyzeCopyAddr = new KeyBinding("key.opencomputers.analyzeCopyAddress", KeyConflictContext.IN_GAME, InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_CONTROL, OpenComputers.Name) - val clipboardPaste = new KeyBinding("key.opencomputers.clipboardPaste", KeyConflictContext.GUI, + val clipboardPaste = new KeyBinding("key.opencomputers.clipboardPaste", textInputConflict, InputMappings.Type.KEYSYM, GLFW.GLFW_KEY_INSERT, OpenComputers.Name) } From b2d948f11b5b5c379380fb135fd489970bd55cf4 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 8 Sep 2022 23:35:27 +0200 Subject: [PATCH 065/159] Add translations for creative tab and new keys --- src/main/resources/assets/opencomputers/lang/de_de.json | 5 ++++- src/main/resources/assets/opencomputers/lang/en_us.json | 3 +++ src/main/resources/assets/opencomputers/lang/fr_fr.json | 6 ++++-- src/main/resources/assets/opencomputers/lang/it_it.json | 1 + src/main/resources/assets/opencomputers/lang/pt_br.json | 3 ++- src/main/resources/assets/opencomputers/lang/pt_pt.json | 1 + src/main/resources/assets/opencomputers/lang/ru_ru.json | 3 ++- src/main/resources/assets/opencomputers/lang/tr_tr.json | 3 ++- src/main/resources/assets/opencomputers/lang/zh_cn.json | 3 ++- src/main/resources/assets/opencomputers/lang/zh_tw.json | 1 + src/main/scala/li/cil/oc/common/block/Redstone.scala | 2 +- .../scala/li/cil/oc/common/block/traits/PowerAcceptor.scala | 2 +- 12 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/resources/assets/opencomputers/lang/de_de.json b/src/main/resources/assets/opencomputers/lang/de_de.json index 346c0c8700..d5b7cc62d3 100644 --- a/src/main/resources/assets/opencomputers/lang/de_de.json +++ b/src/main/resources/assets/opencomputers/lang/de_de.json @@ -151,7 +151,8 @@ "item.oc.WirelessNetworkCard1": "Drahtlosnetzwerkkarte (Stufe 2)", "item.oc.WorldSensorCard": "Weltsensorkarte", "item.oc.wrench": "Schraubenziehschlüssel", - "entity.oc.Drone.name": "Drohne", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "Drohne", "oc:gui.Analyzer.Address": "§6Adresse§f: %s", "oc:gui.Analyzer.AddressCopied": "Adresse wurde in die Zwischenablage kopiert.", "oc:gui.Analyzer.ChargerSpeed": "§6Ladegeschwindigkeit§f: %s", @@ -234,7 +235,9 @@ "oc:container.server": "Server", "oc:container.rack": "Serverschrank", "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.analyzeCopyAddress": "Adresse kopieren (Messgerät)", "key.opencomputers.clipboardPaste": "Zwischenablage einfügen", + "key.opencomputers.extendedTooltip": "Mehr Infos anzeigen", "oc:tooltip.accesspoint": "Verhält sich wie ein Switch, aber empfängt zusätzlich Drahtlosnachrichten und leitet Pakete aus dem Festnetz drahtlos weiter.", "oc:tooltip.abstractbuscard": "Erlaubt es, LIP-Pakete des Abstrakten Busses von §fStargateTech 2§7 zu senden und zu empfangen.", "oc:tooltip.acid": "Eine hochgiftige Möchtegernflüssigkeit, wird üblicherweise nur von gewissen Piraten konsumiert. Mag sich aber auch zu anderen Zwecken eignen.", diff --git a/src/main/resources/assets/opencomputers/lang/en_us.json b/src/main/resources/assets/opencomputers/lang/en_us.json index c995688edc..5d4d881651 100644 --- a/src/main/resources/assets/opencomputers/lang/en_us.json +++ b/src/main/resources/assets/opencomputers/lang/en_us.json @@ -152,6 +152,7 @@ "item.oc.wirelessnetworkcard1": "Wireless Network Card (Tier 2)", "item.oc.worldsensorcard": "World Sensor Card", "item.oc.wrench": "Scrench", + "itemGroup.OpenComputers": "OpenComputers", "entity.oc.Drone": "Drone", "oc:gui.Analyzer.Address": "§6Address§f: %s", "oc:gui.Analyzer.AddressCopied": "Address copied to clipboard.", @@ -236,7 +237,9 @@ "oc:container.server": "Server", "oc:container.rack": "Rack", "oc:container.tabletwrapper": "Tablet", + "key.opencomputers.analyzeCopyAddress": "Copy Address (Analyzer)", "key.opencomputers.clipboardPaste": "Paste Clipboard", + "key.opencomputers.extendedTooltip": "Show Detailed Tooltips", "oc:tooltip.abstractbuscard": "Allows interacting with §fStargateTech 2§7's abstract bus by sending and receiving LIP packets.", "oc:tooltip.acid": "A highly toxic pseudo-liquid, usually only consumed by certain pirates. May prove to be useful in other ways, too, however.", "oc:tooltip.adapter": "Used to control non-component blocks, such as vanilla blocks or blocks from other mods.", diff --git a/src/main/resources/assets/opencomputers/lang/fr_fr.json b/src/main/resources/assets/opencomputers/lang/fr_fr.json index 27a6159bcd..a9c2966662 100644 --- a/src/main/resources/assets/opencomputers/lang/fr_fr.json +++ b/src/main/resources/assets/opencomputers/lang/fr_fr.json @@ -142,7 +142,8 @@ "item.oc.wirelessnetworkcard1": "Carte de réseau sans-fils (Niveau 2)", "item.oc.worldsensorcard": "Carte de capteur du monde", "item.oc.wrench": "Crisseur", - "entity.oc.Drone.name": "Drone", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "Drone", "oc:gui.Analyzer.Address": "§6Addresse§f: %s", "oc:gui.Analyzer.AddressCopied": "Adresse copiée dans le presse-papier.", "oc:gui.Analyzer.ChargerSpeed": "§6Vitesse de charge§f: %s", @@ -218,8 +219,9 @@ "oc:container.rack": "Support de serveur", "oc:container.switch": "Routeur", "oc:container.tabletwrapper": "Tablette", - "key.opencomputers.materialCosts": "Montrer les coût en matériaux", + "key.opencomputers.analyzeCopyAddress": "Copier l'adresse (Analyseur)", "key.opencomputers.clipboardPaste": "Coller le presse-papier", + "key.opencomputers.extendedTooltip": "Afficher plus d'informations", "oc:tooltip.accesspoint": "Fonctionne comme un routeur, de plus il recois les paquets sans-fil et il transmet les paquets filaires au réseau sans-fil.", "oc:tooltip.abstractbuscard": "Il permet d'interagir avec le bus abstait de §fStargateTech 2§7 lors de l'envoi et de la reception de paquets LIP.", "oc:tooltip.acid": "Un produit semi-liquide très toxique, uniquement bu par certains pirates. Grâce à ses propriétés corrosives, il est très utile à la gravure de circuits imprimés.", diff --git a/src/main/resources/assets/opencomputers/lang/it_it.json b/src/main/resources/assets/opencomputers/lang/it_it.json index fabc78d1d1..56be5eaaaa 100644 --- a/src/main/resources/assets/opencomputers/lang/it_it.json +++ b/src/main/resources/assets/opencomputers/lang/it_it.json @@ -105,6 +105,7 @@ "item.oc.upgradetractorbeam": "Upgrade Raggio Traente", "item.oc.wirelessnetworkcard0": "Scheda di Rete Wireless (Livello 1)", "item.oc.wirelessnetworkcard1": "Scheda di Rete Wireless (Livello 2)", + "itemGroup.OpenComputers": "OpenComputers", "oc:gui.Analyzer.Address": "§6Indirizzo§f: %s", "oc:gui.Analyzer.AddressCopied": "Indirizzo copiato negli appunti.", "oc:gui.Analyzer.ChargerSpeed": "§6Velocità ricarica§f: %s", diff --git a/src/main/resources/assets/opencomputers/lang/pt_br.json b/src/main/resources/assets/opencomputers/lang/pt_br.json index dc724a281a..2fb67eae36 100644 --- a/src/main/resources/assets/opencomputers/lang/pt_br.json +++ b/src/main/resources/assets/opencomputers/lang/pt_br.json @@ -153,7 +153,8 @@ "item.oc.wirelessnetworkcard1": "Cartão de Rede Sem Fio (Nível 2)", "item.oc.worldsensorcard": "Cartão de Sensor de Mundo", "item.oc.wrench": "Chave de fenda-boca", - "entity.oc.Drone.name": "Drone", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "Drone", "oc:gui.Analyzer.Address": "§6Endereço§f: %s", "oc:gui.Analyzer.AddressCopied": "Endereço copiado para a área de transferência.", "oc:gui.Analyzer.ChargerSpeed": "§6Velocidade da carga§f: %s", diff --git a/src/main/resources/assets/opencomputers/lang/pt_pt.json b/src/main/resources/assets/opencomputers/lang/pt_pt.json index 15ad352395..d6545483d0 100644 --- a/src/main/resources/assets/opencomputers/lang/pt_pt.json +++ b/src/main/resources/assets/opencomputers/lang/pt_pt.json @@ -51,6 +51,7 @@ "item.oc.upgradesolargenerator": "Melhoramento: Gerador Solar", "item.oc.wirelessnetworkcard0": "Placa de Rede sem Fios", "item.oc.WirelessNetworkCard1": "Placa de Rede sem Fios", + "itemGroup.OpenComputers": "OpenComputers", "oc:gui.Analyzer.Address": "§6Endereço§f: %s", "oc:gui.Analyzer.ComponentName": "§6Nome do Componente§f: %s", "oc:gui.Analyzer.LastError": "§6Último erro§f: %s", diff --git a/src/main/resources/assets/opencomputers/lang/ru_ru.json b/src/main/resources/assets/opencomputers/lang/ru_ru.json index b5942f9240..1cee1964b2 100644 --- a/src/main/resources/assets/opencomputers/lang/ru_ru.json +++ b/src/main/resources/assets/opencomputers/lang/ru_ru.json @@ -151,7 +151,8 @@ "item.oc.wirelessnetworkcard1": "Плата беспроводной сети (2-ой уровень)", "item.oc.worldsensorcard": "Карта-мировой сенсор", "item.oc.wrench": "Ключ", - "entity.oc.Drone.name": "Дрон", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "Дрон", "oc:gui.Analyzer.Address": "§6Адрес§f: %s", "oc:gui.Analyzer.AddressCopied": "Адрес скопирован в буфер обмена.", "oc:gui.Analyzer.ChargerSpeed": "§6Скорость зарядки§f: %s", diff --git a/src/main/resources/assets/opencomputers/lang/tr_tr.json b/src/main/resources/assets/opencomputers/lang/tr_tr.json index 17fc3d3327..095f2270a7 100644 --- a/src/main/resources/assets/opencomputers/lang/tr_tr.json +++ b/src/main/resources/assets/opencomputers/lang/tr_tr.json @@ -151,7 +151,8 @@ "item.oc.wirelessnetworkcard1": "Kablosuz Ağ Kartı (2. Seviye)", "item.oc.worldsensorcard": "Dünya Algılayıcı Kartı", "item.oc.wrench": "Tornanahtar", - "entity.oc.Drone.name": "Drone", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "Drone", "oc:gui.Analyzer.Address": "§6Adres§f: %s", "oc:gui.Analyzer.AddressCopied": "Adres panoya kopyalandı", "oc:gui.Analyzer.ChargerSpeed": "§6Şarj hızı§f: %s", diff --git a/src/main/resources/assets/opencomputers/lang/zh_cn.json b/src/main/resources/assets/opencomputers/lang/zh_cn.json index e98748647e..7d07804ac0 100644 --- a/src/main/resources/assets/opencomputers/lang/zh_cn.json +++ b/src/main/resources/assets/opencomputers/lang/zh_cn.json @@ -151,7 +151,8 @@ "item.oc.wirelessnetworkcard1": "高级无线网卡", "item.oc.worldsensorcard": "世界传感器卡", "item.oc.wrench": "螺丝刀扳手", - "entity.oc.Drone.name": "无人机", + "itemGroup.OpenComputers": "OpenComputers", + "entity.oc.Drone": "无人机", "oc:gui.Analyzer.Address": "§6地址§f:%s", "oc:gui.Analyzer.AddressCopied": "地址已复制到剪贴板。", "oc:gui.Analyzer.ChargerSpeed": "§6充电速度§f:%s", diff --git a/src/main/resources/assets/opencomputers/lang/zh_tw.json b/src/main/resources/assets/opencomputers/lang/zh_tw.json index bdc095b6db..16a3469ad9 100644 --- a/src/main/resources/assets/opencomputers/lang/zh_tw.json +++ b/src/main/resources/assets/opencomputers/lang/zh_tw.json @@ -146,6 +146,7 @@ "item.oc.wirelessnetworkcard1": "無線網卡", "item.oc.worldsensorcard": "世界傳感器卡", "item.oc.wrench": "螺絲刀扳手", + "itemGroup.OpenComputers": "OpenComputers", "entity.oc.drone": "無人機", "oc:gui.Analyzer.Address": "§6位址§f: %s", "oc:gui.Analyzer.AddressCopied": "位址複製到剪貼簿.", diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index 21c3420c6c..d6ccbc5877 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -20,7 +20,7 @@ class Redstone extends RedstoneAware { super.tooltipTail(stack, world, tooltip, advanced) // todo more generic way for redstone mods to provide lines if (Mods.ProjectRedTransmission.isModAvailable) { - for (curr <- Tooltip.get("RedstoneCard.ProjectRed")) tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) + for (curr <- Tooltip.get("redstonecard.ProjectRed")) tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } diff --git a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala index f2bb216dc5..94180143d1 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/PowerAcceptor.scala @@ -20,7 +20,7 @@ trait PowerAcceptor extends SimpleBlock { override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { super.tooltipTail(stack, world, tooltip, advanced) - for (curr <- Tooltip.extended("PowerAcceptor", energyThroughput.toInt)) { + for (curr <- Tooltip.extended("poweracceptor", energyThroughput.toInt)) { tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) } } From 7ce9adc4cf66ac7b041c8dbfd45a8ba1a915427b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 9 Sep 2022 00:20:56 +0200 Subject: [PATCH 066/159] Fix progress bar hover detection --- src/main/scala/li/cil/oc/client/gui/Assembler.scala | 2 +- src/main/scala/li/cil/oc/client/gui/Drone.scala | 2 +- src/main/scala/li/cil/oc/client/gui/Printer.scala | 4 ++-- src/main/scala/li/cil/oc/client/gui/Robot.scala | 2 +- src/main/scala/li/cil/oc/common/container/Printer.scala | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index 991b960f9e..378a74b038 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -85,7 +85,7 @@ class Assembler(state: container.Assembler, playerInventory: PlayerInventory, na copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } } - else if (isPointInRegion(progress.x, progress.y, progress.width, progress.height, mouseX, mouseY)) { + else if (isPointInRegion(progress.x, progress.y, progress.width, progress.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] val timeRemaining = formatTime(inventoryContainer.assemblyRemainingTime) tooltip.add(Localization.Assembler.Progress(inventoryContainer.assemblyProgress, timeRemaining)) diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 94c426c30f..f3ec46817b 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -94,7 +94,7 @@ class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITex override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) RenderState.pushAttrib() - if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { + if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] val format = Localization.Computer.Power + ": %d%% (%d/%d)" tooltip.add(format.format( diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index 38d12eee33..ce20911a96 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -41,12 +41,12 @@ class Printer(state: container.Printer, playerInventory: PlayerInventory, name: override def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) = { super.drawSecondaryForegroundLayer(stack, mouseX, mouseY) RenderState.pushAttrib() - if (isPointInRegion(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX, mouseY)) { + if (isPointInRegion(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] tooltip.add(inventoryContainer.amountMaterial + "/" + inventoryContainer.maxAmountMaterial) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } - if (isPointInRegion(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX, mouseY)) { + if (isPointInRegion(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] tooltip.add(inventoryContainer.amountInk + "/" + inventoryContainer.maxAmountInk) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 6c522c859a..8720bf09e3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -137,7 +137,7 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) RenderState.pushAttrib() - if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX, mouseY)) { + if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] val format = Localization.Computer.Power + ": %d%% (%d/%d)" tooltip.add(format.format( diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index b6d78596b4..e65c79b80c 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -48,7 +48,7 @@ class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: P synchronizedData.putDouble("progress", if (te.isPrinting) te.progress / 100.0 else 0) synchronizedData.putInt("maxAmountMaterial", te.maxAmountMaterial) synchronizedData.putInt("amountMaterial", te.amountMaterial) - synchronizedData.putInt("maxAmountInk", te.amountInk) + synchronizedData.putInt("maxAmountInk", te.maxAmountInk) synchronizedData.putInt("amountInk", te.amountInk) } case _ => From 1bb14216271a998bfbf73f6ddfd3682f4c9ec18b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 9 Sep 2022 22:21:52 +0200 Subject: [PATCH 067/159] Fix render glitches caused by tile renderers --- .../api/event/RackMountableRenderEvent.java | 69 --------------- .../cil/oc/client/renderer/RenderTypes.java | 14 ++++ .../renderer/tileentity/AdapterRenderer.scala | 24 +----- .../tileentity/AssemblerRenderer.scala | 26 +----- .../renderer/tileentity/CaseRenderer.scala | 33 ++------ .../renderer/tileentity/ChargerRenderer.scala | 25 +----- .../tileentity/DisassemblerRenderer.scala | 23 +---- .../tileentity/DiskDriveRenderer.scala | 23 +---- .../tileentity/GeolyzerRenderer.scala | 21 +---- .../tileentity/HologramRendererFallback.scala | 15 ++-- .../tileentity/MicrocontrollerRenderer.scala | 25 +----- .../tileentity/NetSplitterRenderer.scala | 22 +---- .../tileentity/PowerDistributorRenderer.scala | 24 +----- .../renderer/tileentity/RackRenderer.scala | 9 +- .../renderer/tileentity/RaidRenderer.scala | 23 +---- .../renderer/tileentity/RelayRenderer.scala | 24 +----- .../renderer/tileentity/ScreenRenderer.scala | 46 ++-------- .../tileentity/TransposerRenderer.scala | 84 ++++++++----------- .../event/RackMountableRenderHandler.scala | 44 +++++----- 19 files changed, 130 insertions(+), 444 deletions(-) diff --git a/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java b/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java index a880c0ed26..4f689859c9 100644 --- a/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java +++ b/src/main/java/li/cil/oc/api/event/RackMountableRenderEvent.java @@ -4,20 +4,13 @@ import li.cil.oc.api.component.RackMountable; import li.cil.oc.api.internal.Rack; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.IRenderTypeBuffer; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.Direction; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.vector.Matrix4f; import net.minecraftforge.eventbus.api.Cancelable; import net.minecraftforge.eventbus.api.Event; -import org.lwjgl.opengl.GL11; /** * Fired to allow rendering a custom overlay for {@link li.cil.oc.api.component.RackMountable}s. @@ -142,67 +135,5 @@ public TileEntity(final Rack rack, final int mountable, final CompoundNBT data, this.v0 = v0; this.v1 = v1; } - - /** - * Utility method for rendering a texture as the front-side overlay. - * - * @param texture the texture to use to render the overlay. - */ - public void renderOverlay(final ResourceLocation texture) { - renderOverlay(texture, 0, 1); - } - - /** - * Utility method for rendering a texture as the front-side overlay - * over a specified horizontal area. - * - * @param texture the texture to use to render the overlay. - * @param u0 the lower end of the vertical area to render at. - * @param u1 the upper end of the vertical area to render at. - */ - public void renderOverlay(final ResourceLocation texture, final float u0, final float u1) { - Minecraft.getInstance().getTextureManager().bind(texture); - final Tessellator t = Tessellator.getInstance(); - final BufferBuilder r = t.getBuilder(); - final Matrix4f matrix = stack.last().pose(); - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.vertex(matrix, u0, v1, 0).uv(u0, v1).endVertex(); - r.vertex(matrix, u1, v1, 0).uv(u1, v1).endVertex(); - r.vertex(matrix, u1, v0, 0).uv(u1, v0).endVertex(); - r.vertex(matrix, u0, v0, 0).uv(u0, v0).endVertex(); - t.end(); - } - - /** - * Utility method for rendering an atlas texture as the front-side overlay. - * - * @param texture the atlas texture to use to render the overlay. - */ - public void renderOverlayFromAtlas(final ResourceLocation texture) { - renderOverlayFromAtlas(texture, 0, 1); - } - - /** - * Utility method for rendering an atlas texture as the front-side overlay - * over a specified horizontal area. - * - * @param texture the atlas texture to use to render the overlay. - * @param u0 the lower end of the vertical area to render at. - * @param u1 the upper end of the vertical area to render at. - */ - public void renderOverlayFromAtlas(final ResourceLocation texture, final float u0, final float u1) { - final AtlasTexture atlas = Minecraft.getInstance().getModelManager().getAtlas(PlayerContainer.BLOCK_ATLAS); - atlas.bind(); - final TextureAtlasSprite icon = atlas.getSprite(texture); - final Tessellator t = Tessellator.getInstance(); - final BufferBuilder r = t.getBuilder(); - final Matrix4f matrix = stack.last().pose(); - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - r.vertex(matrix, u0, v1, 0).uv(icon.getU(u0 * 16), icon.getV(v1 * 16)).endVertex(); - r.vertex(matrix, u1, v1, 0).uv(icon.getU(u1 * 16), icon.getV(v1 * 16)).endVertex(); - r.vertex(matrix, u1, v0, 0).uv(icon.getU(u1 * 16), icon.getV(v0 * 16)).endVertex(); - r.vertex(matrix, u0, v0, 0).uv(icon.getU(u0 * 16), icon.getV(v0 * 16)).endVertex(); - t.end(); - } } } diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 107f9834d0..7752611181 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -68,6 +68,20 @@ private static final RenderType createUpgrade(String name, ResourceLocation text .setWriteMaskState(COLOR_WRITE) .createCompositeState(false)); + public static final RenderType BLOCK_OVERLAY = create(OpenComputers.ID() + ":overlay_block", + DefaultVertexFormats.POSITION_TEX, GL11.GL_QUADS, 1024, State.builder() + .setTextureState(BLOCK_SHEET_MIPPED) + .setTransparencyState(LIGHTNING_TRANSPARENCY) + .setAlphaState(DEFAULT_ALPHA) + .createCompositeState(false)); + + public static final RenderType BLOCK_OVERLAY_COLOR = create(OpenComputers.ID() + ":overlay_block", + DefaultVertexFormats.POSITION_COLOR_TEX, GL11.GL_QUADS, 1024, State.builder() + .setTextureState(BLOCK_SHEET_MIPPED) + .setTransparencyState(LIGHTNING_TRANSPARENCY) + .setAlphaState(DEFAULT_ALPHA) + .createCompositeState(false)); + private RenderTypes() { super(null, null, 0, 0, false, false, null, null); throw new Error(); diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala index 03e7bf1c5d..b6a06d7501 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/AdapterRenderer.scala @@ -5,17 +5,15 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction -import org.lwjgl.opengl.GL11 object AdapterRenderer extends Function[TileEntityRendererDispatcher, AdapterRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new AdapterRenderer(dispatch) @@ -25,24 +23,16 @@ class AdapterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity override def render(adapter: tileentity.Adapter, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - if (adapter.openSides.contains(true)) { - RenderState.pushAttrib() - RenderState.disableEntityLighting() - RenderState.makeItBlend() + RenderSystem.color4f(1, 1, 1, 1) + if (adapter.openSides.contains(true)) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - Minecraft.getInstance().getModelManager().getAtlas(AtlasTexture.LOCATION_BLOCKS).bind() - - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) val sideActivity = Textures.getSprite(Textures.Block.AdapterOn) @@ -88,13 +78,7 @@ class AdapterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala index fe1180bc70..129f5b025e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/AssemblerRenderer.scala @@ -5,15 +5,13 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Assembler import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 object AssemblerRenderer extends Function[TileEntityRendererDispatcher, AssemblerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new AssemblerRenderer(dispatch) @@ -23,21 +21,13 @@ class AssemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti override def render(assembler: Assembler, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) + RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() stack.translate(0.5, 0.5, 0.5) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) { val icon = Textures.getSprite(Textures.Block.AssemblerTopOn) @@ -47,13 +37,9 @@ class AssemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti r.vertex(stack.last.pose, -0.5f, 0.55f, -0.5f).uv(icon.getU0, icon.getV0).endVertex() } - t.end() - // TODO Unroll loop to draw all at once? val indent = 6 / 16f + 0.005f for (i <- 0 until 4) { - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - if (assembler.isAssembling) { val icon = Textures.getSprite(Textures.Block.AssemblerSideAssembling) r.vertex(stack.last.pose, indent, 0.5f, -indent).uv(icon.getU((0.5f - indent) * 16), icon.getV1).endVertex() @@ -70,16 +56,10 @@ class AssemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti r.vertex(stack.last.pose, 0.5005f, -0.5f, -0.5f).uv(icon.getU0, icon.getV0).endVertex() } - t.end() - stack.mulPose(Vector3f.YP.rotationDegrees(90)) } - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala index 6a5624ca06..99895e1d3d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/CaseRenderer.scala @@ -3,20 +3,17 @@ package li.cil.oc.client.renderer.tileentity import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Case import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.RenderHelper -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 object CaseRenderer extends Function[TileEntityRendererDispatcher, CaseRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new CaseRenderer(dispatch) @@ -26,12 +23,6 @@ class CaseRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen override def render(computer: Case, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) - stack.pushPose() stack.translate(0.5, 0.5, 0.5) @@ -47,37 +38,25 @@ class CaseRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen stack.scale(1, -1, 1) if (computer.isRunning) { - renderFrontOverlay(stack, Textures.Block.CaseFrontOn) + renderFrontOverlay(stack, Textures.Block.CaseFrontOn, buffer.getBuffer(RenderTypes.BLOCK_OVERLAY)) if (System.currentTimeMillis() - computer.lastFileSystemAccess < 400 && computer.world.random.nextDouble() > 0.1) { - renderFrontOverlay(stack, Textures.Block.CaseFrontActivity) + renderFrontOverlay(stack, Textures.Block.CaseFrontActivity, buffer.getBuffer(RenderTypes.BLOCK_OVERLAY)) } } else if (computer.hasErrored && RenderUtil.shouldShowErrorLight(computer.hashCode)) { - renderFrontOverlay(stack, Textures.Block.CaseFrontError) + renderFrontOverlay(stack, Textures.Block.CaseFrontError, buffer.getBuffer(RenderTypes.BLOCK_OVERLAY)) } - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } - private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation): Unit = { - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - + private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation, r: IVertexBuilder): Unit = { val icon = Textures.getSprite(texture) r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - - t.end() } } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala index 8fa3299018..429b7a4de8 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/ChargerRenderer.scala @@ -5,16 +5,14 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Charger import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 object ChargerRenderer extends Function[TileEntityRendererDispatcher, ChargerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new ChargerRenderer(dispatch) @@ -24,14 +22,9 @@ class ChargerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity override def render(charger: Charger, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - if (charger.chargeSpeed > 0) { - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) - RenderSystem.color4f(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) + if (charger.chargeSpeed > 0) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) @@ -46,11 +39,7 @@ class ChargerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity stack.translate(-0.5f, 0.5f, 0.5f) stack.scale(1, -1, 1) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) { val inverse = 1 - charger.chargeSpeed.toFloat @@ -80,13 +69,7 @@ class ChargerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity r.vertex(stack.last.pose, 1.005f, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala index 0134e4f5aa..b129606b6c 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/DisassemblerRenderer.scala @@ -5,14 +5,12 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.opengl.GL11 object DisassemblerRenderer extends Function[TileEntityRendererDispatcher, DisassemblerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new DisassemblerRenderer(dispatch) @@ -22,23 +20,16 @@ class DisassemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileE override def render(disassembler: tileentity.Disassembler, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - if (disassembler.isActive) { - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderSystem.color4f(1, 1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) + if (disassembler.isActive) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - val t = Tessellator.getInstance - val r = t.getBuilder - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) { val icon = Textures.getSprite(Textures.Block.DisassemblerTopOn) @@ -71,13 +62,7 @@ class DisassemblerRenderer(dispatch: TileEntityRendererDispatcher) extends TileE r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala index ccb3b65efc..6068fe056e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/DiskDriveRenderer.scala @@ -5,20 +5,16 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.DiskDrive import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.model.ItemCameraTransforms import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.item.ItemEntity import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 -import org.lwjgl.opengl.GL13 object DiskDriveRenderer extends Function[TileEntityRendererDispatcher, DiskDriveRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new DiskDriveRenderer(dispatch) @@ -28,7 +24,6 @@ class DiskDriveRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti override def render(drive: DiskDrive, dt: Float, matrix: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() RenderSystem.color4f(1, 1, 1, 1) matrix.pushPose() @@ -58,30 +53,16 @@ class DiskDriveRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnti matrix.translate(-0.5, 0.5, 0.505) matrix.scale(1, -1, 1) - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) - - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) val icon = Textures.getSprite(Textures.Block.DiskDriveFrontActivity) r.vertex(matrix.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() r.vertex(matrix.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() r.vertex(matrix.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() r.vertex(matrix.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() } matrix.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala index e6d0058d86..2d609c12ec 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/GeolyzerRenderer.scala @@ -5,14 +5,12 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Geolyzer import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.opengl.GL11 object GeolyzerRenderer extends Function[TileEntityRendererDispatcher, GeolyzerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new GeolyzerRenderer(dispatch) @@ -22,11 +20,6 @@ class GeolyzerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit override def render(geolyzer: Geolyzer, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() @@ -35,11 +28,7 @@ class GeolyzerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) val icon = Textures.getSprite(Textures.Block.GeolyzerTopOn) r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV1).endVertex() @@ -47,13 +36,7 @@ class GeolyzerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala index ce6049eb00..ae63453f3b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala @@ -6,6 +6,7 @@ import li.cil.oc.common.tileentity.Hologram import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.util.math.vector.Vector3f object HologramRendererFallback { var text = "Requires OpenGL 1.5" @@ -13,15 +14,19 @@ object HologramRendererFallback { def render(hologram: Hologram, f: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") + RenderSystem.color4f(1, 1, 1, 1) + val fontRenderer = Minecraft.getInstance.font stack.pushPose() - val pos = hologram.getBlockPos - stack.translate(pos.getX + 0.5, pos.getY + 0.75, pos.getZ + 0.5) - + stack.translate(0.5, 0.75, 0.5) stack.scale(1 / 128f, -1 / 128f, 1 / 128f) - RenderSystem.disableCull() - fontRenderer.draw(stack, text, -fontRenderer.width(text) / 2, 0, 0xFFFFFFFF) + + fontRenderer.drawInBatch(text, -fontRenderer.width(text) / 2, 0, 0xFFFFFFFF, + false, stack.last.pose, buffer, false, 0, light) + stack.mulPose(Vector3f.YP.rotationDegrees(180)) + fontRenderer.drawInBatch(text, -fontRenderer.width(text) / 2, 0, 0xFFFFFFFF, + false, stack.last.pose, buffer, false, 0, light) stack.popPose() diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala index 45b734c738..aec54a5b29 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/MicrocontrollerRenderer.scala @@ -4,19 +4,17 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Microcontroller import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.BufferBuilder import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 object MicrocontrollerRenderer extends Function[TileEntityRendererDispatcher, MicrocontrollerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new MicrocontrollerRenderer(dispatch) @@ -26,11 +24,6 @@ class MicrocontrollerRenderer(dispatch: TileEntityRendererDispatcher) extends Ti override def render(mcu: Microcontroller, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(1) RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() @@ -47,11 +40,7 @@ class MicrocontrollerRenderer(dispatch: TileEntityRendererDispatcher) extends Ti stack.translate(-0.5, 0.5, 0.505) stack.scale(1, -1, 1) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) renderFrontOverlay(stack, Textures.Block.MicrocontrollerFrontLight, r) @@ -62,18 +51,12 @@ class MicrocontrollerRenderer(dispatch: TileEntityRendererDispatcher) extends Ti renderFrontOverlay(stack, Textures.Block.MicrocontrollerFrontError, r) } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } - private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation, r: BufferBuilder): Unit = { + private def renderFrontOverlay(stack: MatrixStack, texture: ResourceLocation, r: IVertexBuilder): Unit = { val icon = Textures.getSprite(texture) r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala index 865759ae0b..17627fa3b3 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/NetSplitterRenderer.scala @@ -5,17 +5,15 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction -import org.lwjgl.opengl.GL11 object NetSplitterRenderer extends Function[TileEntityRendererDispatcher, NetSplitterRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new NetSplitterRenderer(dispatch) @@ -25,11 +23,9 @@ class NetSplitterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEn override def render(splitter: tileentity.NetSplitter, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - if (splitter.openSides.contains(!splitter.isInverted)) { - RenderState.pushAttrib() - RenderState.disableEntityLighting() - RenderState.makeItBlend() + RenderSystem.color4f(1, 1, 1, 1) + if (splitter.openSides.contains(!splitter.isInverted)) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) @@ -38,11 +34,7 @@ class NetSplitterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEn Minecraft.getInstance().getModelManager().getAtlas(AtlasTexture.LOCATION_BLOCKS).bind() - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) val sideActivity = Textures.getSprite(Textures.Block.NetSplitterOn) @@ -88,13 +80,7 @@ class NetSplitterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEn r.vertex(stack.last.pose, 1, 0, 1).uv(sideActivity.getU0, sideActivity.getV0).endVertex() } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala index 73baff5172..cf70b78788 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/PowerDistributorRenderer.scala @@ -5,14 +5,12 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.opengl.GL11 object PowerDistributorRenderer extends Function[TileEntityRendererDispatcher, PowerDistributorRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new PowerDistributorRenderer(dispatch) @@ -22,24 +20,16 @@ class PowerDistributorRenderer(dispatch: TileEntityRendererDispatcher) extends T override def render(distributor: tileentity.PowerDistributor, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - if (distributor.globalBuffer > 0) { - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha((distributor.globalBuffer / distributor.globalBufferSize).toFloat) + RenderSystem.color4f(1, 1, 1, 1) + if (distributor.globalBuffer > 0) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) { val icon = Textures.getSprite(Textures.Block.PowerDistributorTopOn) @@ -72,13 +62,7 @@ class PowerDistributorRenderer(dispatch: TileEntityRendererDispatcher) extends T r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala index 16da338c0a..ebdf3c26ca 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RackRenderer.scala @@ -26,7 +26,7 @@ class RackRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen override def render(rack: Rack, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() + RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() @@ -45,21 +45,14 @@ class RackRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen // Note: we manually sync the rack inventory for this to work. for (i <- 0 until rack.getContainerSize) { if (!rack.getItem(i).isEmpty) { - stack.pushPose() - RenderState.pushAttrib() - val v0 = vOffset + i * vSize val v1 = vOffset + (i + 1) * vSize val event = new RackMountableRenderEvent.TileEntity(rack, i, rack.lastData(i), stack, buffer, light, overlay, v0, v1) MinecraftForge.EVENT_BUS.post(event) - - RenderState.popAttrib() - stack.popPose() } } stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala index 3090082504..b4bb92dd4c 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RaidRenderer.scala @@ -4,19 +4,18 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Raid import li.cil.oc.util.RenderState import net.minecraft.client.renderer.BufferBuilder import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 object RaidRenderer extends Function[TileEntityRendererDispatcher, RaidRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new RaidRenderer(dispatch) @@ -26,10 +25,6 @@ class RaidRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen override def render(raid: Raid, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() @@ -46,11 +41,7 @@ class RaidRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen stack.translate(-0.5, 0.5, 0.505) stack.scale(1, -1, 1) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) { val icon = Textures.getSprite(Textures.Block.RaidFrontError) @@ -70,13 +61,7 @@ class RaidRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen } } - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } @@ -84,7 +69,7 @@ class RaidRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRen private val u1 = 2 / 16f private val fs = 4 / 16f - private def renderSlot(stack: MatrixStack, r: BufferBuilder, slot: Int, icon: TextureAtlasSprite) { + private def renderSlot(stack: MatrixStack, r: IVertexBuilder, slot: Int, icon: TextureAtlasSprite) { val l = u1 + slot * fs val h = u1 + (slot + 1) * fs r.vertex(stack.last.pose, l, 1, 0).uv(icon.getU(l * 16), icon.getV1).endVertex() diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala index 85a84baa2f..efab260d61 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RelayRenderer.scala @@ -5,14 +5,12 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.opengl.GL11 object RelayRenderer extends Function[TileEntityRendererDispatcher, RelayRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new RelayRenderer(dispatch) @@ -22,25 +20,17 @@ class RelayRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe override def render(switch: tileentity.Relay, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") + RenderSystem.color4f(1, 1, 1, 1) + val activity = math.max(0, 1 - (System.currentTimeMillis() - switch.lastMessage) / 1000.0) if (activity > 0) { - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(activity.toFloat) - stack.pushPose() stack.translate(0.5, 0.5, 0.5) stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY) val icon = Textures.getSprite(Textures.Block.SwitchSideOn) r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() @@ -63,13 +53,7 @@ class RelayRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala index e490356847..0faec2772e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala @@ -4,25 +4,22 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity.Screen import li.cil.oc.integration.util.Wrench import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.Hand import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 -import org.lwjgl.opengl.GL13 -import org.lwjgl.opengl.GL14 object ScreenRenderer extends Function[TileEntityRendererDispatcher, ScreenRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new ScreenRenderer(dispatch) @@ -42,8 +39,6 @@ class ScreenRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityR api.Items.get(Constants.BlockName.ScreenTier2), api.Items.get(Constants.BlockName.ScreenTier3)) - private val canUseBlendColor = true // Minecraft 1.16 already requires OpenGL 2.0 or above. - // ----------------------------------------------------------------------- // // Rendering // ----------------------------------------------------------------------- // @@ -73,13 +68,6 @@ class ScreenRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityR return } - RenderState.checkError(getClass.getName + ".render: checks") - - RenderState.pushAttrib() - - RenderSystem.glMultiTexCoord2f(GL13.GL_TEXTURE1, 0xFF, 0xFF) - RenderState.disableEntityLighting() - RenderState.makeItBlend() RenderSystem.color4f(1, 1, 1, 1) stack.pushPose() @@ -88,29 +76,19 @@ class ScreenRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityR RenderState.checkError(getClass.getName + ".render: setup") - drawOverlay(stack) + drawOverlay(stack, buffer.getBuffer(RenderTypes.BLOCK_OVERLAY)) RenderState.checkError(getClass.getName + ".render: overlay") - if (distance > fadeDistanceSq) { - val alpha = math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat) - if (canUseBlendColor) { - GL14.glBlendColor(0, 0, 0, alpha) - RenderSystem.blendFunc(GL14.GL_CONSTANT_ALPHA, GL11.GL_ONE) - } - } + val alpha = if (distance > fadeDistanceSq) math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat) else 1f RenderState.checkError(getClass.getName + ".render: fade") if (screen.buffer.isRenderingEnabled) { - draw(stack) + draw(stack, alpha, buffer) } - RenderState.disableBlend() - RenderState.enableEntityLighting() - stack.popPose() - RenderState.popAttrib() RenderState.checkError(getClass.getName + ".render: leaving") } @@ -136,37 +114,27 @@ class ScreenRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityR stack.scale(1, -1, 1) } - private def drawOverlay(matrix: MatrixStack) = if (screen.facing == Direction.UP || screen.facing == Direction.DOWN) { + private def drawOverlay(matrix: MatrixStack, r: IVertexBuilder) = if (screen.facing == Direction.UP || screen.facing == Direction.DOWN) { // Show up vector overlay when holding same screen block. val stack = Minecraft.getInstance.player.getItemInHand(Hand.MAIN_HAND) if (!stack.isEmpty) { if (Wrench.holdsApplicableWrench(Minecraft.getInstance.player, screen.getBlockPos) || screens.contains(api.Items.get(stack))) { matrix.pushPose() transform(matrix) - RenderSystem.depthMask(false) matrix.translate(screen.width / 2f - 0.5f, screen.height / 2f - 0.5f, 0.05f) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) - val icon = Textures.getSprite(Textures.Block.ScreenUpIndicator) r.vertex(matrix.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() r.vertex(matrix.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() r.vertex(matrix.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() r.vertex(matrix.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - t.end() - - RenderSystem.depthMask(true) matrix.popPose() } } } - private def draw(stack: MatrixStack) { + private def draw(stack: MatrixStack, alpha: Float, buffer: IRenderTypeBuffer) { RenderState.checkError(getClass.getName + ".draw: entering (aka: wasntme)") val sx = screen.width diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala index 139b09b63b..ad749314cf 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala @@ -5,14 +5,12 @@ import java.util.function.Function import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.tileentity import li.cil.oc.util.RenderState import net.minecraft.client.renderer.IRenderTypeBuffer -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.tileentity.TileEntityRenderer import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.opengl.GL11 object TransposerRenderer extends Function[TileEntityRendererDispatcher, TransposerRenderer] { override def apply(dispatch: TileEntityRendererDispatcher) = new TransposerRenderer(dispatch) @@ -22,64 +20,50 @@ class TransposerRenderer(dispatch: TileEntityRendererDispatcher) extends TileEnt override def render(transposer: tileentity.Transposer, dt: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - val activity = math.max(0, 1 - (System.currentTimeMillis() - transposer.lastOperation) / 1000.0) - if (activity > 0) { - RenderState.pushAttrib() - - RenderState.disableEntityLighting() - RenderState.makeItBlend() - RenderState.setBlendAlpha(activity.toFloat) + RenderSystem.color4f(1, 1, 1, 1) + val activity = math.max(0, 1 - (System.currentTimeMillis() - transposer.lastOperation) / 1000.0f) + if (activity > 0) { stack.pushPose() stack.translate(0.5, 0.5, 0.5) stack.scale(1.0025f, -1.0025f, 1.0025f) stack.translate(-0.5f, -0.5f, -0.5f) - val t = Tessellator.getInstance - val r = t.getBuilder - - Textures.Block.bind() - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = buffer.getBuffer(RenderTypes.BLOCK_OVERLAY_COLOR) val icon = Textures.getSprite(Textures.Block.TransposerOn) - r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() - - r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV1).endVertex() - - r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU0, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU1, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - - r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU0, icon.getV1).endVertex() - r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU1, icon.getV1).endVertex() - r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU0, icon.getV0).endVertex() - - r.vertex(stack.last.pose, 0, 1, 0).uv(icon.getU0, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 1, 1).uv(icon.getU1, icon.getV1).endVertex() - r.vertex(stack.last.pose, 0, 0, 1).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 0, 0, 0).uv(icon.getU0, icon.getV0).endVertex() - - r.vertex(stack.last.pose, 1, 1, 1).uv(icon.getU0, icon.getV1).endVertex() - r.vertex(stack.last.pose, 1, 1, 0).uv(icon.getU1, icon.getV1).endVertex() - r.vertex(stack.last.pose, 1, 0, 0).uv(icon.getU1, icon.getV0).endVertex() - r.vertex(stack.last.pose, 1, 0, 1).uv(icon.getU0, icon.getV0).endVertex() - - t.end() - - RenderState.disableBlend() - RenderState.enableEntityLighting() + r.vertex(stack.last.pose, 0, 1, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + + r.vertex(stack.last.pose, 0, 0, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + + r.vertex(stack.last.pose, 1, 1, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 0, 1, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 1, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 0, 0, 1).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 0, 0, 0).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() + + r.vertex(stack.last.pose, 1, 1, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 1, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV1).endVertex() + r.vertex(stack.last.pose, 1, 0, 0).color(1, 1, 1, activity).uv(icon.getU1, icon.getV0).endVertex() + r.vertex(stack.last.pose, 1, 0, 1).color(1, 1, 1, activity).uv(icon.getU0, icon.getV0).endVertex() stack.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala b/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala index bf7a11cb7f..057a839684 100644 --- a/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/RackMountableRenderHandler.scala @@ -5,6 +5,7 @@ import li.cil.oc.Constants import li.cil.oc.api import li.cil.oc.api.event.RackMountableRenderEvent import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.client.renderer.tileentity.RenderUtil import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ @@ -13,6 +14,7 @@ import net.minecraft.client.Minecraft import net.minecraft.client.renderer.model.ItemCameraTransforms import net.minecraft.entity.item.ItemEntity import net.minecraft.item.ItemStack +import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Vector3f import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.eventbus.api.SubscribeEvent @@ -52,55 +54,47 @@ object RackMountableRenderHandler { } if (System.currentTimeMillis() - e.data.getLong("lastAccess") < 400 && e.rack.world.random.nextDouble() > 0.1) { - RenderState.disableEntityLighting() - RenderState.makeItBlend() - - e.renderOverlayFromAtlas(Textures.Block.RackDiskDriveActivity) - - RenderState.disableBlend() - RenderState.enableEntityLighting() + renderOverlayFromAtlas(e, Textures.Block.RackDiskDriveActivity) } } else if (e.data != null && Servers.contains(api.Items.get(e.rack.getItem(e.mountable)))) { // Server. - RenderState.disableEntityLighting() - RenderState.makeItBlend() - if (e.data.getBoolean("isRunning")) { - e.renderOverlayFromAtlas(Textures.Block.RackServerOn) + renderOverlayFromAtlas(e, Textures.Block.RackServerOn) } if (e.data.getBoolean("hasErrored") && RenderUtil.shouldShowErrorLight(e.rack.hashCode * (e.mountable + 1))) { - e.renderOverlayFromAtlas(Textures.Block.RackServerError) + renderOverlayFromAtlas(e, Textures.Block.RackServerError) } if (System.currentTimeMillis() - e.data.getLong("lastFileSystemAccess") < 400 && e.rack.world.random.nextDouble() > 0.1) { - e.renderOverlayFromAtlas(Textures.Block.RackServerActivity) + renderOverlayFromAtlas(e, Textures.Block.RackServerActivity) } if ((System.currentTimeMillis() - e.data.getLong("lastNetworkActivity") < 300 && System.currentTimeMillis() % 200 > 100) && e.data.getBoolean("isRunning")) { - e.renderOverlayFromAtlas(Textures.Block.RackServerNetworkActivity) + renderOverlayFromAtlas(e, Textures.Block.RackServerNetworkActivity) } - - RenderState.disableBlend() - RenderState.enableEntityLighting() } else if (e.data != null && TerminalServer == api.Items.get(e.rack.getItem(e.mountable))) { // Terminal server. - RenderState.disableEntityLighting() - RenderState.makeItBlend() - - e.renderOverlayFromAtlas(Textures.Block.RackTerminalServerOn) + renderOverlayFromAtlas(e, Textures.Block.RackTerminalServerOn) val countConnected = e.data.getList("keys", NBT.TAG_STRING).size() if (countConnected > 0) { val u0 = 7 / 16f val u1 = u0 + (2 * countConnected - 1) / 16f - e.renderOverlayFromAtlas(Textures.Block.RackTerminalServerPresence, u0, u1) + renderOverlayFromAtlas(e, Textures.Block.RackTerminalServerPresence, u0, u1) } - - RenderState.disableBlend() - RenderState.enableEntityLighting() } } + private def renderOverlayFromAtlas(e: RackMountableRenderEvent.TileEntity, texture: ResourceLocation, u0: Float = 0, u1: Float = 1) { + val matrix = e.stack.last.pose + val r = e.typeBuffer.getBuffer(RenderTypes.BLOCK_OVERLAY) + val icon = Textures.getSprite(texture) + r.vertex(matrix, u0, e.v1, 0).uv(icon.getU(u0 * 16), icon.getV(e.v1 * 16)).endVertex(); + r.vertex(matrix, u1, e.v1, 0).uv(icon.getU(u1 * 16), icon.getV(e.v1 * 16)).endVertex(); + r.vertex(matrix, u1, e.v0, 0).uv(icon.getU(u1 * 16), icon.getV(e.v0 * 16)).endVertex(); + r.vertex(matrix, u0, e.v0, 0).uv(icon.getU(u0 * 16), icon.getV(e.v0 * 16)).endVertex(); + } + @SubscribeEvent def onRackMountableRendering(e: RackMountableRenderEvent.Block): Unit = { if (DiskDriveMountable == api.Items.get(e.rack.getItem(e.mountable))) { From 7306f0874e31b0e1faa15d50686abef93b6413d2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 9 Sep 2022 23:32:50 +0200 Subject: [PATCH 068/159] Fix rack mountable interaction --- src/main/scala/li/cil/oc/common/block/SimpleBlock.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 5cc050efd9..4a219c338d 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -187,7 +187,11 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str ActionResultType.sidedSuccess(world.isClientSide) case _ => { val loc = trace.getLocation - if (localOnBlockActivated(world, pos, player, hand, heldItem, trace.getDirection, loc.x.toFloat, loc.y.toFloat, loc.z.toFloat)) + val pos = trace.getBlockPos + val x = loc.x.toFloat - pos.getX + val y = loc.y.toFloat - pos.getY + val z = loc.z.toFloat - pos.getZ + if (localOnBlockActivated(world, pos, player, hand, heldItem, trace.getDirection, x, y, z)) ActionResultType.sidedSuccess(world.isClientSide) else ActionResultType.PASS } } From df79f2f9f7bff32ac72020235473388c93b2712a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 10 Sep 2022 00:06:55 +0200 Subject: [PATCH 069/159] Fix screens not being closeable --- src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala index ff10fd2020..097e3afff9 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala @@ -99,6 +99,10 @@ trait InputBuffer extends DisplayBuffer { override def keyPressed(keyCode: Int, scanCode: Int, mods: Int): Boolean = { if (onInput(InputMappings.getKey(keyCode, scanCode))) return true if (!this.isInstanceOf[ContainerScreen[_]] || !ItemSearch.isInputFocused) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE && shouldCloseOnEsc) { + onClose() + return true + } if (buffer != null && keyCode != GLFW.GLFW_KEY_UNKNOWN) { if (hasKeyboard) { if (pressedKeys.add(keyCode) || !ignoreRepeat(keyCode)) { From 1db849cd69bd26986b487b6bd9ce3f27b7e4261b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:55:14 +0200 Subject: [PATCH 070/159] Fix computer and robot GUI buffer rendering --- .../scala/li/cil/oc/client/gui/Drone.scala | 2 +- .../scala/li/cil/oc/client/gui/Robot.scala | 56 +++--- .../scala/li/cil/oc/client/gui/Screen.scala | 16 +- .../oc/client/gui/traits/DisplayBuffer.scala | 22 +-- .../oc/client/gui/traits/InputBuffer.scala | 16 +- .../client/renderer/gui/BufferRenderer.scala | 166 +++++++----------- .../cil/oc/common/component/TextBuffer.scala | 2 +- .../li/cil/oc/common/container/Robot.scala | 5 +- 8 files changed, 119 insertions(+), 166 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index f3ec46817b..a5da5eb429 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -86,7 +86,7 @@ class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITex RenderState.popAttrib() } - override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0 + override protected def changeSize(w: Double, h: Double) = 2.0 override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) = drawSecondaryForegroundLayer(stack, mouseX, mouseY) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index 8720bf09e3..bb09f38721 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -13,7 +13,6 @@ import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.container -import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.widget.button.Button @@ -32,7 +31,7 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex with traits.InputBuffer with INestedGuiEventHandler { override protected val buffer: TextBuffer = inventoryContainer.info.screenBuffer - .map(ComponentTracker.get(Minecraft.getInstance.level, _)) + .flatMap(ComponentTracker.get(Minecraft.getInstance.level, _)) .collectFirst { case buffer: TextBuffer => buffer }.orNull @@ -53,10 +52,11 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex // Scroll offset for robot inventory. private var inventoryOffset = 0 + var isScrolling = false - private def canScroll = inventoryContainer.otherInventory.getContainerSize > 16 + private def canScroll = inventoryContainer.info.mainInvSize > 16 - private def maxOffset = inventoryContainer.otherInventory.getContainerSize / 4 - 4 + private def maxOffset = inventoryContainer.info.mainInvSize / 4 - 4 private val slotSize = 18 @@ -77,7 +77,7 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex private val scrollX = inventoryX + slotSize * 4 + 2 private val scrollY = inventoryY private val scrollWidth = 8 - private val scrollHeight = 94 + private val scrollHeight = 92 private val power = addCustomWidget(new ProgressBar(26, 156 - deltaY)) @@ -88,8 +88,8 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex override def render(stack: MatrixStack, mouseX: Int, mouseY: Int, dt: Float) { powerButton.toggled = inventoryContainer.isRunning scrollButton.active = canScroll - scrollButton.hoverOverride = isDragging - if (inventoryContainer.otherInventory.getContainerSize < 16 + inventoryOffset * 4) { + scrollButton.hoverOverride = isScrolling + if (inventoryContainer.info.mainInvSize < 16 + inventoryOffset * 4) { if (inventoryOffset != 0) scrollTo(0) } super.render(stack, mouseX, mouseY, dt) @@ -110,13 +110,11 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex override def drawBuffer(stack: MatrixStack) { if (buffer != null) { stack.translate(bufferX, bufferY, 0) - RenderState.disableEntityLighting() stack.pushPose() stack.translate(-3, -3, 0) RenderSystem.color4f(1, 1, 1, 1) - BufferRenderer.drawBackground() + BufferRenderer.drawBackground(stack, bufferRenderWidth.toInt, bufferRenderHeight.toInt, forRobot = true) stack.popPose() - RenderState.makeItBlend() val scaleX = bufferRenderWidth / buffer.renderWidth val scaleY = bufferRenderHeight / buffer.renderHeight val scale = math.min(scaleX, scaleY).toFloat @@ -132,11 +130,11 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex } } - override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) {} + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) = + drawSecondaryForegroundLayer(stack, mouseX, mouseY) override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) - RenderState.pushAttrib() if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX - leftPos, mouseY - topPos)) { val tooltip = new java.util.ArrayList[String] val format = Localization.Computer.Power + ": %d%% (%d/%d)" @@ -150,17 +148,16 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex tooltip.addAll(asJavaCollection(if (inventoryContainer.isRunning) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) copiedDrawHoveringText(stack, tooltip, mouseX - leftPos, mouseY - topPos, font) } - RenderState.popAttrib() } override protected def renderBg(stack: MatrixStack, dt: Float, mouseX: Int, mouseY: Int) { - RenderSystem.color3f(1, 1, 1) + RenderSystem.color4f(1, 1, 1, 1) if (buffer != null) Textures.bind(Textures.GUI.Robot) else Textures.bind(Textures.GUI.RobotNoScreen) blit(stack, leftPos, topPos, 0, 0, imageWidth, imageHeight) power.level = inventoryContainer.globalBuffer.toDouble / inventoryContainer.globalBufferSize drawWidgets(stack) - if (inventoryContainer.otherInventory.getContainerSize > 0) { + if (inventoryContainer.info.mainInvSize > 0) { drawSelection(stack) } @@ -174,15 +171,23 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex val mx = mouseX.asInstanceOf[Int] val my = mouseY.asInstanceOf[Int] if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isCoordinateOverScrollBar(mx - leftPos, my - topPos)) { - setDragging(true) + isScrolling = true scrollMouse(mouseY) true } else super.mouseClicked(mouseX, mouseY, button) } + override def mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean = { + if (canScroll && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && isScrolling) { + isScrolling = false + return true + } + super.mouseReleased(mouseX, mouseY, button) + } + override def mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean = { - if (isDragging) { + if (isScrolling) { scrollMouse(mouseY) true } @@ -219,26 +224,29 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex private def scrollTo(row: Int) { inventoryOffset = math.max(0, math.min(maxOffset, row)) menu.generateSlotsFor(inventoryOffset) + val yMin = topPos + scrollY + 1 + if (maxOffset > 0) { + scrollButton.y = yMin + (scrollHeight - 13) * inventoryOffset / maxOffset + } + else { + scrollButton.y = yMin + } } - override protected def changeSize(w: Double, h: Double, recompile: Boolean): Double = { + override protected def changeSize(w: Double, h: Double): Double = { val bw = w * TextBufferRenderCache.renderer.charRenderWidth val bh = h * TextBufferRenderCache.renderer.charRenderHeight val scaleX = math.min(bufferRenderWidth / bw, 1) val scaleY = math.min(bufferRenderHeight / bh, 1) - if (recompile) { - BufferRenderer.compileBackground(bufferRenderWidth.toInt, bufferRenderHeight.toInt, forRobot = true) - } math.min(scaleX, scaleY) } private def drawSelection(stack: MatrixStack) { val slot = inventoryContainer.selectedSlot - inventoryOffset * 4 if (slot >= 0 && slot < 16) { - RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) - val now = System.currentTimeMillis() / 1000.0f - val offsetV = ((now - now.toInt) * selectionsStates).toInt * selectionStepV + val now = System.currentTimeMillis() % 1000 / 1000.0f + val offsetV = (now * selectionsStates).toInt * selectionStepV val x = leftPos + inventoryX - 1 + (slot % 4) * (selectionSize - 2) val y = topPos + inventoryY - 1 + (slot / 4) * (selectionSize - 2) diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index 4a8adab78b..5dc7eaf5fe 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -1,11 +1,9 @@ package li.cil.oc.client.gui import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.gui.BufferRenderer -import li.cil.oc.util.RenderState import net.minecraft.client.gui.INestedGuiEventHandler import net.minecraft.client.gui.screen import net.minecraft.client.settings.KeyBinding @@ -27,6 +25,8 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has private var x, y = 0 + private var innerWidth, innerHeight = 0 + private var mx, my = -1 override def mouseScrolled(mouseX: Double, mouseY: Double, scroll: Double): Boolean = { @@ -112,28 +112,24 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has override def drawBuffer(stack: MatrixStack) { stack.translate(x, y, 0) - BufferRenderer.drawBackground() + BufferRenderer.drawBackground(stack, innerWidth, innerHeight) if (hasPower()) { stack.translate(bufferMargin, bufferMargin, 0) stack.scale(scale.toFloat, scale.toFloat, 1) - RenderState.makeItBlend() BufferRenderer.drawText(stack, buffer) } } - override protected def changeSize(w: Double, h: Double, recompile: Boolean) = { + override protected def changeSize(w: Double, h: Double) = { val bw = buffer.renderWidth val bh = buffer.renderHeight val scaleX = math.min(width / (bw + bufferMargin * 2.0), 1) val scaleY = math.min(height / (bh + bufferMargin * 2.0), 1) val scale = math.min(scaleX, scaleY) - val innerWidth = (bw * scale).toInt - val innerHeight = (bh * scale).toInt + innerWidth = (bw * scale).toInt + innerHeight = (bh * scale).toInt x = (width - (innerWidth + bufferMargin * 2)) / 2 y = (height - (innerHeight + bufferMargin * 2)) / 2 - if (recompile) { - BufferRenderer.compileBackground(innerWidth, innerHeight) - } scale } } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala b/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala index c8014c24f5..7069895a62 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/DisplayBuffer.scala @@ -1,10 +1,7 @@ package li.cil.oc.client.gui.traits import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem -import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.util.RenderState -import net.minecraft.client.Minecraft import net.minecraft.client.gui.screen.Screen trait DisplayBuffer extends Screen { @@ -16,29 +13,14 @@ trait DisplayBuffer extends Screen { protected def bufferRows: Int - protected var guiSizeChanged = false - - protected var currentWidth, currentHeight = -1 - protected var scale = 0.0 - override protected def init() = { - super.init() - BufferRenderer.init(Minecraft.getInstance.textureManager) - guiSizeChanged = true - } - protected def drawBufferLayer(stack: MatrixStack) { - val oldWidth = currentWidth - val oldHeight = currentHeight - currentWidth = bufferColumns - currentHeight = bufferRows - scale = changeSize(currentWidth, currentHeight, guiSizeChanged || oldWidth != currentWidth || oldHeight != currentHeight) + scale = changeSize(bufferColumns, bufferRows) RenderState.checkError(getClass.getName + ".drawBufferLayer: entering (aka: wasntme)") stack.pushPose() - RenderState.disableEntityLighting() drawBuffer(stack) stack.popPose() @@ -47,5 +29,5 @@ trait DisplayBuffer extends Screen { protected def drawBuffer(stack: MatrixStack): Unit - protected def changeSize(w: Double, h: Double, recompile: Boolean): Double + protected def changeSize(w: Double, h: Double): Double } diff --git a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala index 097e3afff9..f2d5a49d4b 100644 --- a/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/traits/InputBuffer.scala @@ -1,7 +1,6 @@ package li.cil.oc.client.gui.traits import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.api import li.cil.oc.client.KeyBindings import li.cil.oc.client.Textures @@ -43,7 +42,6 @@ trait InputBuffer extends DisplayBuffer { if (System.currentTimeMillis() - showKeyboardMissing < 1000) { Textures.bind(Textures.GUI.KeyboardMissing) - RenderSystem.disableDepthTest() val x = bufferX + buffer.renderWidth - 16 val y = bufferY + buffer.renderHeight - 16 @@ -57,8 +55,6 @@ trait InputBuffer extends DisplayBuffer { r.vertex(stack.last.pose, x, y, 0).uv(0, 0).endVertex() t.end() - RenderSystem.enableDepthTest() - RenderState.checkError(getClass.getName + ".drawBufferLayer: keyboard icon") } } @@ -97,12 +93,12 @@ trait InputBuffer extends DisplayBuffer { } override def keyPressed(keyCode: Int, scanCode: Int, mods: Int): Boolean = { - if (onInput(InputMappings.getKey(keyCode, scanCode))) return true if (!this.isInstanceOf[ContainerScreen[_]] || !ItemSearch.isInputFocused) { if (keyCode == GLFW.GLFW_KEY_ESCAPE && shouldCloseOnEsc) { onClose() return true } + if (onInput(InputMappings.getKey(keyCode, scanCode))) return true if (buffer != null && keyCode != GLFW.GLFW_KEY_UNKNOWN) { if (hasKeyboard) { if (pressedKeys.add(keyCode) || !ignoreRepeat(keyCode)) { @@ -117,11 +113,13 @@ trait InputBuffer extends DisplayBuffer { } override def keyReleased(keyCode: Int, scanCode: Int, mods: Int): Boolean = { - if (pressedKeys.remove(keyCode)) { - buffer.keyUp('\u0000', keyCode, null) - return true + if (!this.isInstanceOf[ContainerScreen[_]] || !ItemSearch.isInputFocused) { + if (pressedKeys.remove(keyCode)) { + buffer.keyUp('\u0000', keyCode, null) + return true + } + // Wasn't pressed while viewing the screen. } - // Wasn't pressed while viewing the screen. super.keyReleased(keyCode, scanCode, mods) } diff --git a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala index 75f5198af6..12376b6f59 100644 --- a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala @@ -1,11 +1,14 @@ package li.cil.oc.client.renderer.gui import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.api import li.cil.oc.client.Textures import li.cil.oc.util.RenderState -import net.minecraft.client.renderer.texture.TextureManager +import net.minecraft.client.renderer.IRenderTypeBuffer +import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.vertex.DefaultVertexFormats +import net.minecraft.util.math.vector.Matrix4f import org.lwjgl.opengl.GL11 object BufferRenderer { @@ -13,103 +16,68 @@ object BufferRenderer { val innerMargin = 1 - private var textureManager: Option[TextureManager] = None - - private var displayLists = 0 - - def init(tm: TextureManager) = this.synchronized(if (textureManager.isEmpty) { - RenderState.checkError(getClass.getName + ".displayLists: entering (aka: wasntme)") - - textureManager = Some(tm) - displayLists = GL11.glGenLists(2) - - RenderState.checkError(getClass.getName + ".displayLists: leaving") - }) - - def compileBackground(bufferWidth: Int, bufferHeight: Int, forRobot: Boolean = false) = - if (textureManager.isDefined) { - RenderState.checkError(getClass.getName + ".compileBackground: entering (aka: wasntme)") - - val innerWidth = innerMargin * 2 + bufferWidth - val innerHeight = innerMargin * 2 + bufferHeight - - GL11.glNewList(displayLists, GL11.GL_COMPILE) - - Textures.bind(Textures.GUI.Borders) - - GL11.glBegin(GL11.GL_QUADS) - - val margin = if (forRobot) 2 else 7 - val (c0, c1, c2, c3) = if (forRobot) (5, 7, 9, 11) else (0, 7, 9, 16) - - // Top border (left corner, middle bar, right corner). - drawBorder( - 0, 0, margin, margin, - c0, c0, c1, c1) - drawBorder( - margin, 0, innerWidth, margin, - c1 + 0.25, c0, c2 - 0.25, c1) - drawBorder( - margin + innerWidth, 0, margin, margin, - c2, c0, c3, c1) - - // Middle area (left bar, screen background, right bar). - drawBorder( - 0, margin, margin, innerHeight, - c0, c1 + 0.25, c1, c2 - 0.25) - drawBorder( - margin, margin, innerWidth, innerHeight, - c1 + 0.25, c1 + 0.25, c2 - 0.25, c2 - 0.25) - drawBorder( - margin + innerWidth, margin, margin, innerHeight, - c2, c1 + 0.25, c3, c2 - 0.25) - - // Bottom border (left corner, middle bar, right corner). - drawBorder( - 0, margin + innerHeight, margin, margin, - c0, c2, c1, c3) - drawBorder( - margin, margin + innerHeight, innerWidth, margin, - c1 + 0.25, c2, c2 - 0.25, c3) - drawBorder( - margin + innerWidth, margin + innerHeight, margin, margin, - c2, c2, c3, c3) - - GL11.glEnd() - - GL11.glEndList() - - RenderState.checkError(getClass.getName + ".compileBackground: leaving") - } - - def drawBackground() = - if (textureManager.isDefined) { - GL11.glCallList(displayLists) - } - - def drawText(stack: MatrixStack, screen: api.internal.TextBuffer) = - if (textureManager.isDefined) { - RenderState.pushAttrib() - RenderSystem.depthMask(false) - val changed = screen.renderText(stack) - RenderSystem.depthMask(true) - RenderState.popAttrib() - changed - } - else false + def drawBackground(stack: MatrixStack, bufferWidth: Int, bufferHeight: Int, forRobot: Boolean = false) = { + RenderState.checkError(getClass.getName + ".drawBackground: entering (aka: wasntme)") + + val innerWidth = innerMargin * 2 + bufferWidth + val innerHeight = innerMargin * 2 + bufferHeight + + val t = Tessellator.getInstance + val r = t.getBuilder + Textures.bind(Textures.GUI.Borders) + r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + val margin = if (forRobot) 2 else 7 + val (c0, c1, c2, c3) = if (forRobot) (5, 7, 9, 11) else (0, 7, 9, 16) + + // Top border (left corner, middle bar, right corner). + drawQuad(stack.last.pose, r, + 0, 0, margin, margin, + c0, c0, c1, c1) + drawQuad(stack.last.pose, r, + margin, 0, innerWidth, margin, + c1 + 0.25f, c0, c2 - 0.25f, c1) + drawQuad(stack.last.pose, r, + margin + innerWidth, 0, margin, margin, + c2, c0, c3, c1) + + // Middle area (left bar, screen background, right bar). + drawQuad(stack.last.pose, r, + 0, margin, margin, innerHeight, + c0, c1 + 0.25f, c1, c2 - 0.25f) + drawQuad(stack.last.pose, r, + margin, margin, innerWidth, innerHeight, + c1 + 0.25f, c1 + 0.25f, c2 - 0.25f, c2 - 0.25f) + drawQuad(stack.last.pose, r, + margin + innerWidth, margin, margin, innerHeight, + c2, c1 + 0.25f, c3, c2 - 0.25f) + + // Bottom border (left corner, middle bar, right corner). + drawQuad(stack.last.pose, r, + 0, margin + innerHeight, margin, margin, + c0, c2, c1, c3) + drawQuad(stack.last.pose, r, + margin, margin + innerHeight, innerWidth, margin, + c1 + 0.25f, c2, c2 - 0.25f, c3) + drawQuad(stack.last.pose, r, + margin + innerWidth, margin + innerHeight, margin, margin, + c2, c2, c3, c3) + + t.end() + + RenderState.checkError(getClass.getName + ".drawBackground: leaving") + } - private def drawBorder(x: Double, y: Double, w: Double, h: Double, u1: Double, v1: Double, u2: Double, v2: Double) = { - val u1d = u1 / 16.0 - val u2d = u2 / 16.0 - val v1d = v1 / 16.0 - val v2d = v2 / 16.0 - GL11.glTexCoord2d(u1d, v2d) - GL11.glVertex3d(x, y + h, 0) - GL11.glTexCoord2d(u2d, v2d) - GL11.glVertex3d(x + w, y + h, 0) - GL11.glTexCoord2d(u2d, v1d) - GL11.glVertex3d(x + w, y, 0) - GL11.glTexCoord2d(u1d, v1d) - GL11.glVertex3d(x, y, 0) + private def drawQuad(matrix: Matrix4f, builder: IVertexBuilder, x: Float, y: Float, w: Float, h: Float, u1: Float, v1: Float, u2: Float, v2: Float) = { + val u1f = u1 / 16f + val u2f = u2 / 16f + val v1f = v1 / 16f + val v2f = v2 / 16f + builder.vertex(matrix, x, y + h, 0).uv(u1f, v2f).endVertex() + builder.vertex(matrix, x + w, y + h, 0).uv(u2f, v2f).endVertex() + builder.vertex(matrix, x+ w, y, 0).uv(u2f, v1f).endVertex() + builder.vertex(matrix, x, y, 0).uv(u1f, v1f).endVertex() } + + def drawText(stack: MatrixStack, screen: api.internal.TextBuffer) = screen.renderText(stack) } diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index 6379456e7c..e60c964fd7 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -630,7 +630,7 @@ object TextBuffer { @OnlyIn(Dist.CLIENT) override def render(stack: MatrixStack) = { val wasDirty = dirty - TextBufferRenderCache.render(stack.asInstanceOf[MatrixStack], renderer) + TextBufferRenderCache.render(stack, renderer) wasDirty } diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 1fae7ef6aa..e8da264928 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -83,12 +83,13 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe // Slot.x and Slot.y are final, so have to rebuild when scrolling def generateSlotsFor(scroll: Int) { - for (i <- 0 to 15) { + val maxRows = math.max(info.mainInvSize / 4, 4) + for (i <- 0 until maxRows) { val y = 156 + (i - scroll) * slotSize - deltaY for (j <- 0 to 3) { val x = 170 + j * slotSize - val slot = new InventorySlot(this, otherInventory, slots.size, x, y, i >= scroll && i < scroll + 4) val idx = 4 + j + 4 * i + val slot = new InventorySlot(this, otherInventory, idx, x, y, i >= scroll && i < scroll + 4) if (slots.size() <= idx) addSlot(slot) else slots.set(idx, slot) } From e2e3ae3e0b202756504c8c352295a9f724ba3d0e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 12 Sep 2022 00:58:06 +0200 Subject: [PATCH 071/159] Reimplement rendering cache for TextBuffer --- .../cil/oc/client/renderer/RenderCache.java | 117 ++++++++++++++++++ .../cil/oc/client/renderer/RenderTypes.java | 53 ++++++++ .../renderer/TextBufferRenderCache.scala | 90 ++------------ .../renderer/font/DynamicFontRenderer.scala | 45 +++++-- .../renderer/font/StaticFontRenderer.scala | 36 ++++++ .../renderer/font/TextureFontRenderer.scala | 98 +++++---------- 6 files changed, 287 insertions(+), 152 deletions(-) create mode 100644 src/main/scala/li/cil/oc/client/renderer/RenderCache.java diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderCache.java b/src/main/scala/li/cil/oc/client/renderer/RenderCache.java new file mode 100644 index 0000000000..c3460cb6f9 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/RenderCache.java @@ -0,0 +1,117 @@ +package li.cil.oc.client.renderer +; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.BufferBuilder.DrawState; +import net.minecraft.client.renderer.GLAllocation; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Tessellator; +import org.lwjgl.system.MemoryUtil; + +public class RenderCache implements IRenderTypeBuffer { + public static class DrawEntry { + private final DrawState state; + private final ByteBuffer data; + + public DrawEntry(DrawState state, ByteBuffer data, boolean copy) { + this.state = state; + if (copy) + { + int bufferCap = state.format().getVertexSize() * state.vertexCount(); + ByteBuffer temp = GLAllocation.createByteBuffer(bufferCap); + temp.put(data).flip(); + data = temp; + } + this.data = data; + } + + public DrawState state() { + return state; + } + + public ByteBuffer data() { + return data; + } + } + + private final Map> cached; + private RenderType activeType; + private BufferBuilder activeBuilder; + + public RenderCache() { + cached = new HashMap<>(); + } + + public boolean isEmpty() { + for (List frames : cached.values()) { + if (!frames.isEmpty()) return false; + } + return true; + } + + public void clear() { + cached.forEach((k, v) -> v.clear()); + } + + private void flush(RenderType type) { + if (type == activeType) { + activeBuilder.end(); + List frames = cached.computeIfAbsent(type, k -> new ArrayList<>()); + Pair rendered = activeBuilder.popNextBuffer(); + if (rendered.getSecond().hasRemaining()) { + frames.add(new DrawEntry(rendered.getFirst(), rendered.getSecond(), true)); + } + activeType = null; + } + } + + @Override + public IVertexBuilder getBuffer(RenderType type) { + if (type == null) throw new NullPointerException(); // Same as vanilla. + if (activeType != null) { + if (activeType == type) return activeBuilder; + flush(activeType); + } + activeType = type; + activeBuilder = Tessellator.getInstance().getBuilder(); + activeBuilder.clear(); + activeBuilder.begin(type.mode(), type.format()); + return activeBuilder; + } + + public void finish() { + // Flush the last active type (if any) so it gets rendered too. + if (activeType != null) flush(activeType); + } + + public void render(MatrixStack stack) { + // Apply transform globally so we don't have to update stored vertices. + RenderSystem.pushMatrix(); + RenderSystem.multMatrix(stack.last().pose()); + + cached.forEach((type, frames) -> { + if (!frames.isEmpty()) { + type.setupRenderState(); + frames.forEach(frame -> { + DrawState state = frame.state(); + state.format().setupBufferState(MemoryUtil.memAddress(frame.data())); + RenderSystem.drawArrays(state.mode(), 0, state.vertexCount()); + state.format().clearBufferState(); + }); + type.clearRenderState(); + } + }); + + RenderSystem.popMatrix(); + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 7752611181..90ca13d578 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -3,10 +3,13 @@ import java.util.OptionalDouble; import com.google.common.collect.ImmutableList; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.platform.GlStateManager; import li.cil.oc.OpenComputers; import li.cil.oc.client.Textures; import net.minecraft.client.renderer.RenderState.LineState; import net.minecraft.client.renderer.RenderState.TextureState; +import net.minecraft.client.renderer.RenderState.TexturingState; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType.State; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; @@ -82,6 +85,56 @@ private static final RenderType createUpgrade(String name, ResourceLocation text .setAlphaState(DEFAULT_ALPHA) .createCompositeState(false)); + public static final RenderType FONT_QUAD = create(OpenComputers.ID() + ":font_quad", + DefaultVertexFormats.POSITION_COLOR, GL11.GL_QUADS, 1024, State.builder() + .createCompositeState(false)); + + private static class CustomTextureState extends TexturingState { + public CustomTextureState(int id) { + super("custom_tex_" + id, () -> { + // Should already be enabled, but vanilla does it too. + RenderSystem.enableTexture(); + RenderSystem.bindTexture(id); + }, () -> {}); + } + } + + private static class LinearTexturingState extends TexturingState { + public LinearTexturingState(boolean linear) { + super(linear ? "lin_font_texturing" : "near_font_texturing", () -> { + // Texture is already bound, only have to make set minify filter. + if (linear) GlStateManager._texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + else GlStateManager._texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + }, () -> { + // Nothing to do, the texture was already unbound. + }); + } + } + + private static final LinearTexturingState NEAR = new LinearTexturingState(false); + private static final LinearTexturingState LINEAR = new LinearTexturingState(true); + + public static final RenderType createFontTex(String name, ResourceLocation texture, boolean linear) { + return create(OpenComputers.ID() + ":font_stat_" + name, + DefaultVertexFormats.POSITION_COLOR_TEX, GL11.GL_QUADS, 1024, State.builder() + // First parameter is blur (i.e. linear filter). + // We can't use it because it's also MAG_FILTER. + .setTextureState(new TextureState(texture, false, false)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setAlphaState(DEFAULT_ALPHA) + .setTexturingState(linear ? LINEAR : NEAR) + .createCompositeState(false)); + } + + public static final RenderType createFontTex(int id) { + return create(OpenComputers.ID() + ":font_dyn_" + id, + DefaultVertexFormats.POSITION_COLOR_TEX, GL11.GL_QUADS, 1024, State.builder() + .setTexturingState(new CustomTextureState(id)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setAlphaState(DEFAULT_ALPHA) + .createCompositeState(false)); + } + private RenderTypes() { super(null, null, 0, 0, false, false, null, null); throw new Error(); diff --git a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala index 54f44434db..9843f3bade 100644 --- a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala +++ b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala @@ -1,113 +1,49 @@ package li.cil.oc.client.renderer -import java.util.concurrent.Callable import java.util.concurrent.TimeUnit import com.google.common.cache.CacheBuilder -import com.google.common.cache.RemovalListener -import com.google.common.cache.RemovalNotification import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.Settings import li.cil.oc.client.renderer.font.TextBufferRenderData import li.cil.oc.util.RenderState -import net.minecraft.tileentity.TileEntity import net.minecraftforge.event.TickEvent.ClientTickEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import org.lwjgl.opengl.GL11 -object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEntity, Int] { +object TextBufferRenderCache { val renderer = if (Settings.get.fontRenderer == "texture") new font.StaticFontRenderer() else new font.DynamicFontRenderer() private val cache = com.google.common.cache.CacheBuilder.newBuilder(). expireAfterAccess(2, TimeUnit.SECONDS). - removalListener(this). - asInstanceOf[CacheBuilder[TextBufferRenderData, Int]]. - build[TextBufferRenderData, Int]() - - // To allow access in cache entry init. - private var currentBuffer: TextBufferRenderData = _ + build[TextBufferRenderData, RenderCache]() // ----------------------------------------------------------------------- // // Rendering // ----------------------------------------------------------------------- // def render(stack: MatrixStack, buffer: TextBufferRenderData) { - currentBuffer = buffer - compileOrDraw(stack, cache.get(currentBuffer, this)) - } - - private def compileOrDraw(stack: MatrixStack, list: Int) = { - if (currentBuffer.dirty) { - RenderState.checkError(getClass.getName + ".compileOrDraw: entering (aka: wasntme)") + RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") - for (line <- currentBuffer.data.buffer) { + val cached = cache.get(buffer, () => new RenderCache) + if (buffer.dirty || cached.isEmpty) { + for (line <- buffer.data.buffer) { renderer.generateChars(line) } - val doCompile = !RenderState.compilingDisplayList - if (doCompile) { - currentBuffer.dirty = false - GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE) - - RenderState.checkError(getClass.getName + ".compileOrDraw: glNewList") - } - - renderer.drawBuffer(stack, currentBuffer.data, currentBuffer.viewport._1, currentBuffer.viewport._2) - - RenderState.checkError(getClass.getName + ".compileOrDraw: drawString") - - if (doCompile) { - GL11.glEndList() - - RenderState.checkError(getClass.getName + ".compileOrDraw: glEndList") - } - - RenderState.checkError(getClass.getName + ".compileOrDraw: leaving") - - true - } - else { - GL11.glCallList(list) - RenderSystem.enableTexture() - RenderSystem.depthMask(true) - RenderSystem.color4f(1, 1, 1, 1) - - // Because display lists and the RenderSystem don't like each other, apparently. - GL11.glEnable(GL11.GL_TEXTURE_2D) - RenderState.bindTexture(0) - GL11.glDepthMask(true) - GL11.glColor4f(1, 1, 1, 1) + buffer.dirty = false - RenderState.disableBlend() + cached.clear() + renderer.drawBuffer(new MatrixStack(), cached, buffer.data, buffer.viewport._1, buffer.viewport._2) + cached.finish() - RenderState.checkError(getClass.getName + ".compileOrDraw: glCallList") + RenderState.checkError(getClass.getName + ".render: compiled buffer") } - } - - // ----------------------------------------------------------------------- // - // Cache - // ----------------------------------------------------------------------- // - - def call = { - RenderState.checkError(getClass.getName + ".call: entering (aka: wasntme)") - - val list = GL11.glGenLists(1) - currentBuffer.dirty = true // Force compilation. - - RenderState.checkError(getClass.getName + ".call: leaving") - - list - } - - def onRemoval(e: RemovalNotification[TileEntity, Int]) { - RenderState.checkError(getClass.getName + ".onRemoval: entering (aka: wasntme)") - GL11.glDeleteLists(e.getValue, 1) + cached.render(stack) - RenderState.checkError(getClass.getName + ".onRemoval: leaving") + RenderState.checkError(getClass.getName + ".render: leaving") } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala index 6d5e36a069..109286eb7b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala @@ -1,11 +1,14 @@ package li.cil.oc.client.renderer.font import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Settings +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture import li.cil.oc.util.FontUtils import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.texture.TextureUtil import net.minecraft.resources.IReloadableResourceManager import net.minecraft.resources.IResourceManager @@ -67,6 +70,11 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa RenderState.checkError(getClass.getName + ".bindTexture") } + override protected def selectType(index: Int): RenderType = { + activeTexture = textures(index) + activeTexture.getType + } + override protected def generateChar(char: Char) { charMap.getOrElseUpdate(char, createCharIcon(char)) } @@ -78,6 +86,13 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa } } + override protected def drawChar(builder: IVertexBuilder, matrix: Matrix4f, color: Int, tx: Float, ty: Float, char: Char) { + charMap.get(char) match { + case Some(icon) if icon.texture == activeTexture => icon.draw(builder, matrix, color, tx, ty) + case _ => + } + } + private def createCharIcon(char: Char): DynamicFontRenderer.CharIcon = { if (FontUtils.wcwidth(char) < 1 || glyphProvider.getGlyph(char) == null) { if (char == '?') null @@ -102,12 +117,14 @@ object DynamicFontRenderer { if (Settings.get.textLinearFiltering) { GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR) } else { - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) } GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST) GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(size * size * 4)) RenderState.bindTexture(0) + private val rt = RenderTypes.createFontTex(id) + RenderState.checkError(getClass.getName + ".: create texture") // Some padding to avoid bleeding. @@ -115,8 +132,8 @@ object DynamicFontRenderer { private val cellHeight = owner.charHeight + 2 private val cols = size / cellWidth private val rows = size / cellHeight - private val uStep = cellWidth / size.toDouble - private val vStep = cellHeight / size.toDouble + private val uStep = cellWidth / size.toFloat + private val vStep = cellHeight / size.toFloat private val pad = 1f / size private val capacity = cols * rows @@ -130,6 +147,8 @@ object DynamicFontRenderer { RenderState.bindTexture(id) } + def getType() = rt + def isFull(char: Char) = chars + FontUtils.wcwidth(char) > capacity def add(char: Char) = { @@ -152,25 +171,35 @@ object DynamicFontRenderer { } } - class CharIcon(val texture: CharTexture, val w: Int, val h: Int, val u1: Double, val v1: Double, val u2: Double, val v2: Double) { + class CharIcon(val texture: CharTexture, val w: Int, val h: Int, val u1: Float, val v1: Float, val u2: Float, val v2: Float) { def draw(matrix: Matrix4f, tx: Float, ty: Float) { - GL11.glTexCoord2d(u1, v2) + GL11.glTexCoord2f(u1, v2) val vec = new Vector4f(tx, ty + h, 0, 1) vec.transform(matrix) GL11.glVertex3f(vec.x, vec.y, vec.z) - GL11.glTexCoord2d(u2, v2) + GL11.glTexCoord2f(u2, v2) vec.set(tx + w, ty + h, 0, 1) vec.transform(matrix) GL11.glVertex3f(vec.x, vec.y, vec.z) - GL11.glTexCoord2d(u2, v1) + GL11.glTexCoord2f(u2, v1) vec.set(tx + w, ty, 0, 1) vec.transform(matrix) GL11.glVertex3f(vec.x, vec.y, vec.z) - GL11.glTexCoord2d(u1, v1) + GL11.glTexCoord2f(u1, v1) vec.set(tx, ty, 0, 1) vec.transform(matrix) GL11.glVertex3f(vec.x, vec.y, vec.z) } + + def draw(builder: IVertexBuilder, matrix: Matrix4f, color: Int, tx: Float, ty: Float) { + val r = ((color >> 16) & 0xFF) / 255f + val g = ((color >> 8) & 0xFF) / 255f + val b = (color & 0xFF) / 255f + builder.vertex(matrix, tx, ty + h, 0).color(r, g, b, 1f).uv(u1, v2).endVertex() + builder.vertex(matrix, tx + w, ty + h, 0).color(r, g, b, 1f).uv(u2, v2).endVertex() + builder.vertex(matrix, tx + w, ty, 0).color(r, g, b, 1f).uv(u2, v1).endVertex() + builder.vertex(matrix, tx, ty, 0).color(r, g, b, 1f).uv(u1, v1).endVertex() + } } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala index 103bfea405..ca0c53c3d6 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/StaticFontRenderer.scala @@ -1,10 +1,13 @@ package li.cil.oc.client.renderer.font import com.google.common.base.Charsets +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.RenderType import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Matrix4f import net.minecraft.util.math.vector.Vector4f @@ -50,6 +53,21 @@ class StaticFontRenderer extends TextureFontRenderer { else { Textures.bind(Textures.Font.Aliased) } + if (Settings.get.textLinearFiltering) { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR) + } + else { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) + } + } + + override protected def selectType(index: Int): RenderType = { + if (Settings.get.textAntiAlias) { + RenderTypes.createFontTex("smoothed", Textures.Font.AntiAliased, Settings.get.textLinearFiltering) + } + else { + RenderTypes.createFontTex("aliased", Textures.Font.Aliased, Settings.get.textLinearFiltering) + } } override protected def drawChar(matrix: Matrix4f, tx: Float, ty: Float, char: Char) { @@ -79,5 +97,23 @@ class StaticFontRenderer extends TextureFontRenderer { GL11.glVertex3f(vec.x, vec.y, vec.z) } + protected def drawChar(builder: IVertexBuilder, matrix: Matrix4f, color: Int, tx: Float, ty: Float, char: Char) { + val index = 1 + (chars.indexOf(char) match { + case -1 => chars.indexOf('?') + case i => i + }) + val x = (index - 1) % cols + val y = (index - 1) / cols + val u = x * uStep + val v = y * vStep + val r = ((color >> 16) & 0xFF) / 255f + val g = ((color >> 8) & 0xFF) / 255f + val b = (color & 0xFF) / 255f + builder.vertex(matrix, tx - dw, ty + charHeight * s, 0).color(r, g, b, 1f).uv(u, v + vSize).endVertex() + builder.vertex(matrix, tx + charWidth * s, ty + charHeight * s, 0).color(r, g, b, 1f).uv(u + uSize, v + vSize).endVertex() + builder.vertex(matrix, tx + charWidth * s, ty - dh, 0).color(r, g, b, 1f).uv(u + uSize, v).endVertex() + builder.vertex(matrix, tx - dw, ty - dh, 0).color(r, g, b, 1f).uv(u, v).endVertex() + } + override protected def generateChar(char: Char) {} } diff --git a/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala index fbd11495cf..47ec494452 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/TextureFontRenderer.scala @@ -2,10 +2,14 @@ package li.cil.oc.client.renderer.font import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Settings +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.util.PackedColor import li.cil.oc.util.RenderState import li.cil.oc.util.TextBuffer +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.util.math.vector.Matrix4f import net.minecraft.util.math.vector.Vector4f import org.lwjgl.opengl.GL11 @@ -35,23 +39,16 @@ abstract class TextureFontRenderer { } } - def drawBuffer(stack: MatrixStack, buffer: TextBuffer, viewportWidth: Int, viewportHeight: Int) { + def drawBuffer(stack: MatrixStack, renderBuff: IRenderTypeBuffer, buffer: TextBuffer, viewportWidth: Int, viewportHeight: Int) { val format = buffer.format stack.pushPose() - RenderState.pushAttrib() stack.scale(0.5f, 0.5f, 1) - GL11.glDepthMask(false) - RenderState.makeItBlend() - GL11.glDisable(GL11.GL_TEXTURE_2D) - - RenderState.checkError(getClass.getName + ".drawBuffer: configure state") - // Background first. We try to merge adjacent backgrounds of the same // color to reduce the number of quads we have to draw. - GL11.glBegin(GL11.GL_QUADS) + var quadBuilder: IVertexBuilder = null for (y <- 0 until (viewportHeight min buffer.height)) { val color = buffer.color(y) var cbg = 0x000000 @@ -59,67 +56,39 @@ abstract class TextureFontRenderer { var width = 0 for (col <- color.map(PackedColor.unpackBackground(_, format)) if x + width < viewportWidth) { if (col != cbg) { - drawQuad(stack.last.pose, cbg, x, y, width) + if (quadBuilder == null) quadBuilder = renderBuff.getBuffer(RenderTypes.FONT_QUAD) + drawQuad(quadBuilder, stack.last.pose, cbg, x, y, width) cbg = col x += width width = 0 } width = width + 1 } - drawQuad(stack.last.pose, cbg, x, y, width) - } - GL11.glEnd() - - RenderState.checkError(getClass.getName + ".drawBuffer: background") - - GL11.glEnable(GL11.GL_TEXTURE_2D) - - if (Settings.get.textLinearFiltering) { - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR) + drawQuad(quadBuilder, stack.last.pose, cbg, x, y, width) } - // Foreground second. We only have to flush when the color changes, so - // unless every char has a different color this should be quite efficient. - for (y <- 0 until (viewportHeight min buffer.height)) { - val line = buffer.buffer(y) - val color = buffer.color(y) - val ty = y * charHeight - for (i <- 0 until textureCount) { - bindTexture(i) - GL11.glBegin(GL11.GL_QUADS) - var cfg = -1 + // Foreground second. We only have to flush when the texture changes. + for (i <- 0 until textureCount) { + // Initialized early because our RenderCache drops empty buffers. + val fontBuilder = renderBuff.getBuffer(selectType(i)) + for (y <- 0 until (viewportHeight min buffer.height)) { + val line = buffer.buffer(y) + val color = buffer.color(y) + val ty = y * charHeight var tx = 0f for (n <- 0 until viewportWidth) { val ch = line(n) - val col = PackedColor.unpackForeground(color(n), format) - // Check if color changed. - if (col != cfg) { - cfg = col - GL11.glColor3f( - ((cfg & 0xFF0000) >> 16) / 255f, - ((cfg & 0x00FF00) >> 8) / 255f, - ((cfg & 0x0000FF) >> 0) / 255f) - } // Don't render whitespace. if (ch != ' ') { - drawChar(stack.last.pose, tx, ty, ch) + val col = PackedColor.unpackForeground(color(n), format) + drawChar(fontBuilder, stack.last.pose, col, tx, ty, ch) } tx += charWidth } - GL11.glEnd() } } - RenderState.checkError(getClass.getName + ".drawBuffer: foreground") - - RenderSystem.bindTexture(0) - GL11.glDepthMask(true) - GL11.glColor3f(1, 1, 1) - RenderState.disableBlend() - RenderState.popAttrib() stack.popPose() - - RenderState.checkError(getClass.getName + ".drawBuffer: leaving") } def drawString(stack: MatrixStack, s: String, x: Int, y: Int): Unit = { @@ -158,30 +127,25 @@ abstract class TextureFontRenderer { protected def bindTexture(index: Int): Unit + protected def selectType(index: Int): RenderType + protected def generateChar(char: Char): Unit protected def drawChar(matrix: Matrix4f, tx: Float, ty: Float, char: Char): Unit - private def drawQuad(matrix: Matrix4f, color: Int, x: Int, y: Int, width: Int) = if (color != 0 && width > 0) { + protected def drawChar(builder: IVertexBuilder, matrix: Matrix4f, color: Int, tx: Float, ty: Float, char: Char): Unit + + private def drawQuad(builder: IVertexBuilder, matrix: Matrix4f, color: Int, x: Int, y: Int, width: Int) = if (color != 0 && width > 0) { val x0 = x * charWidth val x1 = (x + width) * charWidth val y0 = y * charHeight val y1 = (y + 1) * charHeight - RenderSystem.color3f( - ((color >> 16) & 0xFF) / 255f, - ((color >> 8) & 0xFF) / 255f, - (color & 0xFF) / 255f) - val vec = new Vector4f(x0, y1, 0, 1) - vec.transform(matrix) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(x1, y1, 0, 1) - vec.transform(matrix) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(x1, y0, 0, 1) - vec.transform(matrix) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(x0, y0, 0, 1) - vec.transform(matrix) - GL11.glVertex3f(vec.x, vec.y, vec.z) + val r = ((color >> 16) & 0xFF) / 255f + val g = ((color >> 8) & 0xFF) / 255f + val b = (color & 0xFF) / 255f + builder.vertex(matrix, x0, y1, 0).color(r, g, b, 1f).endVertex() + builder.vertex(matrix, x1, y1, 0).color(r, g, b, 1f).endVertex() + builder.vertex(matrix, x1, y0, 0).color(r, g, b, 1f).endVertex() + builder.vertex(matrix, x0, y0, 0).color(r, g, b, 1f).endVertex() } } From 43e9598e62b631e2504b2b672a1f04de80285a5a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 14 Sep 2022 22:13:03 +0200 Subject: [PATCH 072/159] Fix Microcontroller TE not breaking properly --- .../scala/li/cil/oc/common/block/traits/CustomDrops.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala index c5a768bf43..3841b5c89d 100644 --- a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala +++ b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala @@ -21,7 +21,13 @@ trait CustomDrops[Tile <: TileEntity] extends SimpleBlock { override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = new util.ArrayList[ItemStack]() - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = {} + @Deprecated + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { + // Copied from vanilla, can't use super as that also drops the contents. + if (state.hasTileEntity && (!state.is(newState.getBlock) || !newState.hasTileEntity)) { + world.removeBlockEntity(pos) + } + } override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = { if (!world.isClientSide) { From 256a32e6805c8943733f9b823ab62cc6ba0cf286 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 15 Sep 2022 21:27:24 +0200 Subject: [PATCH 073/159] Fix signals sending player name as ITextComponent --- .../scala/li/cil/oc/common/component/TextBuffer.scala | 2 +- src/main/scala/li/cil/oc/common/entity/Drone.scala | 2 +- src/main/scala/li/cil/oc/common/tileentity/Screen.scala | 2 +- src/main/scala/li/cil/oc/server/component/Keyboard.scala | 8 ++++---- .../scala/li/cil/oc/server/component/MotionSensor.scala | 2 +- .../li/cil/oc/server/component/UpgradeGenerator.scala | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index e60c964fd7..56883a9c34 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -881,7 +881,7 @@ object TextBuffer { } args += Int.box(data) if (Settings.get.inputUsername) { - args += player.getName + args += player.getName.getString } owner.node.sendToReachable("computer.checked_signal", args: _*) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 263441faa8..a413b95215 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -483,7 +483,7 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, val direction = new Vector3d(entity.getX - getX, entity.getY + entity.getEyeHeight - getY, entity.getZ - getZ).normalize() if (!world.isClientSide) { if (Settings.get.inputUsername) - machine.signal("hit", Double.box(direction.x), Double.box(direction.z), Double.box(direction.y), entity.getName) + machine.signal("hit", Double.box(direction.x), Double.box(direction.z), Double.box(direction.y), entity.getName.getString) else machine.signal("hit", Double.box(direction.x), Double.box(direction.z), Double.box(direction.y)) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 9f70f51133..9c229d7992 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -173,7 +173,7 @@ class Screen(selfType: TileEntityType[_ <: Screen], var tier: Int) extends TileE case Some((oldX, oldY)) if oldX == x && oldY == y => // Ignore case _ => entity match { case player: PlayerEntity if Settings.get.inputUsername => - origin.node.sendToReachable("computer.signal", "walk", Int.box(x + 1), Int.box(height - y), player.getName) + origin.node.sendToReachable("computer.signal", "walk", Int.box(x + 1), Int.box(height - y), player.getName.getString) case _ => origin.node.sendToReachable("computer.signal", "walk", Int.box(x + 1), Int.box(height - y)) } diff --git a/src/main/scala/li/cil/oc/server/component/Keyboard.scala b/src/main/scala/li/cil/oc/server/component/Keyboard.scala index eeeb5b0148..d385cb3e9a 100644 --- a/src/main/scala/li/cil/oc/server/component/Keyboard.scala +++ b/src/main/scala/li/cil/oc/server/component/Keyboard.scala @@ -51,7 +51,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit pressedKeys.get(player) match { case Some(keys) => for ((code, char) <- keys) { if (Settings.get.inputUsername) { - signal(player, "key_up", char, code, player.getName) + signal(player, "key_up", char, code, player.getName.getString) } else { signal(player, "key_up", char, code) @@ -70,7 +70,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit if (isUsableByPlayer(p)) { pressedKeys.getOrElseUpdate(p, mutable.Map.empty[Integer, Character]) += code -> char if (Settings.get.inputUsername) { - signal(p, "key_down", char, code, p.getName) + signal(p, "key_down", char, code, p.getName.getString) } else { signal(p, "key_down", char, code) @@ -81,7 +81,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit case Some(keys) if keys.contains(code) => keys -= code if (Settings.get.inputUsername) { - signal(p, "key_up", char, code, p.getName) + signal(p, "key_up", char, code, p.getName.getString) } else { signal(p, "key_up", char, code) @@ -92,7 +92,7 @@ class Keyboard(val host: EnvironmentHost) extends AbstractManagedEnvironment wit if (isUsableByPlayer(p)) { for (line <- value.linesWithSeparators) { if (Settings.get.inputUsername) { - signal(p, "clipboard", line, p.getName) + signal(p, "clipboard", line, p.getName.getString) } else { signal(p, "clipboard", line) diff --git a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala index 927dcd500a..fce5a745bc 100644 --- a/src/main/scala/li/cil/oc/server/component/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/server/component/MotionSensor.scala @@ -118,7 +118,7 @@ class MotionSensor(val host: EnvironmentHost) extends prefab.AbstractManagedEnvi private def sendSignal(entity: LivingEntity) { if (Settings.get.inputUsername) { - node.sendToReachable("computer.signal", "motion", Double.box(entity.getX - (x + 0.5)), Double.box(entity.getY - (y + 0.5)), Double.box(entity.getZ - (z + 0.5)), entity.getName) + node.sendToReachable("computer.signal", "motion", Double.box(entity.getX - (x + 0.5)), Double.box(entity.getY - (y + 0.5)), Double.box(entity.getZ - (z + 0.5)), entity.getName.getString) } else { node.sendToReachable("computer.signal", "motion", Double.box(entity.getX - (x + 0.5)), Double.box(entity.getY - (y + 0.5)), Double.box(entity.getZ - (z + 0.5))) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index c1d11abad4..2dec5cb1b1 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -103,7 +103,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab @Callback(doc = """function():number -- Get the size of the item stack in the generator's queue.""") def count(context: Context, args: Arguments): Array[AnyRef] = { inventory match { - case SomeStack(stack) => result(stack.getCount, stack.getItem.getName(stack)) + case SomeStack(stack) => result(stack.getCount, stack.getItem.getName(stack).getString) case _ => result(0) } } From c24426ab1aa3aa943582504e41b8cfa4824a03ae Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 15 Sep 2022 22:39:13 +0200 Subject: [PATCH 074/159] Fix case contents dropping on block state change --- src/main/scala/li/cil/oc/common/block/SimpleBlock.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 4a219c338d..7b7902173d 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -155,7 +155,7 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str @Deprecated override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { - if (!world.isClientSide) world.getBlockEntity(pos) match { + if (!world.isClientSide && !newState.is(state.getBlock)) world.getBlockEntity(pos) match { case inventory: Inventory => inventory.dropAllSlots() case _ => // Ignore. } From e269289a94c555cf8e0409ce7eb6c3bf1640a6a4 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:39:35 +0200 Subject: [PATCH 075/159] Fix native Lua not working in dev environment --- libs/OpenComputers-JNLua.jar | Bin 131575 -> 133597 bytes .../server/machine/luac/LuaStateFactory.scala | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/OpenComputers-JNLua.jar b/libs/OpenComputers-JNLua.jar index f06a8f722abedbdd1b7b5b0ef421311b4e7673ab..b27363fb26456ca13a8703e7964c5eb752953207 100644 GIT binary patch delta 11187 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 Date: Fri, 16 Sep 2022 21:33:34 +0200 Subject: [PATCH 076/159] Fix loot disks having the wrong color --- src/main/scala/li/cil/oc/common/item/FloppyDisk.scala | 2 +- src/main/scala/li/cil/oc/util/Color.scala | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index 6bcb939267..ebccb184c5 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -36,7 +36,7 @@ class FloppyDisk(props: Properties = new Properties().tab(CreativeTab)) extends stack.getTag.getInt(Settings.namespace + "color") else 8 - modelLocationFromDyeName(Color.dyes(dyeIndex max 0 min 15)) + modelLocationFromDyeName(Color.byId(dyeIndex max 0 min 15)) } @OnlyIn(Dist.CLIENT) diff --git a/src/main/scala/li/cil/oc/util/Color.scala b/src/main/scala/li/cil/oc/util/Color.scala index a2ebbdf220..6cc9a47d19 100644 --- a/src/main/scala/li/cil/oc/util/Color.scala +++ b/src/main/scala/li/cil/oc/util/Color.scala @@ -25,6 +25,7 @@ object Color { DyeColor.WHITE -> 0xF0F0F0 ) + @Deprecated val dyes = Array( "dyeBlack", "dyeRed", @@ -62,6 +63,9 @@ object Color { "dyeOrange" -> DyeColor.ORANGE, "dyeWhite" -> DyeColor.WHITE) + @Deprecated + val byId = dyes.map(name => (byOreName(name).getId, name)).toMap + private val byTag = DyeColor.values.map(col => (col.getTag.getName, col)).toMap val byTier = Array(DyeColor.LIGHT_GRAY, DyeColor.YELLOW, DyeColor.CYAN, DyeColor.MAGENTA) From dec53d185e2d90b60abc9fe2abf680a0aeb056da Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 16 Sep 2022 22:07:22 +0200 Subject: [PATCH 077/159] Fix FS thread pool not getting created --- src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala index 24497d69ae..ccbd576d5c 100644 --- a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala +++ b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala @@ -9,10 +9,12 @@ import java.util.concurrent.atomic.AtomicInteger import li.cil.oc.OpenComputers import li.cil.oc.Settings +import li.cil.oc.common.SaveHandler +import li.cil.oc.server.fs.Buffered import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus -import net.minecraftforge.fml.event.server.FMLServerStartingEvent +import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent import net.minecraftforge.fml.event.server.FMLServerStoppedEvent import scala.collection.mutable @@ -26,7 +28,10 @@ object ThreadPoolFactory { } @SubscribeEvent - def serverStart(e: FMLServerStartingEvent): Unit = { + def serverStart(e: FMLServerAboutToStartEvent): Unit = { + // Access these handles to ensure the pools actually exist. + SaveHandler.stateSaveHandler + Buffered.fileSaveHandler ThreadPoolFactory.safePools.foreach(_.newThreadPool()) } From 3ff63fdaa1973e8986fdbdee89326fc3368e391a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 18 Sep 2022 00:16:34 +0200 Subject: [PATCH 078/159] Update key codes to match GLFW --- .../loot/network/data/usr/bin/nc.lua | 2 +- .../opencomputers/loot/openos/bin/edit.lua | 2 +- .../loot/openos/lib/core/full_keyboard.lua | 247 +++++++++-------- .../loot/openos/lib/keyboard.lua | 48 ++-- .../opencomputers/loot/plan9k/bin/readkey.lua | 257 +++++++++--------- .../plan9k/lib/modules/base/17_keyboard.lua | 247 +++++++++-------- 6 files changed, 411 insertions(+), 392 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/network/data/usr/bin/nc.lua b/src/main/resources/assets/opencomputers/loot/network/data/usr/bin/nc.lua index 99cbed8c88..c54e50dfb6 100644 --- a/src/main/resources/assets/opencomputers/loot/network/data/usr/bin/nc.lua +++ b/src/main/resources/assets/opencomputers/loot/network/data/usr/bin/nc.lua @@ -38,7 +38,7 @@ local function handleTcp() elseif e[2] == "message" then term.write(e[4]) end - elseif e[1] == "key_up" and channel then + elseif e[1] == "key_up" and channel and e[3] ~= 0 then network.tcp.send(channel, string.char(e[3])) term.write(string.char(e[3])) end diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua index 8ee9b2a34d..a791fb6fb4 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua @@ -628,7 +628,7 @@ local function onKeyDown(char, code) elseif not readonly then if not keyboard.isControl(char) then insert(unicode.char(char)) - elseif unicode.char(char) == "\t" then + elseif code == keyboard.keys.tab then insert(" ") end end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua index e71d409fd6..65b5d580cd 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua @@ -1,133 +1,140 @@ local keyboard = require("keyboard") -keyboard.keys["1"] = 0x02 -keyboard.keys["2"] = 0x03 -keyboard.keys["3"] = 0x04 -keyboard.keys["4"] = 0x05 -keyboard.keys["5"] = 0x06 -keyboard.keys["6"] = 0x07 -keyboard.keys["7"] = 0x08 -keyboard.keys["8"] = 0x09 -keyboard.keys["9"] = 0x0A -keyboard.keys["0"] = 0x0B -keyboard.keys.a = 0x1E -keyboard.keys.b = 0x30 -keyboard.keys.c = 0x2E -keyboard.keys.d = 0x20 -keyboard.keys.e = 0x12 -keyboard.keys.f = 0x21 -keyboard.keys.g = 0x22 -keyboard.keys.h = 0x23 -keyboard.keys.i = 0x17 -keyboard.keys.j = 0x24 -keyboard.keys.k = 0x25 -keyboard.keys.l = 0x26 -keyboard.keys.m = 0x32 -keyboard.keys.n = 0x31 -keyboard.keys.o = 0x18 -keyboard.keys.p = 0x19 -keyboard.keys.q = 0x10 -keyboard.keys.r = 0x13 -keyboard.keys.s = 0x1F -keyboard.keys.t = 0x14 -keyboard.keys.u = 0x16 -keyboard.keys.v = 0x2F -keyboard.keys.w = 0x11 -keyboard.keys.x = 0x2D -keyboard.keys.y = 0x15 -keyboard.keys.z = 0x2C +-- Keyboard codes used by GLFW +keyboard.keys["1"] = 0x31 +keyboard.keys["2"] = 0x32 +keyboard.keys["3"] = 0x33 +keyboard.keys["4"] = 0x34 +keyboard.keys["5"] = 0x35 +keyboard.keys["6"] = 0x36 +keyboard.keys["7"] = 0x37 +keyboard.keys["8"] = 0x38 +keyboard.keys["9"] = 0x39 +keyboard.keys["0"] = 0x30 +keyboard.keys.a = 0x41 +keyboard.keys.b = 0x42 +keyboard.keys.c = 0x43 +keyboard.keys.d = 0x44 +keyboard.keys.e = 0x45 +keyboard.keys.f = 0x46 +keyboard.keys.g = 0x47 +keyboard.keys.h = 0x48 +keyboard.keys.i = 0x49 +keyboard.keys.j = 0x4A +keyboard.keys.k = 0x4B +keyboard.keys.l = 0x4C +keyboard.keys.m = 0x4D +keyboard.keys.n = 0x4E +keyboard.keys.o = 0x4F +keyboard.keys.p = 0x50 +keyboard.keys.q = 0x51 +keyboard.keys.r = 0x52 +keyboard.keys.s = 0x53 +keyboard.keys.t = 0x54 +keyboard.keys.u = 0x55 +keyboard.keys.v = 0x56 +keyboard.keys.w = 0x57 +keyboard.keys.x = 0x58 +keyboard.keys.y = 0x59 +keyboard.keys.z = 0x5A -keyboard.keys.apostrophe = 0x28 -keyboard.keys.at = 0x91 -keyboard.keys.back = 0x0E -- backspace -keyboard.keys.backslash = 0x2B -keyboard.keys.capital = 0x3A -- capslock -keyboard.keys.colon = 0x92 -keyboard.keys.comma = 0x33 -keyboard.keys.enter = 0x1C -keyboard.keys.equals = 0x0D -keyboard.keys.grave = 0x29 -- accent grave -keyboard.keys.lbracket = 0x1A -keyboard.keys.lcontrol = 0x1D -keyboard.keys.lmenu = 0x38 -- left Alt -keyboard.keys.lshift = 0x2A -keyboard.keys.minus = 0x0C -keyboard.keys.numlock = 0x45 -keyboard.keys.pause = 0xC5 -keyboard.keys.period = 0x34 -keyboard.keys.rbracket = 0x1B -keyboard.keys.rcontrol = 0x9D -keyboard.keys.rmenu = 0xB8 -- right Alt -keyboard.keys.rshift = 0x36 -keyboard.keys.scroll = 0x46 -- Scroll Lock -keyboard.keys.semicolon = 0x27 -keyboard.keys.slash = 0x35 -- / on main keyboard -keyboard.keys.space = 0x39 -keyboard.keys.stop = 0x95 -keyboard.keys.tab = 0x0F -keyboard.keys.underline = 0x93 +keyboard.keys.apostrophe = 0x27 +keyboard.keys.at = -1 -- removed (GLFW) +keyboard.keys.back = 0x103 -- backspace +keyboard.keys.backslash = 0x5C +keyboard.keys.capital = 0x118 -- capslock +keyboard.keys.colon = -1 -- removed (GLFW) +keyboard.keys.comma = 0x2C +keyboard.keys.enter = 0x101 +keyboard.keys.equals = 0x3D +keyboard.keys.grave = 0x60 -- accent grave +keyboard.keys.lbracket = 0x5B +keyboard.keys.lcontrol = 0x155 +keyboard.keys.lmenu = 0x156 -- left Alt +keyboard.keys.lshift = 0x154 +keyboard.keys.minus = -1 -- removed (GLFW) +keyboard.keys.numlock = 0x11A +keyboard.keys.pause = 0x11C +keyboard.keys.period = 0x2E +keyboard.keys.rbracket = 0x5D +keyboard.keys.rcontrol = 0x159 +keyboard.keys.rmenu = 0x15A -- right Alt +keyboard.keys.rshift = 0x158 +keyboard.keys.scroll = 0x119 -- Scroll Lock +keyboard.keys.semicolon = 0x3B +keyboard.keys.slash = 0x2F -- / on main keyboard +keyboard.keys.space = 0x20 +keyboard.keys.stop = -1 -- removed (GLFW) +keyboard.keys.tab = 0x102 +keyboard.keys.underline = -1 -- removed (GLFW) -- Keypad (and numpad with numlock off) -keyboard.keys.up = 0xC8 -keyboard.keys.down = 0xD0 -keyboard.keys.left = 0xCB -keyboard.keys.right = 0xCD -keyboard.keys.home = 0xC7 -keyboard.keys["end"] = 0xCF -keyboard.keys.pageUp = 0xC9 -keyboard.keys.pageDown = 0xD1 -keyboard.keys.insert = 0xD2 -keyboard.keys.delete = 0xD3 +keyboard.keys.up = 0x109 +keyboard.keys.down = 0x108 +keyboard.keys.left = 0x107 +keyboard.keys.right = 0x106 +keyboard.keys.home = 0x10C +keyboard.keys["end"] = 0x10D +keyboard.keys.pageUp = 0x10A +keyboard.keys.pageDown = 0x10B +keyboard.keys.insert = 0x104 +keyboard.keys.delete = 0x105 -- Function keys -keyboard.keys.f1 = 0x3B -keyboard.keys.f2 = 0x3C -keyboard.keys.f3 = 0x3D -keyboard.keys.f4 = 0x3E -keyboard.keys.f5 = 0x3F -keyboard.keys.f6 = 0x40 -keyboard.keys.f7 = 0x41 -keyboard.keys.f8 = 0x42 -keyboard.keys.f9 = 0x43 -keyboard.keys.f10 = 0x44 -keyboard.keys.f11 = 0x57 -keyboard.keys.f12 = 0x58 -keyboard.keys.f13 = 0x64 -keyboard.keys.f14 = 0x65 -keyboard.keys.f15 = 0x66 -keyboard.keys.f16 = 0x67 -keyboard.keys.f17 = 0x68 -keyboard.keys.f18 = 0x69 -keyboard.keys.f19 = 0x71 +keyboard.keys.f1 = 0x122 +keyboard.keys.f2 = 0x123 +keyboard.keys.f3 = 0x124 +keyboard.keys.f4 = 0x125 +keyboard.keys.f5 = 0x126 +keyboard.keys.f6 = 0x127 +keyboard.keys.f7 = 0x128 +keyboard.keys.f8 = 0x129 +keyboard.keys.f9 = 0x12A +keyboard.keys.f10 = 0x12B +keyboard.keys.f11 = 0x12C +keyboard.keys.f12 = 0x12D +keyboard.keys.f13 = 0x12E +keyboard.keys.f14 = 0x12F +keyboard.keys.f15 = 0x130 +keyboard.keys.f16 = 0x131 +keyboard.keys.f17 = 0x132 +keyboard.keys.f18 = 0x133 +keyboard.keys.f19 = 0x134 +keyboard.keys.f20 = 0x135 +keyboard.keys.f21 = 0x136 +keyboard.keys.f22 = 0x137 +keyboard.keys.f23 = 0x138 +keyboard.keys.f24 = 0x139 +keyboard.keys.f25 = 0x13A --- Japanese keyboards -keyboard.keys.kana = 0x70 -keyboard.keys.kanji = 0x94 -keyboard.keys.convert = 0x79 -keyboard.keys.noconvert = 0x7B -keyboard.keys.yen = 0x7D -keyboard.keys.circumflex = 0x90 -keyboard.keys.ax = 0x96 +-- Japanese keyboards (removed in GLFW) +keyboard.keys.kana = -1 +keyboard.keys.kanji = -1 +keyboard.keys.convert = -1 +keyboard.keys.noconvert = -1 +keyboard.keys.yen = -1 +keyboard.keys.circumflex = -1 +keyboard.keys.ax = -1 -- Numpad -keyboard.keys.numpad0 = 0x52 -keyboard.keys.numpad1 = 0x4F -keyboard.keys.numpad2 = 0x50 -keyboard.keys.numpad3 = 0x51 -keyboard.keys.numpad4 = 0x4B -keyboard.keys.numpad5 = 0x4C -keyboard.keys.numpad6 = 0x4D -keyboard.keys.numpad7 = 0x47 -keyboard.keys.numpad8 = 0x48 -keyboard.keys.numpad9 = 0x49 -keyboard.keys.numpadmul = 0x37 -keyboard.keys.numpaddiv = 0xB5 -keyboard.keys.numpadsub = 0x4A -keyboard.keys.numpadadd = 0x4E -keyboard.keys.numpaddecimal = 0x53 -keyboard.keys.numpadcomma = 0xB3 -keyboard.keys.numpadenter = 0x9C -keyboard.keys.numpadequals = 0x8D +keyboard.keys.numpad0 = 0x140 +keyboard.keys.numpad1 = 0x141 +keyboard.keys.numpad2 = 0x142 +keyboard.keys.numpad3 = 0x143 +keyboard.keys.numpad4 = 0x144 +keyboard.keys.numpad5 = 0x145 +keyboard.keys.numpad6 = 0x146 +keyboard.keys.numpad7 = 0x147 +keyboard.keys.numpad8 = 0x148 +keyboard.keys.numpad9 = 0x149 +keyboard.keys.numpadmul = 0x14C +keyboard.keys.numpaddiv = 0x14B +keyboard.keys.numpadsub = 0x14D +keyboard.keys.numpadadd = 0x14E +keyboard.keys.numpaddecimal = 0x14A +keyboard.keys.numpadcomma = -1 -- removed (GLFW) +keyboard.keys.numpadenter = 0x14F +keyboard.keys.numpadequals = 0x150 -- Create inverse mapping for name lookup. setmetatable(keyboard.keys, diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/keyboard.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/keyboard.lua index 777d830248..5e1e7dc004 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/keyboard.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/keyboard.lua @@ -4,31 +4,29 @@ local keyboard = {pressedChars = {}, pressedCodes = {}} -- __index loads all key data from /lib/tools/keyboard_full.lua (only once) -- new key metadata should be added here if required for boot keyboard.keys = { - c = 0x2E, - d = 0x20, - q = 0x10, - w = 0x11, - back = 0x0E, -- backspace - delete = 0xD3, - down = 0xD0, - enter = 0x1C, - home = 0xC7, - lcontrol = 0x1D, - left = 0xCB, - lmenu = 0x38, -- left Alt - lshift = 0x2A, - pageDown = 0xD1, - rcontrol = 0x9D, - right = 0xCD, - rmenu = 0xB8, -- right Alt - rshift = 0x36, - space = 0x39, - tab = 0x0F, - up = 0xC8, - ["end"] = 0xCF, - enter = 0x1C, - tab = 0x0F, - numpadenter = 0x9C, + c = 0x43, + d = 0x44, + q = 0x51, + w = 0x57, + back = 0x103, -- backspace + delete = 0x105, + down = 0x108, + enter = 0x101, + home = 0x10C, + lcontrol = 0x155, + left = 0x107, + lmenu = 0x156, -- left Alt + lshift = 0x154, + pageDown = 0x10B, + rcontrol = 0x159, + right = 0x106, + rmenu = 0x15A, -- right Alt + rshift = 0x158, + space = 0x20, + tab = 0x102, + up = 0x109, + ["end"] = 0x10D, + numpadenter = 0x14F, } ------------------------------------------------------------------------------- diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua index 089efce24f..39f78ab45b 100644 --- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua +++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua @@ -10,134 +10,141 @@ end local keyboard = {pressedChars = {}, pressedCodes = {}} +-- Keyboard codes used by GLFW keyboard.keys = { - ["1"] = 0x02, - ["2"] = 0x03, - ["3"] = 0x04, - ["4"] = 0x05, - ["5"] = 0x06, - ["6"] = 0x07, - ["7"] = 0x08, - ["8"] = 0x09, - ["9"] = 0x0A, - ["0"] = 0x0B, - a = 0x1E, - b = 0x30, - c = 0x2E, - d = 0x20, - e = 0x12, - f = 0x21, - g = 0x22, - h = 0x23, - i = 0x17, - j = 0x24, - k = 0x25, - l = 0x26, - m = 0x32, - n = 0x31, - o = 0x18, - p = 0x19, - q = 0x10, - r = 0x13, - s = 0x1F, - t = 0x14, - u = 0x16, - v = 0x2F, - w = 0x11, - x = 0x2D, - y = 0x15, - z = 0x2C, - - apostrophe = 0x28, - at = 0x91, - back = 0x0E, -- backspace - backslash = 0x2B, - colon = 0x92, - comma = 0x33, - enter = 0x1C, - equals = 0x0D, - grave = 0x29, -- accent grave - lbracket = 0x1A, - lcontrol = 0x1D, - lmenu = 0x38, -- left Alt - lshift = 0x2A, - minus = 0x0C, - numlock = 0x45, - pause = 0xC5, - period = 0x34, - rbracket = 0x1B, - rcontrol = 0x9D, - rmenu = 0xB8, -- right Alt - rshift = 0x36, - scroll = 0x46, -- Scroll Lock - semicolon = 0x27, - slash = 0x35, -- / on main keyboard - space = 0x39, - stop = 0x95, - tab = 0x0F, - underline = 0x93, + ["1"] = 0x31, + ["2"] = 0x32, + ["3"] = 0x33, + ["4"] = 0x34, + ["5"] = 0x35, + ["6"] = 0x36, + ["7"] = 0x37, + ["8"] = 0x38, + ["9"] = 0x39, + ["0"] = 0x30, + a = 0x41, + b = 0x42, + c = 0x43, + d = 0x44, + e = 0x45, + f = 0x46, + g = 0x47, + h = 0x48, + i = 0x49, + j = 0x4A, + k = 0x4B, + l = 0x4C, + m = 0x4D, + n = 0x4E, + o = 0x4F, + p = 0x50, + q = 0x51, + r = 0x52, + s = 0x53, + t = 0x54, + u = 0x55, + v = 0x56, + w = 0x57, + x = 0x58, + y = 0x59, + z = 0x5A, + + apostrophe = 0x27, + at = -1, -- removed (GLFW) + back = 0x103, -- backspace + backslash = 0x5C, + colon = -1, -- removed (GLFW) + comma = 0x2C, + enter = 0x101, + equals = 0x3D, + grave = 0x60, -- accent grave + lbracket = 0x5B, + lcontrol = 0x155, + lmenu = 0x156, -- left Alt + lshift = 0x154, + minus = -1, -- removed (GLFW) + numlock = 0x11A, + pause = 0x11C, + period = 0x2E, + rbracket = 0x5D, + rcontrol = 0x159, + rmenu = 0x15A, -- right Alt + rshift = 0x158, + scroll = 0x119, -- Scroll Lock + semicolon = 0x3B, + slash = 0x2F, -- / on main keyboard + space = 0x20, + stop = -1, -- removed (GLFW) + tab = 0x102, + underline = -1, -- removed (GLFW) -- Keypad (and numpad with numlock off) - up = 0xC8, - down = 0xD0, - left = 0xCB, - right = 0xCD, - home = 0xC7, - ["end"] = 0xCF, - pageUp = 0xC9, - pageDown = 0xD1, - insert = 0xD2, - delete = 0xD3, + up = 0x109, + down = 0x108, + left = 0x107, + right = 0x106, + home = 0x10C, + ["end"] = 0x10D, + pageUp = 0x10A, + pageDown = 0x10B, + insert = 0x104, + delete = 0x105, -- Function keys - f1 = 0x3B, - f2 = 0x3C, - f3 = 0x3D, - f4 = 0x3E, - f5 = 0x3F, - f6 = 0x40, - f7 = 0x41, - f8 = 0x42, - f9 = 0x43, - f10 = 0x44, - f11 = 0x57, - f12 = 0x58, - f13 = 0x64, - f14 = 0x65, - f15 = 0x66, - f16 = 0x67, - f17 = 0x68, - f18 = 0x69, - f19 = 0x71, - - -- Japanese keyboards - kana = 0x70, - kanji = 0x94, - convert = 0x79, - noconvert = 0x7B, - yen = 0x7D, - circumflex = 0x90, - ax = 0x96, + f1 = 0x122, + f2 = 0x123, + f3 = 0x124, + f4 = 0x125, + f5 = 0x126, + f6 = 0x127, + f7 = 0x128, + f8 = 0x129, + f9 = 0x12A, + f10 = 0x12B, + f11 = 0x12C, + f12 = 0x12D, + f13 = 0x12E, + f14 = 0x12F, + f15 = 0x130, + f16 = 0x131, + f17 = 0x132, + f18 = 0x133, + f19 = 0x134, + f20 = 0x135, + f21 = 0x136, + f22 = 0x137, + f23 = 0x138, + f24 = 0x139, + f25 = 0x13A, + + -- Japanese keyboards (removed in GLFW) + kana = -1, + kanji = -1, + convert = -1, + noconvert = -1, + yen = -1, + circumflex = -1, + ax = -1, -- Numpad - numpad0 = 0x52, - numpad1 = 0x4F, - numpad2 = 0x50, - numpad3 = 0x51, - numpad4 = 0x4B, - numpad5 = 0x4C, - numpad6 = 0x4D, - numpad7 = 0x47, - numpad8 = 0x48, - numpad9 = 0x49, - numpadmul = 0x37, - numpaddiv = 0xB5, - numpadsub = 0x4A, - numpadadd = 0x4E, - numpaddecimal = 0x53, - numpadcomma = 0xB3, - numpadenter = 0x9C, - numpadequals = 0x8D, + numpad0 = 0x140, + numpad1 = 0x141, + numpad2 = 0x142, + numpad3 = 0x143, + numpad4 = 0x144, + numpad5 = 0x145, + numpad6 = 0x146, + numpad7 = 0x147, + numpad8 = 0x148, + numpad9 = 0x149, + numpadmul = 0x14C, + numpaddiv = 0x14B, + numpadsub = 0x14D, + numpadadd = 0x14E, + numpaddecimal = 0x14A, + numpadcomma = -1, -- removed (GLFW) + numpadenter = 0x14F, + numpadequals = 0x150, } do @@ -165,10 +172,10 @@ function on.key_down(_, source, ascii, keycode, user) if ascii ~= 0 and ascii ~= 127 then io.stdout:write(unicode.char(ascii)) else - if keycode == 200 then io.stdout:write("\x1b[A") - elseif keycode == 208 then io.stdout:write("\x1b[B") - elseif keycode == 205 then io.stdout:write("\x1b[C") - elseif keycode == 203 then io.stdout:write("\x1b[D") + if keycode == keyboard.up then io.stdout:write("\x1b[A") + elseif keycode == keyboard.down then io.stdout:write("\x1b[B") + elseif keycode == keyboard.right then io.stdout:write("\x1b[C") + elseif keycode == keyboard.left then io.stdout:write("\x1b[D") elseif keycode == keyboard.keys.f1 then io.stdout:write("\x1bOP") elseif keycode == keyboard.keys.f2 then io.stdout:write("\x1bOQ") diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_keyboard.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_keyboard.lua index d07d9ed903..3b9c5cf2ac 100644 --- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_keyboard.lua +++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_keyboard.lua @@ -1,133 +1,140 @@ local keyboard = {pressedChars = {}, pressedCodes = {}} +-- Keyboard codes used by GLFW keyboard.keys = { - ["1"] = 0x02, - ["2"] = 0x03, - ["3"] = 0x04, - ["4"] = 0x05, - ["5"] = 0x06, - ["6"] = 0x07, - ["7"] = 0x08, - ["8"] = 0x09, - ["9"] = 0x0A, - ["0"] = 0x0B, + ["1"] = 0x31, + ["2"] = 0x32, + ["3"] = 0x33, + ["4"] = 0x34, + ["5"] = 0x35, + ["6"] = 0x36, + ["7"] = 0x37, + ["8"] = 0x38, + ["9"] = 0x39, + ["0"] = 0x30, a = 0x1E, - b = 0x30, - c = 0x2E, - d = 0x20, - e = 0x12, - f = 0x21, - g = 0x22, - h = 0x23, - i = 0x17, - j = 0x24, - k = 0x25, - l = 0x26, - m = 0x32, - n = 0x31, - o = 0x18, - p = 0x19, - q = 0x10, - r = 0x13, - s = 0x1F, - t = 0x14, - u = 0x16, - v = 0x2F, - w = 0x11, - x = 0x2D, - y = 0x15, - z = 0x2C, - - apostrophe = 0x28, - at = 0x91, - back = 0x0E, -- backspace - backslash = 0x2B, - colon = 0x92, - comma = 0x33, - enter = 0x1C, - equals = 0x0D, - grave = 0x29, -- accent grave - lbracket = 0x1A, - lcontrol = 0x1D, - lmenu = 0x38, -- left Alt - lshift = 0x2A, - minus = 0x0C, - numlock = 0x45, - pause = 0xC5, - period = 0x34, - rbracket = 0x1B, - rcontrol = 0x9D, - rmenu = 0xB8, -- right Alt - rshift = 0x36, - scroll = 0x46, -- Scroll Lock - semicolon = 0x27, - slash = 0x35, -- / on main keyboard - space = 0x39, - stop = 0x95, - tab = 0x0F, - underline = 0x93, + b = 0x41, + c = 0x42, + d = 0x43, + e = 0x44, + f = 0x45, + g = 0x46, + h = 0x47, + i = 0x48, + j = 0x49, + k = 0x4A, + l = 0x4B, + m = 0x4C, + n = 0x4D, + o = 0x4E, + p = 0x4F, + q = 0x50, + r = 0x51, + s = 0x52, + t = 0x53, + u = 0x54, + v = 0x55, + w = 0x56, + x = 0x57, + y = 0x58, + z = 0x59, + + apostrophe = 0x27, + at = -1, -- removed (GLFW) + back = 0x103, -- backspace + backslash = 0x5C, + colon = -1, -- removed (GLFW) + comma = 0x2C, + enter = 0x101, + equals = 0x3D, + grave = 0x60, -- accent grave + lbracket = 0x5B, + lcontrol = 0x155, + lmenu = 0x156, -- left Alt + lshift = 0x154, + minus = -1, -- removed (GLFW) + numlock = 0x11A, + pause = 0x11C, + period = 0x2E, + rbracket = 0x5D, + rcontrol = 0x159, + rmenu = 0x15A, -- right Alt + rshift = 0x158, + scroll = 0x119, -- Scroll Lock + semicolon = 0x3B, + slash = 0x2F, -- / on main keyboard + space = 0x20, + stop = -1, -- removed (GLFW) + tab = 0x102, + underline = -1, -- removed (GLFW) -- Keypad (and numpad with numlock off) - up = 0xC8, - down = 0xD0, - left = 0xCB, - right = 0xCD, - home = 0xC7, - ["end"] = 0xCF, - pageUp = 0xC9, - pageDown = 0xD1, - insert = 0xD2, - delete = 0xD3, + up = 0x109, + down = 0x108, + left = 0x107, + right = 0x106, + home = 0x10C, + ["end"] = 0x10D, + pageUp = 0x10A, + pageDown = 0x10B, + insert = 0x104, + delete = 0x105, -- Function keys - f1 = 0x3B, - f2 = 0x3C, - f3 = 0x3D, - f4 = 0x3E, - f5 = 0x3F, - f6 = 0x40, - f7 = 0x41, - f8 = 0x42, - f9 = 0x43, - f10 = 0x44, - f11 = 0x57, - f12 = 0x58, - f13 = 0x64, - f14 = 0x65, - f15 = 0x66, - f16 = 0x67, - f17 = 0x68, - f18 = 0x69, - f19 = 0x71, - - -- Japanese keyboards - kana = 0x70, - kanji = 0x94, - convert = 0x79, - noconvert = 0x7B, - yen = 0x7D, - circumflex = 0x90, - ax = 0x96, + f1 = 0x122, + f2 = 0x123, + f3 = 0x124, + f4 = 0x125, + f5 = 0x126, + f6 = 0x127, + f7 = 0x128, + f8 = 0x129, + f9 = 0x12A, + f10 = 0x12B, + f11 = 0x12C, + f12 = 0x12D, + f13 = 0x12E, + f14 = 0x12F, + f15 = 0x130, + f16 = 0x131, + f17 = 0x132, + f18 = 0x133, + f19 = 0x134, + f20 = 0x135, + f21 = 0x136, + f22 = 0x137, + f23 = 0x138, + f24 = 0x139, + f25 = 0x13A, + + -- Japanese keyboards (removed in GLFW) + kana = -1, + kanji = -1, + convert = -1, + noconvert = -1, + yen = -1, + circumflex = -1, + ax = -1, -- Numpad - numpad0 = 0x52, - numpad1 = 0x4F, - numpad2 = 0x50, - numpad3 = 0x51, - numpad4 = 0x4B, - numpad5 = 0x4C, - numpad6 = 0x4D, - numpad7 = 0x47, - numpad8 = 0x48, - numpad9 = 0x49, - numpadmul = 0x37, - numpaddiv = 0xB5, - numpadsub = 0x4A, - numpadadd = 0x4E, - numpaddecimal = 0x53, - numpadcomma = 0xB3, - numpadenter = 0x9C, - numpadequals = 0x8D, + numpad0 = 0x140, + numpad1 = 0x141, + numpad2 = 0x142, + numpad3 = 0x143, + numpad4 = 0x144, + numpad5 = 0x145, + numpad6 = 0x146, + numpad7 = 0x147, + numpad8 = 0x148, + numpad9 = 0x149, + numpadmul = 0x14C, + numpaddiv = 0x14B, + numpadsub = 0x14D, + numpadadd = 0x14E, + numpaddecimal = 0x14A, + numpadcomma = -1, -- removed (GLFW) + numpadenter = 0x14F, + numpadequals = 0x150, } -- Create inverse mapping for name lookup. From 792999bfe102c5c8ff1d1a6e405a3a857a3b8aa6 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 20 Sep 2022 17:45:40 +0200 Subject: [PATCH 079/159] Fix robots cannot be turned on --- src/main/scala/li/cil/oc/server/PacketHandler.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index 06132c61c0..aa0e52de63 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -71,6 +71,12 @@ object PacketHandler extends CommonPacketHandler { case _ => } } + case robot: container.Robot if robot.containerId == containerId => { + (robot.otherInventory, p.player) match { + case (te: Computer, player: ServerPlayerEntity) => trySetComputerPower(te.machine, setPower, player) + case _ => + } + } case _ => // Invalid packet or container closed early. } } From 3b11f3b7a029016e70623c86ce80a3bf52a2b24c Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 20 Sep 2022 20:08:50 +0200 Subject: [PATCH 080/159] Fix robot movement causing invalid TE --- src/main/scala/li/cil/oc/common/block/RobotProxy.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 833c127933..9d015a03d3 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -257,7 +257,11 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 super.removedByPlayer(state, world, pos, player, willHarvest, fluid) } - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = + override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { if (moving.get.isEmpty) super.onRemove(state, world, pos, newState, moved) + else if (!state.is(newState.getBlock)) + // Can't use super.onRemove as that would drop inventory while moving. + world.removeBlockEntity(pos) + } } From d09a23279cbf4bac8d6eaf35377caae4b0f0dbe9 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:38:00 +0200 Subject: [PATCH 081/159] Fix robot container item placement rules --- .../oc/common/container/ComponentSlot.scala | 3 +++ .../li/cil/oc/common/container/Robot.scala | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index 6c6922eb12..8e7aaa12ea 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -38,6 +38,9 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) override def mayPlace(stack: ItemStack): Boolean = { if (!inventory.canPlaceItem(getSlotIndex, stack)) return false if (!isActive) return false + if (slot == common.Slot.Any && tier == common.Tier.Any) return true + // Special case: tool slots fit everything. + if (slot == common.Slot.Tool) return true for (driver <- Driver.itemDrivers) { if (driver.worksWith(stack)) { val slotOk = (slot == common.Slot.Any || driver.slot(stack) == slot) diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index e8da264928..7feba34f99 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -5,7 +5,8 @@ import li.cil.oc.client.Textures import li.cil.oc.common import li.cil.oc.common.ComponentTracker import li.cil.oc.common.tileentity -import li.cil.oc.integration.opencomputers +import li.cil.oc.integration.opencomputers.DriverKeyboard +import li.cil.oc.integration.opencomputers.DriverScreen import li.cil.oc.util.SideTracker import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory @@ -23,7 +24,7 @@ object RobotInfo { case Some(buffer: api.internal.TextBuffer) if buffer.node != null => buffer.node.address } - def hasKeyboard(robot: tileentity.Robot) = robot.info.components.map(api.Driver.driverFor(_, robot.getClass)).contains(opencomputers.DriverKeyboard) + def hasKeyboard(robot: tileentity.Robot) = robot.info.components.map(api.Driver.driverFor(_, robot.getClass)).contains(DriverKeyboard) def readRobotInfo(buff: PacketBuffer): RobotInfo = { val mainInvSize = buff.readVarInt() @@ -77,9 +78,21 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe val deltaY: Int = if (info.screenBuffer.isDefined) 0 else withScreenHeight - noScreenHeight addSlotToContainer(170 + 0 * slotSize, 232 - deltaY, common.Slot.Tool) - addSlotToContainer(170 + 1 * slotSize, 232 - deltaY, info.slot1, info.tier1) - addSlotToContainer(170 + 2 * slotSize, 232 - deltaY, info.slot2, info.tier2) - addSlotToContainer(170 + 3 * slotSize, 232 - deltaY, info.slot3, info.tier3) + addSpecialSlot(170 + 1 * slotSize, 232 - deltaY, info.slot1, info.tier1) + addSpecialSlot(170 + 2 * slotSize, 232 - deltaY, info.slot2, info.tier2) + addSpecialSlot(170 + 3 * slotSize, 232 - deltaY, info.slot3, info.tier3) + + // Like addSlotToContainer, but handles the very special, much edge case with screen & keyboard. + def addSpecialSlot(x: Int, y: Int, slot: String, tier: Int) { + val index = slots.size + addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier) { + override def mayPlace(stack: ItemStack): Boolean = { + if (DriverScreen.worksWith(stack, classOf[tileentity.Robot])) return false + if (DriverKeyboard.worksWith(stack, classOf[tileentity.Robot])) return false + super.mayPlace(stack) + } + }) + } // Slot.x and Slot.y are final, so have to rebuild when scrolling def generateSlotsFor(scroll: Int) { @@ -90,6 +103,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe val x = 170 + j * slotSize val idx = 4 + j + 4 * i val slot = new InventorySlot(this, otherInventory, idx, x, y, i >= scroll && i < scroll + 4) + slot.index = idx if (slots.size() <= idx) addSlot(slot) else slots.set(idx, slot) } From 2e58bf62e5db0519eed45eef1a19c4815da1fd04 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:43:12 +0200 Subject: [PATCH 082/159] Fix robot container not being scrollable --- src/main/scala/li/cil/oc/client/gui/Robot.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index bb09f38721..f1a1ec86b3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -199,8 +199,8 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex } override def mouseScrolled(mouseX: Double, mouseY: Double, scroll: Double): Boolean = { - val mx = mouseX.asInstanceOf[Int] - val my = mouseY.asInstanceOf[Int] + val mx = mouseX.asInstanceOf[Int] - leftPos + val my = mouseY.asInstanceOf[Int] - topPos if (isCoordinateOverInventory(mx, my) || isCoordinateOverScrollBar(mx, my)) { if (scroll < 0) scrollDown() else scrollUp() From e41ef495b5fc746e594e419539ecb726a07c75da Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 21 Sep 2022 00:34:22 +0200 Subject: [PATCH 083/159] Make container slots host-aware --- .../scala/li/cil/oc/common/container/Adapter.scala | 13 +++---------- .../li/cil/oc/common/container/Assembler.scala | 6 ++++-- .../scala/li/cil/oc/common/container/Case.scala | 2 ++ .../scala/li/cil/oc/common/container/Charger.scala | 5 ++++- .../li/cil/oc/common/container/ComponentSlot.scala | 12 ++++++------ .../scala/li/cil/oc/common/container/Database.scala | 2 ++ .../li/cil/oc/common/container/Disassembler.scala | 4 +++- .../li/cil/oc/common/container/DiskDrive.scala | 3 +++ .../scala/li/cil/oc/common/container/Drone.scala | 6 +++++- .../oc/common/container/DynamicComponentSlot.scala | 6 +++++- .../li/cil/oc/common/container/InternalSlot.scala | 5 ----- .../scala/li/cil/oc/common/container/Player.scala | 10 +++++++--- .../scala/li/cil/oc/common/container/Printer.scala | 6 ++++-- .../scala/li/cil/oc/common/container/Rack.scala | 2 ++ .../scala/li/cil/oc/common/container/Raid.scala | 2 ++ .../scala/li/cil/oc/common/container/Relay.scala | 4 +++- .../scala/li/cil/oc/common/container/Robot.scala | 10 ++++++---- .../scala/li/cil/oc/common/container/Server.scala | 2 ++ .../oc/common/container/StaticComponentSlot.scala | 5 ++++- .../scala/li/cil/oc/common/container/Tablet.scala | 10 +++++++++- 20 files changed, 76 insertions(+), 39 deletions(-) delete mode 100644 src/main/scala/li/cil/oc/common/container/InternalSlot.scala diff --git a/src/main/scala/li/cil/oc/common/container/Adapter.scala b/src/main/scala/li/cil/oc/common/container/Adapter.scala index 99bd34b33f..153fe27569 100644 --- a/src/main/scala/li/cil/oc/common/container/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/container/Adapter.scala @@ -12,15 +12,8 @@ import net.minecraft.inventory.container.ContainerType class Adapter(selfType: ContainerType[_ <: Adapter], id: Int, playerInventory: PlayerInventory, adapter: IInventory) extends Player(selfType, id, playerInventory, adapter) { - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, Slot.Upgrade, Tier.Any) { - override def mayPlace(stack: ItemStack): Boolean = { - if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false - Option(Driver.driverFor(stack, classOf[tileentity.Adapter])) match { - case Some(driver) => driver.slot(stack) == Slot.Upgrade - case _ => false - } - } - }) + override protected def getHostClass = classOf[tileentity.Adapter] + + addSlotToContainer(80, 35, Slot.Upgrade) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index 868eb2d99c..3a44df63ed 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -16,10 +16,12 @@ import net.minecraftforge.api.distmarker.OnlyIn class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventory: PlayerInventory, val assembler: IInventory) extends Player(selfType, id, playerInventory, assembler) { + override protected def getHostClass = classOf[tileentity.Assembler] + // Computer case. { val index = slots.size - addSlot(new StaticComponentSlot(this, otherInventory, index, 12, 12, "template", common.Tier.Any) { + addSlot(new StaticComponentSlot(this, otherInventory, index, 12, 12, getHostClass, "template", common.Tier.Any) { @OnlyIn(Dist.CLIENT) override def isActive = !isAssembling && super.isActive @@ -49,7 +51,7 @@ class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventor override def addSlotToContainer(x: Int, y: Int, info: DynamicComponentSlot => InventorySlot) { val index = slots.size - addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => common.Tier.One) { + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, getHostClass, info, () => common.Tier.One) { override def mayPlace(stack: ItemStack): Boolean = { if (!super.mayPlace(stack)) return false AssemblerTemplates.select(getSlot(0).getItem) match { diff --git a/src/main/scala/li/cil/oc/common/container/Case.scala b/src/main/scala/li/cil/oc/common/container/Case.scala index da006105c1..e6b91eda97 100644 --- a/src/main/scala/li/cil/oc/common/container/Case.scala +++ b/src/main/scala/li/cil/oc/common/container/Case.scala @@ -13,6 +13,8 @@ import net.minecraft.util.text.ITextComponent class Case(selfType: ContainerType[_ <: Case], id: Int, playerInventory: PlayerInventory, computer: IInventory, tier: Int) extends Player(selfType, id, playerInventory, computer) { + override protected def getHostClass = classOf[tileentity.Case] + for (i <- 0 to (if (tier >= Tier.Three) 2 else 1)) { val slot = InventorySlots.computer(tier)(getItems.size) addSlotToContainer(98, 16 + i * slotSize, slot.slot, slot.tier) diff --git a/src/main/scala/li/cil/oc/common/container/Charger.scala b/src/main/scala/li/cil/oc/common/container/Charger.scala index 0a1320b868..ee4f997dff 100644 --- a/src/main/scala/li/cil/oc/common/container/Charger.scala +++ b/src/main/scala/li/cil/oc/common/container/Charger.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.container import li.cil.oc.common.Tier +import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemCharge import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack @@ -10,7 +11,9 @@ import net.minecraft.inventory.container.ContainerType class Charger(selfType: ContainerType[_ <: Charger], id: Int, playerInventory: PlayerInventory, charger: IInventory) extends Player(selfType, id, playerInventory, charger) { - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, "tablet", Tier.Any) { + override protected def getHostClass = classOf[tileentity.Charger] + + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, getHostClass, "tablet", Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false if (!isActive) return false diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index 8e7aaa12ea..c3b03d5d77 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -1,7 +1,7 @@ package li.cil.oc.common.container import li.cil.oc.api.Driver -import li.cil.oc.api.internal +import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory @@ -13,7 +13,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToScala._ -abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) extends Slot(inventory, index, x, y) { +abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int, host: Class[_ <: EnvironmentHost]) extends Slot(inventory, index, x, y) { def agentContainer: Player def slot: String @@ -41,14 +41,14 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int) if (slot == common.Slot.Any && tier == common.Tier.Any) return true // Special case: tool slots fit everything. if (slot == common.Slot.Tool) return true - for (driver <- Driver.itemDrivers) { - if (driver.worksWith(stack)) { + Option(Driver.driverFor(stack, host)) match { + case Some(driver) => { val slotOk = (slot == common.Slot.Any || driver.slot(stack) == slot) val tierOk = (tier == common.Tier.Any || driver.tier(stack) <= tier) - if (slotOk && tierOk) return true + slotOk && tierOk } + case _ => false } - false } override def onTake(player: PlayerEntity, stack: ItemStack) = { diff --git a/src/main/scala/li/cil/oc/common/container/Database.scala b/src/main/scala/li/cil/oc/common/container/Database.scala index e7d7bd24ef..1c115fc0cc 100644 --- a/src/main/scala/li/cil/oc/common/container/Database.scala +++ b/src/main/scala/li/cil/oc/common/container/Database.scala @@ -12,6 +12,8 @@ import net.minecraft.item.ItemStack class Database(selfType: ContainerType[_ <: Database], id: Int, playerInventory: PlayerInventory, val container: ItemStack, databaseInventory: IInventory, val tier: Int) extends Player(selfType, id, playerInventory, databaseInventory) { + override protected def getHostClass = null + val rows = math.sqrt(databaseInventory.getContainerSize).ceil.toInt val offset = 8 + Array(3, 2, 0)(tier) * slotSize diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index 6eadd09ef4..e4f64d8ca7 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -18,7 +18,9 @@ class Disassembler(selfType: ContainerType[_ <: Disassembler], id: Int, playerIn private def allowDisassembling(stack: ItemStack) = !stack.isEmpty && (!stack.hasTag || !stack.getTag.getBoolean(Settings.namespace + "undisassemblable")) - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, "ocitem", Tier.Any) { + override protected def getHostClass = classOf[tileentity.Disassembler] + + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, getHostClass, "ocitem", Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false if (!isActive) return false diff --git a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala index 8b8015e01d..9dedf16e12 100644 --- a/src/main/scala/li/cil/oc/common/container/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/container/DiskDrive.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.container import li.cil.oc.common.Slot +import li.cil.oc.common.tileentity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory import net.minecraft.inventory.container.ContainerType @@ -8,6 +9,8 @@ import net.minecraft.inventory.container.ContainerType class DiskDrive(selfType: ContainerType[_ <: DiskDrive], id: Int, playerInventory: PlayerInventory, drive: IInventory) extends Player(selfType, id, playerInventory, drive) { + override protected def getHostClass = classOf[tileentity.DiskDrive] + addSlotToContainer(80, 35, Slot.Floppy) addPlayerInventorySlots(8, 84) } diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index bd4f77fcd1..8f80c74a32 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -17,6 +17,8 @@ class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: Playe val deltaY = 0 + override protected def getHostClass = classOf[entity.Drone] + for (i <- 0 to 1) { val y = 8 + i * slotSize - deltaY for (j <- 0 to 3) { @@ -90,7 +92,9 @@ class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: Playe super.detectCustomDataChanges(nbt) } - class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { + class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) + extends StaticComponentSlot(container, inventory, index, x, y, getHostClass, common.Slot.Any, common.Tier.Any) { + def isValid = (0 until droneInv.getContainerSize).contains(getSlotIndex) @OnlyIn(Dist.CLIENT) override diff --git a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala index 7438e51c53..2f80b6f4e0 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.container +import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.client.Textures import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot @@ -10,7 +11,10 @@ import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation -class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) extends ComponentSlot(inventory, index, x, y) { +class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, host: Class[_ <: EnvironmentHost], + val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) + extends ComponentSlot(inventory, index, x, y, host) { + override def tier: Int = { val mainTier = containerTierGetter() if (mainTier >= 0) info(this).tier diff --git a/src/main/scala/li/cil/oc/common/container/InternalSlot.scala b/src/main/scala/li/cil/oc/common/container/InternalSlot.scala deleted file mode 100644 index 43156ecdcf..0000000000 --- a/src/main/scala/li/cil/oc/common/container/InternalSlot.scala +++ /dev/null @@ -1,5 +0,0 @@ -package li.cil.oc.common.container - -object InternalSlot extends Enumeration { - val Server = Value -} diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index cdcbbc64f7..8831c9b605 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.container +import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.Tier @@ -121,19 +122,22 @@ abstract class Player(selfType: ContainerType[_ <: Player], id: Int, val playerI } } + // Used by the ComponentSlots to make host-aware item placement decisions. + protected def getHostClass: Class[_ <: EnvironmentHost] + def addSlotToContainer(x: Int, y: Int, slot: String = common.Slot.Any, tier: Int = common.Tier.Any) { val index = slots.size - addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier)) + addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, getHostClass, slot, tier)) } def addSlotToContainer(x: Int, y: Int, info: Array[Array[InventorySlot]], containerTierGetter: () => Int) { val index = slots.size - addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, slot => info(slot.containerTierGetter())(slot.getSlotIndex), containerTierGetter)) + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, getHostClass, slot => info(slot.containerTierGetter())(slot.getSlotIndex), containerTierGetter)) } def addSlotToContainer(x: Int, y: Int, info: DynamicComponentSlot => InventorySlot) { val index = slots.size - addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, info, () => Tier.One)) + addSlot(new DynamicComponentSlot(this, otherInventory, index, x, y, getHostClass, info, () => Tier.One)) } /** Render player inventory at the specified coordinates. */ diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index e65c79b80c..57617c976c 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -13,14 +13,16 @@ import net.minecraft.nbt.CompoundNBT class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: PlayerInventory, val printer: IInventory) extends Player(selfType, id, playerInventory, printer) { - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 19, Slot.Filtered, Tier.Any) { + override protected def getHostClass = classOf[tileentity.Printer] + + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 19, getHostClass, Slot.Filtered, Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false if (!isActive) return false PrintData.materialValue(stack) > 0 } }) - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 51, Slot.Filtered, Tier.Any) { + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 51, getHostClass, Slot.Filtered, Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false if (!isActive) return false diff --git a/src/main/scala/li/cil/oc/common/container/Rack.scala b/src/main/scala/li/cil/oc/common/container/Rack.scala index 67abc9142d..a8c470d03d 100644 --- a/src/main/scala/li/cil/oc/common/container/Rack.scala +++ b/src/main/scala/li/cil/oc/common/container/Rack.scala @@ -16,6 +16,8 @@ import net.minecraftforge.common.util.Constants.NBT class Rack(selfType: ContainerType[_ <: Rack], id: Int, playerInventory: PlayerInventory, val rack: IInventory) extends Player(selfType, id, playerInventory, rack) { + override protected def getHostClass = classOf[tileentity.Rack] + addSlotToContainer(20, 23, Slot.RackMountable) addSlotToContainer(20, 43, Slot.RackMountable) addSlotToContainer(20, 63, Slot.RackMountable) diff --git a/src/main/scala/li/cil/oc/common/container/Raid.scala b/src/main/scala/li/cil/oc/common/container/Raid.scala index 46fc89348a..06e356f71d 100644 --- a/src/main/scala/li/cil/oc/common/container/Raid.scala +++ b/src/main/scala/li/cil/oc/common/container/Raid.scala @@ -10,6 +10,8 @@ import net.minecraft.inventory.container.ContainerType class Raid(selfType: ContainerType[_ <: Raid], id: Int, playerInventory: PlayerInventory, raid: IInventory) extends Player(selfType, id, playerInventory, raid) { + override protected def getHostClass = classOf[tileentity.Raid] + addSlotToContainer(60, 23, Slot.HDD, Tier.Three) addSlotToContainer(80, 23, Slot.HDD, Tier.Three) addSlotToContainer(100, 23, Slot.HDD, Tier.Three) diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala index 40c0118ee5..277718d2dd 100644 --- a/src/main/scala/li/cil/oc/common/container/Relay.scala +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -19,10 +19,12 @@ class Relay(selfType: ContainerType[_ <: Relay], id: Int, playerInventory: Playe lazy final val WirelessNetworkCardTier2: ItemInfo = api.Items.get(Constants.ItemName.WirelessNetworkCardTier2) lazy final val LinkedCard: ItemInfo = api.Items.get(Constants.ItemName.LinkedCard) + override protected def getHostClass = classOf[tileentity.Relay] + addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) addSlotToContainer(151, 53, Slot.HDD) - addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 178, 15, Slot.Card, Tier.Any) { + addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 178, 15, getHostClass, Slot.Card, Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (api.Items.get(stack) != WirelessNetworkCardTier1 && api.Items.get(stack) != WirelessNetworkCardTier2 && api.Items.get(stack) != LinkedCard) return false diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 7feba34f99..10df54508f 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -77,6 +77,8 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe private val noScreenHeight = 108 val deltaY: Int = if (info.screenBuffer.isDefined) 0 else withScreenHeight - noScreenHeight + override protected def getHostClass = classOf[tileentity.Robot] + addSlotToContainer(170 + 0 * slotSize, 232 - deltaY, common.Slot.Tool) addSpecialSlot(170 + 1 * slotSize, 232 - deltaY, info.slot1, info.tier1) addSpecialSlot(170 + 2 * slotSize, 232 - deltaY, info.slot2, info.tier2) @@ -85,10 +87,10 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe // Like addSlotToContainer, but handles the very special, much edge case with screen & keyboard. def addSpecialSlot(x: Int, y: Int, slot: String, tier: Int) { val index = slots.size - addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, slot, tier) { + addSlot(new StaticComponentSlot(this, otherInventory, index, x, y, getHostClass, slot, tier) { override def mayPlace(stack: ItemStack): Boolean = { - if (DriverScreen.worksWith(stack, classOf[tileentity.Robot])) return false - if (DriverKeyboard.worksWith(stack, classOf[tileentity.Robot])) return false + if (DriverScreen.worksWith(stack, getHostClass)) return false + if (DriverKeyboard.worksWith(stack, getHostClass)) return false super.mayPlace(stack) } }) @@ -167,7 +169,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe def selectedSlot = selectedSlotData.get class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, var enabled: Boolean) - extends StaticComponentSlot(container, inventory, index, x, y, common.Slot.Any, common.Tier.Any) { + extends StaticComponentSlot(container, inventory, index, x, y, getHostClass, common.Slot.Any, common.Tier.Any) { def isValid: Boolean = getSlotIndex >= 4 && getSlotIndex < 4 + info.mainInvSize diff --git a/src/main/scala/li/cil/oc/common/container/Server.scala b/src/main/scala/li/cil/oc/common/container/Server.scala index 6a1b6b4042..27d2826e35 100644 --- a/src/main/scala/li/cil/oc/common/container/Server.scala +++ b/src/main/scala/li/cil/oc/common/container/Server.scala @@ -13,6 +13,8 @@ import net.minecraft.nbt.CompoundNBT class Server(selfType: ContainerType[_ <: Server], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, serverInventory: IInventory, tier: Int, val rackSlot: Int) extends Player(selfType, id, playerInventory, serverInventory) { + override protected def getHostClass = classOf[component.Server] + for (i <- 0 to 1) { val slot = InventorySlots.server(tier)(slots.size) addSlotToContainer(76, 7 + i * slotSize, slot.slot, slot.tier) diff --git a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala index ed32c3d708..46a64e14ca 100644 --- a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.container +import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.client.Textures import li.cil.oc.common import net.minecraft.inventory.IInventory @@ -7,7 +8,9 @@ import net.minecraft.util.ResourceLocation import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class StaticComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, val slot: String, val tier: Int) extends ComponentSlot(inventory, index, x, y) { +class StaticComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, host: Class[_ <: EnvironmentHost], val slot: String, val tier: Int) + extends ComponentSlot(inventory, index, x, y, host) { + val tierIcon = Textures.Icons.get(tier) @OnlyIn(Dist.CLIENT) diff --git a/src/main/scala/li/cil/oc/common/container/Tablet.scala b/src/main/scala/li/cil/oc/common/container/Tablet.scala index cb865da578..f00ff4ff80 100644 --- a/src/main/scala/li/cil/oc/common/container/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/container/Tablet.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper +import li.cil.oc.integration.opencomputers.DriverScreen import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.IInventory @@ -10,7 +11,14 @@ import net.minecraft.item.ItemStack class Tablet(selfType: ContainerType[_ <: Tablet], id: Int, playerInventory: PlayerInventory, val stack: ItemStack, tablet: IInventory, slot1: String, tier1: Int) extends Player(selfType, id, playerInventory, tablet) { - addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, slot1, tier1)) + override protected def getHostClass = classOf[TabletWrapper] + + addSlot(new StaticComponentSlot(this, otherInventory, otherInventory.getContainerSize - 1, 80, 35, getHostClass, slot1, tier1) { + override def mayPlace(stack: ItemStack): Boolean = { + if (DriverScreen.worksWith(stack, getHostClass)) return false + super.mayPlace(stack) + } + }) addPlayerInventorySlots(8, 84) From f1f9f9bd7390d6e18f672ed24e578b5334f27e43 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 21 Sep 2022 00:45:46 +0200 Subject: [PATCH 084/159] Fix robot GUI item highlighting --- src/main/scala/li/cil/oc/client/gui/Robot.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index f1a1ec86b3..a50b5cbd76 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -130,9 +130,14 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex } } - override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) = + override protected def renderLabels(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawSecondaryForegroundLayer(stack, mouseX, mouseY) + for (slot <- 0 until menu.slots.size()) { + drawSlotHighlight(stack, menu.getSlot(slot)) + } + } + override protected def drawSecondaryForegroundLayer(stack: MatrixStack, mouseX: Int, mouseY: Int) { drawBufferLayer(stack) if (isPointInRegion(power.x, power.y, power.width, power.height, mouseX - leftPos, mouseY - topPos)) { From 11a9c33d43c1598ada059f435c274900be10d7c2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 22 Sep 2022 00:22:14 +0200 Subject: [PATCH 085/159] Fix quadcopter light rendering --- .../renderer/entity/ModelQuadcopter.scala | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala b/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala index b65edc11cb..434d645e32 100644 --- a/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala +++ b/src/main/scala/li/cil/oc/client/renderer/entity/ModelQuadcopter.scala @@ -1,16 +1,14 @@ package li.cil.oc.client.renderer.entity import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.common.entity.Drone -import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.LightTexture import net.minecraft.client.renderer.entity.model.EntityModel import net.minecraft.client.renderer.model.ModelRenderer -import net.minecraft.entity.Entity +import net.minecraft.client.renderer.texture.OverlayTexture import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.math.vector.Vector3f -import org.lwjgl.opengl.GL11 final class ModelQuadcopter extends EntityModel[Drone] { val body = new ModelRenderer(this) @@ -78,9 +76,6 @@ final class ModelQuadcopter extends EntityModel[Drone] { wing3.render(stack, builder, light, overlay, r, g, b, a) if (drone.isRunning) { - RenderState.disableEntityLighting() - RenderSystem.depthFunc(GL11.GL_LEQUAL) - light0.xRot = drone.flapAngles(0)(0) light0.zRot = drone.flapAngles(0)(1) light1.xRot = drone.flapAngles(1)(0) @@ -90,24 +85,16 @@ final class ModelQuadcopter extends EntityModel[Drone] { light3.xRot = drone.flapAngles(3)(0) light3.zRot = drone.flapAngles(3)(1) - // Additive blending for the lights. - RenderState.makeItBlend() - RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - val lightColor = drone.lightColor - val r = (lightColor >>> 16) & 0xFF - val g = (lightColor >>> 8) & 0xFF - val b = (lightColor >>> 0) & 0xFF - RenderSystem.color3f(r / 255f, g / 255f, b / 255f) - - light0.render(stack, builder, light, overlay, r, g, b, a) - light1.render(stack, builder, light, overlay, r, g, b, a) - light2.render(stack, builder, light, overlay, r, g, b, a) - light3.render(stack, builder, light, overlay, r, g, b, a) - - RenderState.disableBlend() - RenderState.enableEntityLighting() - RenderSystem.color4f(1, 1, 1, 1) + val rr = r * ((lightColor >>> 16) & 0xFF) / 255f + val gg = g * ((lightColor >>> 8) & 0xFF) / 255f + val bb = b * ((lightColor >>> 0) & 0xFF) / 255f + val fullLight = LightTexture.pack(15, 15) + + light0.render(stack, builder, fullLight, OverlayTexture.NO_OVERLAY, rr, gg, bb, a) + light1.render(stack, builder, fullLight, OverlayTexture.NO_OVERLAY, rr, gg, bb, a) + light2.render(stack, builder, fullLight, OverlayTexture.NO_OVERLAY, rr, gg, bb, a) + light3.render(stack, builder, fullLight, OverlayTexture.NO_OVERLAY, rr, gg, bb, a) } stack.popPose() } From bfb9211ab638ddec772ec22e319fe36d4e371749 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 22 Sep 2022 22:27:20 +0200 Subject: [PATCH 086/159] Fix drones not being interactible --- src/main/scala/li/cil/oc/common/entity/Drone.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index a413b95215..514dd5ce7a 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -232,7 +232,7 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, // ----------------------------------------------------------------------- // - override def canBeCollidedWith = true + override def isPickable = true override def isPushable = true From 896723128ae907a917348a66e945f9aaa891ecf5 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 22 Sep 2022 22:43:46 +0200 Subject: [PATCH 087/159] Fix Drone selection animation --- src/main/scala/li/cil/oc/client/gui/Drone.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index a5da5eb429..da14cfa0c1 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -129,10 +129,9 @@ class Drone(state: container.Drone, playerInventory: PlayerInventory, name: ITex private def drawSelection(stack: MatrixStack) { val slot = inventoryContainer.selectedSlot if (slot >= 0 && slot < 16) { - RenderState.makeItBlend() Textures.bind(Textures.GUI.RobotSelection) - val now = System.currentTimeMillis() / 1000.0f - val offsetV = ((now - now.toInt) * selectionsStates).toInt * selectionStepV + val now = System.currentTimeMillis() % 1000 / 1000.0f + val offsetV = (now * selectionsStates).toInt * selectionStepV val x = leftPos + inventoryX - 1 + (slot % 4) * (selectionSize - 2) val y = topPos + inventoryY - 1 + (slot / 4) * (selectionSize - 2) From 2e0816add8e4ff04179effb9d3d9b8299c96f4f3 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 22 Sep 2022 23:30:08 +0200 Subject: [PATCH 088/159] Fix Drone container crash on open --- src/main/scala/li/cil/oc/common/container/ContainerTypes.java | 2 +- src/main/scala/li/cil/oc/common/container/Drone.scala | 4 ++-- src/main/scala/li/cil/oc/common/entity/Drone.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java index f85fe7cc20..4b5e98a85a 100644 --- a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java +++ b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java @@ -55,7 +55,7 @@ public static void registerContainers(RegistryEvent.Register> e register(e.getRegistry(), "disk_drive", (id, plr, buff) -> new DiskDrive(DISK_DRIVE, id, plr, new Inventory(1))); register(e.getRegistry(), "drone", (id, plr, buff) -> { int invSize = buff.readVarInt(); - return new Drone(DRONE, id, plr, new Inventory(invSize)); + return new Drone(DRONE, id, plr, new Inventory(8), invSize); }); register(e.getRegistry(), "printer", (id, plr, buff) -> new Printer(PRINTER, id, plr, new Inventory(3))); register(e.getRegistry(), "rack", (id, plr, buff) -> new Rack(RACK, id, plr, new Inventory(4))); diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index 8f80c74a32..04aaf8a86a 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -12,7 +12,7 @@ import net.minecraft.util.IntReferenceHolder import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: PlayerInventory, droneInv: IInventory) +class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: PlayerInventory, droneInv: IInventory, val mainInvSize: Int) extends Player(selfType, id, playerInventory, droneInv) { val deltaY = 0 @@ -95,7 +95,7 @@ class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: Playe class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int) extends StaticComponentSlot(container, inventory, index, x, y, getHostClass, common.Slot.Any, common.Tier.Any) { - def isValid = (0 until droneInv.getContainerSize).contains(getSlotIndex) + def isValid = (0 until mainInvSize).contains(getSlotIndex) @OnlyIn(Dist.CLIENT) override def isActive = isValid && super.isActive diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 514dd5ce7a..850544999c 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -497,7 +497,7 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, override def getDisplayName = StringTextComponent.EMPTY override def createMenu(id: Int, playerInventory: PlayerInventory, player: PlayerEntity) = - new container.Drone(ContainerTypes.DRONE, id, playerInventory, mainInventory) + new container.Drone(ContainerTypes.DRONE, id, playerInventory, mainInventory, mainInventory.getContainerSize) } override def interact(player: PlayerEntity, hand: Hand): ActionResultType = { From bef2bb1ff730c8b5d95ffd5b71e90bbf3c1b4025 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 23 Sep 2022 00:04:11 +0200 Subject: [PATCH 089/159] Fix drone physics and jittering --- src/main/scala/li/cil/oc/common/entity/Drone.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 850544999c..b2ccae8634 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -440,8 +440,8 @@ class Drone(selfType: EntityType[Drone], world: World) extends Entity(selfType, xo = getX yo = getY zo = getZ - moveTowardsClosestSpace(getX, (getBoundingBox.minY + getBoundingBox.maxY) / 2, getZ) - noPhysics = true + noPhysics = !level.noCollision(this) + if (noPhysics) moveTowardsClosestSpace(getX, (getBoundingBox.minY + getBoundingBox.maxY) / 2, getZ) if (isRunning) { val toTarget = new Vector3d(targetX - getX, targetY - getY, targetZ - getZ) From 95fffa8a01d9bfb075c040ccffdda6b3fe3a2bed Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 23 Sep 2022 00:15:30 +0200 Subject: [PATCH 090/159] Fix nanomachine GUI rendering --- .../cil/oc/client/renderer/RenderTypes.java | 9 ++++++++ .../oc/common/event/NanomachinesHandler.scala | 21 ++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 90ca13d578..1e3fc4384d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -135,6 +135,15 @@ public static final RenderType createFontTex(int id) { .createCompositeState(false)); } + public static final RenderType createTexturedQuad(String name, ResourceLocation texture, VertexFormat format, boolean additive) { + return create(OpenComputers.ID() + ":tex_quad_" + name, + format, GL11.GL_QUADS, 1024, State.builder() + .setTextureState(new TextureState(texture, false, false)) + .setTransparencyState(additive ? LIGHTNING_TRANSPARENCY : TRANSLUCENT_TRANSPARENCY) + .setAlphaState(DEFAULT_ALPHA) + .createCompositeState(false)); + } + private RenderTypes() { super(null, null, 0, 0, false, false, null, null); throw new Error(); diff --git a/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala index 941a310d41..fa272c7750 100644 --- a/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala @@ -4,14 +4,17 @@ import java.io.FileInputStream import java.io.FileOutputStream import com.mojang.blaze3d.matrix.MatrixStack +import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.nanomachines.Controller import li.cil.oc.client.Textures +import li.cil.oc.client.renderer.RenderTypes import li.cil.oc.common.EventHandler import li.cil.oc.common.nanomachines.ControllerImpl import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.IRenderTypeBuffer import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.entity.player.PlayerEntity @@ -23,11 +26,13 @@ import net.minecraftforge.event.entity.player.PlayerEvent import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent import net.minecraftforge.event.entity.player.PlayerEvent.PlayerRespawnEvent -import org.lwjgl.opengl.GL11 object NanomachinesHandler { object Client { + val TexNanomachines = RenderTypes.createTexturedQuad("nanomachines", Textures.GUI.Nanomachines, DefaultVertexFormats.POSITION_TEX, false) + val TexNanomachinesBar = RenderTypes.createTexturedQuad("nanomachines_bar", Textures.GUI.NanomachinesBar, DefaultVertexFormats.POSITION_TEX, false) + @SubscribeEvent def onRenderGameOverlay(e: RenderGameOverlayEvent.Post): Unit = { if (e.getType == RenderGameOverlayEvent.ElementType.TEXT) { @@ -52,26 +57,22 @@ object NanomachinesHandler { else if (y < 1) y * height else y) val fill = controller.getLocalBuffer / controller.getLocalBufferSize - Minecraft.getInstance.getTextureManager.bind(Textures.GUI.Nanomachines) - drawRect(stack, left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY) - Minecraft.getInstance.getTextureManager.bind(Textures.GUI.NanomachinesBar) - drawRect(stack, left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY, fill.toFloat) + val buffer = IRenderTypeBuffer.immediate(Tessellator.getInstance.getBuilder) + drawRect(stack, buffer.getBuffer(TexNanomachines), left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY) + drawRect(stack, buffer.getBuffer(TexNanomachinesBar), left.toInt, top.toInt, sizeX, sizeY, sizeX, sizeY, fill.toFloat) + buffer.endBatch() case _ => // Nothing to show. } } } - private def drawRect(stack: MatrixStack, x: Int, y: Int, w: Int, h: Int, tw: Int, th: Int, fill: Float = 1) { + private def drawRect(stack: MatrixStack, r: IVertexBuilder, x: Int, y: Int, w: Int, h: Int, tw: Int, th: Int, fill: Float = 1) { val sx = 1f / tw val sy = 1f / th - val t = Tessellator.getInstance - val r = t.getBuilder - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) r.vertex(stack.last.pose, x, y + h, 0).uv(0, h * sy).endVertex() r.vertex(stack.last.pose, x + w, y + h, 0).uv(w * sx, h * sy).endVertex() r.vertex(stack.last.pose, x + w, y + h * (1 - fill), 0).uv(w * sx, 1 - fill).endVertex() r.vertex(stack.last.pose, x, y + h * (1 - fill), 0).uv(0, 1 - fill).endVertex() - t.end() } } From c7c563ac11e942b28c3939566cdbeb41fe5db85c Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 23 Sep 2022 00:54:41 +0200 Subject: [PATCH 091/159] Fix tablet highlight renderer bleeding state --- .../client/renderer/HighlightRenderer.scala | 67 ++++++++----------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala index dc292ede06..9641c7458a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala @@ -26,6 +26,8 @@ object HighlightRenderer { lazy val tablet = api.Items.get(Constants.ItemName.Tablet) + val TexHologram = RenderTypes.createTexturedQuad("hologram_effect", Textures.Model.HologramEffect, DefaultVertexFormats.POSITION_TEX_COLOR, true) + @SubscribeEvent def onDrawBlockHighlight(e: DrawHighlightEvent.HighlightBlock): Unit = if (e.getTarget != null && e.getTarget.getBlockPos != null) { val hitInfo = e.getTarget @@ -39,17 +41,11 @@ object HighlightRenderer { val (minX, minY, minZ) = (shape.min(Direction.Axis.X).toFloat, shape.min(Direction.Axis.Y).toFloat, shape.min(Direction.Axis.Z).toFloat) val (maxX, maxY, maxZ) = (shape.max(Direction.Axis.X).toFloat, shape.max(Direction.Axis.Y).toFloat, shape.max(Direction.Axis.Z).toFloat) val sideHit = hitInfo.getDirection - val renderPos = blockPos + val view = e.getInfo.getPosition stack.pushPose() - RenderState.pushAttrib() - RenderState.makeItBlend() - Textures.bind(Textures.Model.HologramEffect) - - RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) - RenderSystem.color4f(0.0F, 1.0F, 0.0F, 0.4F) - stack.translate(renderPos.x, renderPos.y, renderPos.z) + stack.translate(blockPos.x - view.x, blockPos.y - view.y, blockPos.z - view.z) stack.scale(1.002f, 1.002f, 1.002f) if (Settings.get.hologramFlickerFrequency > 0 && random.nextDouble() < Settings.get.hologramFlickerFrequency) { @@ -58,45 +54,40 @@ object HighlightRenderer { stack.translate((random.nextGaussian() * 0.01 * sx).toFloat, (random.nextGaussian() * 0.01 * sy).toFloat, (random.nextGaussian() * 0.01 * sz).toFloat) } - val t = Tessellator.getInstance() - val r = t.getBuilder - r.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + val r = e.getBuffers.getBuffer(TexHologram) sideHit match { case Direction.UP => - r.vertex(stack.last.pose, maxX, maxY + 0.002f, maxZ).uv(maxZ * 16, maxX * 16).endVertex() - r.vertex(stack.last.pose, maxX, maxY + 0.002f, minZ).uv(minZ * 16, maxX * 16).endVertex() - r.vertex(stack.last.pose, minX, maxY + 0.002f, minZ).uv(minZ * 16, minX * 16).endVertex() - r.vertex(stack.last.pose, minX, maxY + 0.002f, maxZ).uv(maxZ * 16, minX * 16).endVertex() + r.vertex(stack.last.pose, maxX, maxY + 0.002f, maxZ).uv(maxZ * 16, maxX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX, maxY + 0.002f, minZ).uv(minZ * 16, maxX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, maxY + 0.002f, minZ).uv(minZ * 16, minX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, maxY + 0.002f, maxZ).uv(maxZ * 16, minX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() case Direction.DOWN => - r.vertex(stack.last.pose, maxX, minY - 0.002f, minZ).uv(minZ * 16, maxX * 16).endVertex() - r.vertex(stack.last.pose, maxX, minY - 0.002f, maxZ).uv(maxZ * 16, maxX * 16).endVertex() - r.vertex(stack.last.pose, minX, minY - 0.002f, maxZ).uv(maxZ * 16, minX * 16).endVertex() - r.vertex(stack.last.pose, minX, minY - 0.002f, minZ).uv(minZ * 16, minX * 16).endVertex() + r.vertex(stack.last.pose, maxX, minY - 0.002f, minZ).uv(minZ * 16, maxX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX, minY - 0.002f, maxZ).uv(maxZ * 16, maxX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, minY - 0.002f, maxZ).uv(maxZ * 16, minX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, minY - 0.002f, minZ).uv(minZ * 16, minX * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() case Direction.EAST => - r.vertex(stack.last.pose, maxX + 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, maxX + 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, maxX + 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).endVertex() - r.vertex(stack.last.pose, maxX + 0.002f, minY, minZ).uv(minZ * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX + 0.002f, minY, minZ).uv(minZ * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() case Direction.WEST => - r.vertex(stack.last.pose, minX - 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, minX - 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, minX - 0.002f, minY, minZ).uv(minZ * 16, minY * 16).endVertex() - r.vertex(stack.last.pose, minX - 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, maxY, maxZ).uv(maxZ * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, maxY, minZ).uv(minZ * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, minY, minZ).uv(minZ * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX - 0.002f, minY, maxZ).uv(maxZ * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() case Direction.SOUTH => - r.vertex(stack.last.pose, maxX, maxY, maxZ + 0.002f).uv(maxX * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, minX, maxY, maxZ + 0.002f).uv(minX * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, minX, minY, maxZ + 0.002f).uv(minX * 16, minY * 16).endVertex() - r.vertex(stack.last.pose, maxX, minY, maxZ + 0.002f).uv(maxX * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, maxX, maxY, maxZ + 0.002f).uv(maxX * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, maxY, maxZ + 0.002f).uv(minX * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, minY, maxZ + 0.002f).uv(minX * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX, minY, maxZ + 0.002f).uv(maxX * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() case _ => - r.vertex(stack.last.pose, minX, maxY, minZ - 0.002f).uv(minX * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, maxX, maxY, minZ - 0.002f).uv(maxX * 16, maxY * 16).endVertex() - r.vertex(stack.last.pose, maxX, minY, minZ - 0.002f).uv(maxX * 16, minY * 16).endVertex() - r.vertex(stack.last.pose, minX, minY, minZ - 0.002f).uv(minX * 16, minY * 16).endVertex() + r.vertex(stack.last.pose, minX, maxY, minZ - 0.002f).uv(minX * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX, maxY, minZ - 0.002f).uv(maxX * 16, maxY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, maxX, minY, minZ - 0.002f).uv(maxX * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() + r.vertex(stack.last.pose, minX, minY, minZ - 0.002f).uv(minX * 16, minY * 16).color(0.0F, 1.0F, 0.0F, 0.4F).endVertex() } - t.end() - RenderState.disableBlend() - RenderState.popAttrib() stack.popPose() } } From 229a1625180efd0fd548e8c602687245c17c723a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 24 Sep 2022 20:10:58 +0200 Subject: [PATCH 092/159] Fix 3d-print rendering in-world --- src/main/scala/li/cil/oc/client/Textures.scala | 2 -- .../oc/client/renderer/block/PrintModel.scala | 11 ++++++++--- .../renderer/tileentity/PrinterRenderer.scala | 5 +---- .../li/cil/oc/common/tileentity/Print.scala | 18 +++++++++++++++++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 1d15e1d1db..dc7815b6db 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -549,8 +549,6 @@ object Textures { else RenderState.bindTexture(0) } - def getSprite(location: String): TextureAtlasSprite = getSprite(new ResourceLocation(location)) - def getSprite(location: ResourceLocation): TextureAtlasSprite = Minecraft.getInstance.getModelManager.getAtlas(PlayerContainer.BLOCK_ATLAS).getSprite(location) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala index 4ef1d1d142..0978978c0e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/PrintModel.scala @@ -19,10 +19,12 @@ import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.model.IBakedModel import net.minecraft.client.renderer.model.ItemOverrideList import net.minecraft.client.renderer.texture.MissingTextureSprite +import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.entity.LivingEntity import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation import net.minecraftforge.client.model.data.IModelData import scala.collection.JavaConverters.bufferAsJavaList @@ -46,11 +48,14 @@ object PrintModel extends SmartBlockModelBase { case _ => super.getQuads(state, side, rand) } - private def resolveTexture(name: String) = { - val texture = Textures.getSprite(name) - if (texture.equals(MissingTextureSprite.getLocation)) Textures.getSprite("minecraft:blocks/" + name) + private def resolveTexture(name: String): TextureAtlasSprite = try { + val texture = Textures.getSprite(new ResourceLocation(name)) + if (texture.getName == MissingTextureSprite.getLocation) Textures.getSprite(new ResourceLocation("minecraft:blocks/" + name)) else texture } + catch { + case _: Throwable => Textures.getSprite(MissingTextureSprite.getLocation) + } class ItemModel(val stack: ItemStack) extends SmartBlockModelBase { val data = new PrintData(stack) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala index 32b48a0867..761349b031 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/PrinterRenderer.scala @@ -27,10 +27,8 @@ class PrinterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity if (printer.data.stateOff.nonEmpty) { val stack = printer.data.createItemStack() - RenderState.pushAttrib() matrix.pushPose() - val pos = printer.getBlockPos - matrix.translate(pos.getX + 0.5, pos.getY + 0.5 + 0.3, pos.getZ + 0.5) + matrix.translate(0.5, 0.5 + 0.3, 0.5) matrix.mulPose(Vector3f.YP.rotationDegrees((System.currentTimeMillis() % 20000) / 20000f * 360)) matrix.scale(0.75f, 0.75f, 0.75f) @@ -39,7 +37,6 @@ class PrinterRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntity Minecraft.getInstance.getItemRenderer.renderStatic(stack, ItemCameraTransforms.TransformType.FIXED, light, overlay, matrix, buffer) matrix.popPose() - RenderState.popAttrib() } RenderState.checkError(getClass.getName + ".render: leaving") diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index 0c442238ac..99e1406c5e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -25,10 +25,12 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn +import net.minecraftforge.client.model.data.IModelData +import net.minecraftforge.client.model.data.ModelProperty import scala.collection.convert.ImplicitConversionsToJava._ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) - extends TileEntity(selfType) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { + extends TileEntity(selfType) with traits.TileEntity with traits.RedstoneAware with traits.RotatableTile with IModelData { def this(selfType: TileEntityType[_ <: Print]) = this(selfType, None, None, None) def this(selfType: TileEntityType[_ <: Print], canToggle: () => Boolean, scheduleUpdate: Int => Unit, onStateChange: () => Unit) = @@ -155,4 +157,18 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo nbt.setNewCompoundTag(DataTag, data.saveData) nbt.putBoolean(StateTag, state) } + + // ----------------------------------------------------------------------- // + + @Deprecated + override def getModelData() = this + + @Deprecated + override def hasProperty(prop: ModelProperty[_]) = false + + @Deprecated + override def getData[T](prop: ModelProperty[T]): T = null.asInstanceOf[T] + + @Deprecated + override def setData[T](prop: ModelProperty[T], value: T): T = null.asInstanceOf[T] } From 2c01b8b0aa8605f543c2e436acec4d0ffcfbbf9b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 24 Sep 2022 23:48:44 +0200 Subject: [PATCH 093/159] Upgrade cable AABB bounds to VoxelShape --- .../client/renderer/HighlightRenderer.scala | 110 ------------------ .../scala/li/cil/oc/common/block/Cable.scala | 47 ++++---- .../li/cil/oc/common/tileentity/Cable.scala | 2 - 3 files changed, 20 insertions(+), 139 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala index 9641c7458a..f4326dc68d 100644 --- a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala @@ -118,118 +118,8 @@ object HighlightRenderer { RenderSystem.enableTexture() RenderSystem.disableBlend() - e.setCanceled(true) - case cable: common.tileentity.Cable => - // See WorldRenderer.renderHitOutline. - RenderSystem.enableBlend() - RenderSystem.blendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) - RenderSystem.color4f(0, 0, 0, 0.4f) - RenderSystem.lineWidth(2) - RenderSystem.disableTexture() - RenderSystem.depthMask(false) - stack.pushPose() - - stack.translate(blockPos.x, blockPos.y, blockPos.z) - - val mask = common.block.Cable.neighbors(world, hitInfo.getBlockPos) - val tesselator = Tessellator.getInstance - val buffer = tesselator.getBuilder() - - buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION) - Cable.drawOverlay(stack, buffer, mask) - tesselator.end() - - stack.popPose() - RenderSystem.depthMask(true) - RenderSystem.enableTexture() - RenderSystem.disableBlend() - e.setCanceled(true) case _ => } } - - private object Cable { - private final val EXPAND = 0.002f - private final val MIN = (common.block.Cable.MIN - EXPAND).toFloat - private final val MAX = (common.block.Cable.MAX + EXPAND).toFloat - - def drawOverlay(stack: MatrixStack, buffer: BufferBuilder, mask: Int): Unit = { - // Draw the cable arms - for (side <- Direction.values) { - if (((1 << side.get3DDataValue) & mask) != 0) { - val offset = if (side.getAxisDirection == Direction.AxisDirection.NEGATIVE) -EXPAND else 1 + EXPAND - val centre = if (side.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX - - // Draw the arm end quad - drawLineAdjacent(stack, buffer, side.getAxis, offset, MIN, MIN, MIN, MAX) - drawLineAdjacent(stack, buffer, side.getAxis, offset, MIN, MAX, MAX, MAX) - drawLineAdjacent(stack, buffer, side.getAxis, offset, MAX, MAX, MAX, MIN) - drawLineAdjacent(stack, buffer, side.getAxis, offset, MAX, MIN, MIN, MIN) - - // Draw the connecting lines to the middle - drawLineAlong(stack, buffer, side.getAxis, MIN, MIN, offset, centre) - drawLineAlong(stack, buffer, side.getAxis, MAX, MIN, offset, centre) - drawLineAlong(stack, buffer, side.getAxis, MAX, MAX, offset, centre) - drawLineAlong(stack, buffer, side.getAxis, MIN, MAX, offset, centre) - } - } - - // Draw the cable core - drawCore(stack, buffer, mask, Direction.WEST, Direction.DOWN, Direction.Axis.Z) - drawCore(stack, buffer, mask, Direction.WEST, Direction.UP, Direction.Axis.Z) - drawCore(stack, buffer, mask, Direction.EAST, Direction.DOWN, Direction.Axis.Z) - drawCore(stack, buffer, mask, Direction.EAST, Direction.UP, Direction.Axis.Z) - - drawCore(stack, buffer, mask, Direction.WEST, Direction.NORTH, Direction.Axis.Y) - drawCore(stack, buffer, mask, Direction.WEST, Direction.SOUTH, Direction.Axis.Y) - drawCore(stack, buffer, mask, Direction.EAST, Direction.NORTH, Direction.Axis.Y) - drawCore(stack, buffer, mask, Direction.EAST, Direction.SOUTH, Direction.Axis.Y) - - drawCore(stack, buffer, mask, Direction.DOWN, Direction.NORTH, Direction.Axis.X) - drawCore(stack, buffer, mask, Direction.DOWN, Direction.SOUTH, Direction.Axis.X) - drawCore(stack, buffer, mask, Direction.UP, Direction.NORTH, Direction.Axis.X) - drawCore(stack, buffer, mask, Direction.UP, Direction.SOUTH, Direction.Axis.X) - } - - /** Draw part of the core object */ - private def drawCore(stack: MatrixStack, buffer: BufferBuilder, mask: Int, a: Direction, b: Direction, other: Direction.Axis): Unit = { - if (((mask >> a.ordinal) & 1) != ((mask >> b.ordinal) & 1)) return - - val offA = if (a.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX - val offB = if (b.getAxisDirection == Direction.AxisDirection.NEGATIVE) MIN else MAX - drawLineAlong(stack, buffer, other, offA, offB, MIN, MAX) - } - - /** Draw a line parallel to an axis */ - private def drawLineAlong(stack: MatrixStack, buffer: BufferBuilder, axis: Direction.Axis, offA: Float, offB: Float, start: Float, end: Float): Unit = { - axis match { - case Direction.Axis.X => - buffer.vertex(stack.last.pose, start, offA, offB).endVertex() - buffer.vertex(stack.last.pose, end, offA, offB).endVertex() - case Direction.Axis.Y => - buffer.vertex(stack.last.pose, offA, start, offB).endVertex() - buffer.vertex(stack.last.pose, offA, end, offB).endVertex() - case Direction.Axis.Z => - buffer.vertex(stack.last.pose, offA, offB, start).endVertex() - buffer.vertex(stack.last.pose, offA, offB, end).endVertex() - } - } - - /** Draw a line perpendicular to an axis */ - private def drawLineAdjacent(stack: MatrixStack, buffer: BufferBuilder, axis: Direction.Axis, offset: Float, startA: Float, startB: Float, endA: Float, endB: Float): Unit = { - axis match { - case Direction.Axis.X => - buffer.vertex(stack.last.pose, offset, startA, startB).endVertex() - buffer.vertex(stack.last.pose, offset, endA, endB).endVertex() - case Direction.Axis.Y => - buffer.vertex(stack.last.pose, startA, offset, startB).endVertex() - buffer.vertex(stack.last.pose, endA, offset, endB).endVertex() - case Direction.Axis.Z => - buffer.vertex(stack.last.pose, startA, startB, offset).endVertex() - buffer.vertex(stack.last.pose, endA, endB, offset).endVertex() - } - } - } - } diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index be9812e9b9..8f921d6725 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -14,7 +14,10 @@ import net.minecraft.entity.{Entity, LivingEntity} import net.minecraft.item.{DyeColor, ItemStack} import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction -import net.minecraft.util.math.{AxisAlignedBB, BlockPos, RayTraceResult} +import net.minecraft.util.math.{BlockPos, RayTraceResult} +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.IBlockReader import net.minecraft.world.World @@ -32,13 +35,16 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends // ----------------------------------------------------------------------- // + // This is to skip the vanilla shape cache - we have our own, but based on World data, not BlockState. + override def hasDynamicShape() = true + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = world.getBlockEntity(pos) match { case t: tileentity.Cable => t.createItemStack() case _ => createItemStack() } - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = Cable.bounds(world, pos) + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = Cable.shape(world, pos) // ----------------------------------------------------------------------- // @@ -71,22 +77,22 @@ object Cable { final val MIN = 0.375 final val MAX = 1 - MIN - final val DefaultBounds: AxisAlignedBB = new AxisAlignedBB(MIN, MIN, MIN, MAX, MAX, MAX) + final val DefaultShape: VoxelShape = VoxelShapes.box(MIN, MIN, MIN, MAX, MAX, MAX) - final val CachedParts: Array[AxisAlignedBB] = Array( - new AxisAlignedBB( MIN, 0, MIN, MAX, MIN, MAX ), // Down - new AxisAlignedBB( MIN, MAX, MIN, MAX, 1, MAX ), // Up - new AxisAlignedBB( MIN, MIN, 0, MAX, MAX, MIN ), // North - new AxisAlignedBB( MIN, MIN, MAX, MAX, MAX, 1 ), // South - new AxisAlignedBB( 0, MIN, MIN, MIN, MAX, MAX ), // West - new AxisAlignedBB( MAX, MIN, MIN, 1, MAX, MAX )) // East + final val CachedParts: Array[VoxelShape] = Array( + VoxelShapes.box( MIN, 0, MIN, MAX, MIN, MAX ), // Down + VoxelShapes.box( MIN, MAX, MIN, MAX, 1, MAX ), // Up + VoxelShapes.box( MIN, MIN, 0, MAX, MAX, MIN ), // North + VoxelShapes.box( MIN, MIN, MAX, MAX, MAX, 1 ), // South + VoxelShapes.box( 0, MIN, MIN, MIN, MAX, MAX ), // West + VoxelShapes.box( MAX, MIN, MIN, 1, MAX, MAX )) // East final val CachedBounds = { // 6 directions = 6 bits = 11111111b >> 2 = 0xFF >> 2 (0 to 0xFF >> 2).map(mask => { - Direction.values.foldLeft(DefaultBounds)((bound, side) => { - if (((1 << side.get3DDataValue) & mask) != 0) bound.minmax(CachedParts(side.ordinal())) - else bound + Direction.values.foldLeft(DefaultShape)((shape, side) => { + if (((1 << side.get3DDataValue) & mask) != 0) VoxelShapes.or(shape, CachedParts(side.ordinal())) + else shape }) }).toArray } @@ -120,20 +126,7 @@ object Cable { result } - def bounds(world: IBlockReader, pos: BlockPos) = Cable.CachedBounds(Cable.neighbors(world, pos)) - - def parts(world: IBlockReader, pos: BlockPos, entityBox : AxisAlignedBB, boxes : util.List[AxisAlignedBB]) = { - val center = Cable.DefaultBounds.move(pos) - if (entityBox.intersects(center)) boxes.add(center) - - val mask = Cable.neighbors(world, pos) - for (side <- Direction.values) { - if(((1 << side.get3DDataValue) & mask) != 0) { - val part = Cable.CachedParts(side.ordinal()).move(pos) - if (entityBox.intersects(part)) boxes.add(part) - } - } - } + def shape(world: IBlockReader, pos: BlockPos) = Cable.CachedBounds(Cable.neighbors(world, pos)) private def hasNetworkNode(tileEntity: TileEntity, side: Direction): Boolean = { if (tileEntity != null) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala index 43d08ba5fd..131d76cd76 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Cable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Cable.scala @@ -41,6 +41,4 @@ class Cable(selfType: TileEntityType[_ <: Cable]) extends TileEntity(selfType) w api.Network.joinOrCreateNetwork(this) } } - - override def getRenderBoundingBox = common.block.Cable.bounds(getLevel, getBlockPos).move(x, y, z) } From c5f8df35b15f420b4bc7271def86c885dc338c43 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 26 Sep 2022 21:46:29 +0200 Subject: [PATCH 094/159] Upgrade 3d-print AABB bounds to VoxelShape --- .../client/renderer/HighlightRenderer.scala | 39 +------------------ .../scala/li/cil/oc/common/block/Print.scala | 14 ++++--- .../li/cil/oc/common/tileentity/Print.scala | 35 ++++++++++------- 3 files changed, 31 insertions(+), 57 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala index f4326dc68d..743913a9cd 100644 --- a/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/HighlightRenderer.scala @@ -1,23 +1,18 @@ package li.cil.oc.client.renderer import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.client.Textures -import li.cil.oc.util.ExtendedAABB._ import li.cil.oc.util.ExtendedWorld._ -import li.cil.oc.util.{BlockPosition, RenderState} -import li.cil.oc.{Constants, Settings, api, common} +import li.cil.oc.util.BlockPosition +import li.cil.oc.{Constants, Settings, api} import net.minecraft.client.Minecraft import net.minecraft.client.renderer._ import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.shapes.ISelectionContext -import net.minecraft.util.math.vector.Vector3d import net.minecraftforge.client.event.DrawHighlightEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import org.lwjgl.opengl.GL11 import scala.util.Random @@ -91,35 +86,5 @@ object HighlightRenderer { stack.popPose() } } - - Minecraft.getInstance.level.getBlockEntity(hitInfo.getBlockPos) match { - case print: common.tileentity.Print if print.shapes.nonEmpty => - val expansion = 0.002f - - // See WorldRenderer.renderHitOutline. - RenderSystem.enableBlend() - RenderSystem.blendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 1) - RenderSystem.color4f(0, 0, 0, 0.4f) - RenderSystem.lineWidth(2) - RenderSystem.disableTexture() - RenderSystem.depthMask(false) - - val tesselator = Tessellator.getInstance - val buffer = tesselator.getBuilder() - buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION) - for (shape <- print.shapes) { - val bounds = shape.bounds.rotateTowards(print.facing) - WorldRenderer.renderLineBox(stack, buffer, bounds.inflate(expansion, expansion, expansion) - .move(blockPos.x, blockPos.y, blockPos.z), 0, 0, 0, 0x66/0xFFf.toFloat) - } - tesselator.end() - - RenderSystem.depthMask(true) - RenderSystem.enableTexture() - RenderSystem.disableBlend() - - e.setCanceled(true) - case _ => - } } } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 30483782a9..9f8a48c231 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -22,11 +22,11 @@ import net.minecraft.item.ItemStack import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.RayTraceResult -import net.minecraft.util.math.vector.Vector3d +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader @@ -67,6 +67,8 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends } } + override def hasDynamicShape() = true + override def getLightValue(state: BlockState, world: IBlockReader, pos: BlockPos): Int = world match { case world: World if world.isLoaded(pos) => world.getBlockEntity(pos) match { @@ -93,10 +95,10 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends } } - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = { world.getBlockEntity(pos) match { - case print: tileentity.Print => print.bounds - case _ => super.getBoundingBox(state, world, pos) + case print: tileentity.Print => print.shape + case _ => super.getShape(state, world, pos, ctx) } } @@ -136,7 +138,7 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends override protected def doCustomInit(tileEntity: tileentity.Print, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) tileEntity.data.loadData(stack) - tileEntity.updateBounds() + tileEntity.updateShape() tileEntity.updateRedstone() tileEntity.getLevel.getLightEngine.checkBlock(tileEntity.getBlockPos) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index 99e1406c5e..6123a0a640 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -18,15 +18,18 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntityType import net.minecraft.util.Direction import net.minecraft.util.SoundCategory -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.math.shapes.IBooleanFunction +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.client.model.data.ModelProperty +import scala.collection.Iterable import scala.collection.convert.ImplicitConversionsToJava._ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) @@ -40,11 +43,11 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo val data = new PrintData() - var boundsOff = ExtendedAABB.unitBounds - var boundsOn = ExtendedAABB.unitBounds + var shapeOff = VoxelShapes.block + var shapeOn = VoxelShapes.block var state = false - def bounds = if (state) boundsOn else boundsOff + def shape = if (state) shapeOn else shapeOff def noclip = if (state) data.noclipOn else data.noclipOff def shapes = if (state) data.stateOn else data.stateOff @@ -85,13 +88,17 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo } } - def updateBounds(): Unit = { - boundsOff = data.stateOff.drop(1).foldLeft(data.stateOff.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.minmax(b.bounds)) - if (boundsOff.volume == 0) boundsOff = ExtendedAABB.unitBounds - else boundsOff = boundsOff.rotateTowards(facing) - boundsOn = data.stateOn.drop(1).foldLeft(data.stateOn.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.minmax(b.bounds)) - if (boundsOn.volume == 0) boundsOn = ExtendedAABB.unitBounds - else boundsOn = boundsOn.rotateTowards(facing) + private def convertShape(state: Iterable[PrintData.Shape]): VoxelShape = if (!state.isEmpty) { + state.foldLeft(VoxelShapes.empty)((curr, s) => { + val voxel = VoxelShapes.create(s.bounds.rotateTowards(facing)) + VoxelShapes.joinUnoptimized(curr, voxel, IBooleanFunction.OR) + }).optimize() + } + else VoxelShapes.block + + def updateShape(): Unit = { + shapeOff = convertShape(data.stateOff) + shapeOn = convertShape(data.stateOn) } def updateRedstone(): Unit = { @@ -109,7 +116,7 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo override protected def onRotationChanged(): Unit = { super.onRotationChanged() - updateBounds() + updateShape() } // ----------------------------------------------------------------------- // @@ -131,7 +138,7 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo state = nbt.getBoolean(StateTagCompat) else state = nbt.getBoolean(StateTag) - updateBounds() + updateShape() } override def saveForServer(nbt: CompoundNBT): Unit = { @@ -145,7 +152,7 @@ class Print(selfType: TileEntityType[_ <: Print], val canToggle: Option[() => Bo super.loadForClient(nbt) data.loadData(nbt.getCompound(DataTag)) state = nbt.getBoolean(StateTag) - updateBounds() + updateShape() if (getLevel != null) { getLevel.sendBlockUpdated(getBlockPos, getLevel.getBlockState(getBlockPos), getLevel.getBlockState(getBlockPos), 3) if (data.emitLight) getLevel.getLightEngine.checkBlock(getBlockPos) From c396d37e96ea981cc6c8fbbcc8928f346b4b91f3 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 26 Sep 2022 22:37:15 +0200 Subject: [PATCH 095/159] Upgrade static AABB bounds to VoxelShape --- .../scala/li/cil/oc/common/block/Hologram.scala | 8 +++++--- .../cil/oc/common/block/RobotAfterimage.scala | 14 +++++++++----- .../li/cil/oc/common/block/RobotProxy.scala | 17 +++++++++++------ .../li/cil/oc/common/block/SimpleBlock.scala | 10 ---------- .../li/cil/oc/common/tileentity/Robot.scala | 1 - 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index 07c02a282c..e5a19806c0 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -9,8 +9,10 @@ import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader @@ -20,11 +22,11 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToScala._ class Hologram(val tier: Int) extends SimpleBlock { - val bounds = new AxisAlignedBB(0, 0, 0, 1, 0.5f, 1) + val shape = VoxelShapes.box(0, 0, 0, 1, 0.5, 1) // ----------------------------------------------------------------------- // - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = bounds + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = shape // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index c72bb45e10..946bd9d529 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -19,10 +19,12 @@ import net.minecraft.item.ItemStack import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.world.IBlockReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld @@ -33,23 +35,25 @@ class RobotAfterimage(props: Properties = Properties.of(Material.STONE).strength // ----------------------------------------------------------------------- // + override def hasDynamicShape() = true + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = findMovingRobot(world, pos) match { case Some(robot) => robot.info.createItemStack() case _ => ItemStack.EMPTY } - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = { findMovingRobot(world, pos) match { case Some(robot) => val block = robot.getBlockState.getBlock.asInstanceOf[SimpleBlock] - val bounds = block.getBoundingBox(state, world, robot.getBlockPos) + val shape = block.getShape(state, world, robot.getBlockPos, ctx) val delta = robot.moveFrom.fold(BlockPos.ZERO)(vec => { val blockPos = robot.getBlockPos new BlockPos(blockPos.getX - vec.getX, blockPos.getY - vec.getY, blockPos.getZ - vec.getZ) }) - bounds.move(delta) - case _ => super.getBoundingBox(state, world, pos) + shape.move(delta.getX, delta.getY, delta.getZ) + case _ => super.getShape(state, world, pos, ctx) } } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 9d015a03d3..6c92e94ef8 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -33,9 +33,11 @@ import net.minecraft.loot.LootParameters import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent @@ -48,6 +50,8 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 setCreativeTab(null) ItemBlacklist.hide(this) + val shape = VoxelShapes.box(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) + override val getDescriptionId = "robot" var moving = new ThreadLocal[Option[tileentity.Robot]] { @@ -62,20 +66,21 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 case _ => ItemStack.EMPTY } - override def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { + override def hasDynamicShape() = true + + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = { world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy => val robot = proxy.robot - val bounds = new AxisAlignedBB(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) if (robot.isAnimatingMove) { val remaining = robot.animationTicksLeft.toDouble / robot.animationTicksTotal.toDouble val blockPos = robot.moveFrom.get val vec = robot.getBlockPos val delta = new BlockPos(blockPos.getX - vec.getX, blockPos.getY - vec.getY, blockPos.getZ - vec.getZ) - bounds.move(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) + shape.move(delta.getX * remaining, delta.getY * remaining, delta.getZ * remaining) } - else bounds - case _ => super.getBoundingBox(state, world, pos) + else shape + case _ => super.getShape(state, world, pos, ctx) } } diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 7b7902173d..25c9e53236 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -132,16 +132,6 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str // Block // ----------------------------------------------------------------------- // - @Deprecated - def getBoundingBox(state: BlockState, world: IBlockReader, pos: BlockPos): AxisAlignedBB = { - val shape = super.getShape(state, world, pos, ISelectionContext.empty()) - if (shape.isEmpty) shape.bounds else new AxisAlignedBB(0, 0, 0, 1, 1, 1) - } - - @Deprecated - override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = - VoxelShapes.create(getBoundingBox(state, world, pos)) - override def canHarvestBlock(state: BlockState, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = true override def getHarvestTool(state: BlockState): ToolType = null diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 953f7e427d..7bdf4c0e2f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -49,7 +49,6 @@ import net.minecraft.util.Direction import net.minecraft.util.SoundCategory import net.minecraft.util.SoundEvents import net.minecraft.util.Util -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.text.StringTextComponent import net.minecraftforge.common.MinecraftForge From d7ee8ced8c348c82cb1d0c49514e784dbddfcaf8 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 27 Sep 2022 00:11:27 +0200 Subject: [PATCH 096/159] Turn cable shape into a BlockState property --- .../oc/client/renderer/block/CableModel.scala | 43 ++++----- .../scala/li/cil/oc/common/block/Cable.scala | 89 +++++++++++++------ .../property/PropertyCableConnection.java | 54 +++++++++++ 3 files changed, 129 insertions(+), 57 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/block/property/PropertyCableConnection.java diff --git a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala index 8b14f8348b..d8fa5d5684 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/CableModel.scala @@ -4,10 +4,8 @@ import java.util import java.util.Collections import li.cil.oc.client.Textures -import li.cil.oc.common.block +import li.cil.oc.common.block.property.PropertyCableConnection import li.cil.oc.common.tileentity -import li.cil.oc.integration.Mods -import li.cil.oc.util.BlockPosition import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ItemColorizer @@ -27,29 +25,22 @@ import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable -object CableModel extends CableModel - -class CableModel extends SmartBlockModelBase { +object CableModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride - override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = + override def getQuads(state: BlockState, side: Direction, rand: util.Random, data: IModelData): util.List[BakedQuad] = { data match { - case cable: tileentity.Cable => - val world = cable.getLevel - val neighbors = block.Cable.neighbors(world, cable.getBlockPos) + case cable: tileentity.Cable if side == null => val color = cable.getColor - var isCableSide = 0 - for (side <- Direction.values) { - if (world.getBlockEntity(cable.getBlockPos.relative(side)).isInstanceOf[tileentity.Cable]){ - isCableSide = block.Cable.mask(side, isCableSide) - } - } val faces = mutable.ArrayBuffer.empty[BakedQuad] faces ++= bakeQuads(Middle, cableTexture, color) - for (side <- Direction.values) { - val connected = (neighbors & (1 << side.get3DDataValue)) != 0 - val isCableOnSide = (isCableSide & (1 << side.get3DDataValue)) != 0 + val directions = Direction.values + val numConnected = directions.count(d => state.getValue(PropertyCableConnection.BY_DIRECTION.get(d)) != PropertyCableConnection.Shape.NONE) + for (side <- directions) { + val shape = state.getValue(PropertyCableConnection.BY_DIRECTION.get(side)) + val connected = shape != PropertyCableConnection.Shape.NONE + val isCableOnSide = shape == PropertyCableConnection.Shape.CABLE val (plug, shortBody, longBody) = Connected(side.get3DDataValue) if (connected) { if (isCableOnSide) { @@ -60,21 +51,17 @@ class CableModel extends SmartBlockModelBase { faces ++= bakeQuads(plug, cableCapTexture, None) } } - else if (((1 << side.getOpposite.get3DDataValue) & neighbors) == neighbors || neighbors == 0) { - faces ++= bakeQuads(Disconnected(side.get3DDataValue), cableCapTexture, None) + else { + val otherConn = state.getValue(PropertyCableConnection.BY_DIRECTION.get(side.getOpposite)) != PropertyCableConnection.Shape.NONE + if ((otherConn && numConnected == 1) || numConnected == 0) { + faces ++= bakeQuads(Disconnected(side.get3DDataValue), cableCapTexture, None) + } } } bufferAsJavaList(faces) case _ => super.getQuads(state, side, rand) } - - protected def isCable(pos: BlockPosition) = { - pos.world match { - case Some(world) => - world.getBlockEntity(pos).isInstanceOf[tileentity.Cable] - case _ => false - } } protected final val Middle = makeBox(new Vector3d(6 / 16f, 6 / 16f, 6 / 16f), new Vector3d(10 / 16f, 10 / 16f, 10 / 16f)) diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index 8f921d6725..92e9cdcfe4 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -2,25 +2,27 @@ package li.cil.oc.common.block import java.util +import li.cil.oc.common.block.property.PropertyCableConnection import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block -import net.minecraft.block.Blocks import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.{Entity, LivingEntity} import net.minecraft.item.{DyeColor, ItemStack} +import net.minecraft.state.StateContainer import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction import net.minecraft.util.math.{BlockPos, RayTraceResult} import net.minecraft.util.math.shapes.ISelectionContext import net.minecraft.util.math.shapes.VoxelShape import net.minecraft.util.math.shapes.VoxelShapes -import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.IBlockReader +import net.minecraft.world.IWorld import net.minecraft.world.World +import net.minecraft.world.server.ServerWorld import net.minecraftforge.common.extensions.IForgeBlock import scala.collection.JavaConverters._ @@ -35,8 +37,19 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends // ----------------------------------------------------------------------- // - // This is to skip the vanilla shape cache - we have our own, but based on World data, not BlockState. - override def hasDynamicShape() = true + override protected def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = { + builder.add(PropertyCableConnection.DOWN, PropertyCableConnection.UP, + PropertyCableConnection.NORTH, PropertyCableConnection.SOUTH, + PropertyCableConnection.WEST, PropertyCableConnection.EAST) + } + + registerDefaultState(defaultBlockState. + setValue(PropertyCableConnection.DOWN, PropertyCableConnection.Shape.NONE). + setValue(PropertyCableConnection.UP, PropertyCableConnection.Shape.NONE). + setValue(PropertyCableConnection.NORTH, PropertyCableConnection.Shape.NONE). + setValue(PropertyCableConnection.SOUTH, PropertyCableConnection.Shape.NONE). + setValue(PropertyCableConnection.WEST, PropertyCableConnection.Shape.NONE). + setValue(PropertyCableConnection.EAST, PropertyCableConnection.Shape.NONE)) override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = world.getBlockEntity(pos) match { @@ -44,7 +57,26 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends case _ => createItemStack() } - override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = Cable.shape(world, pos) + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = Cable.shape(state) + + override def updateShape(state: BlockState, fromSide: Direction, fromState: BlockState, world: IWorld, pos: BlockPos, fromPos: BlockPos): BlockState = + Cable.updateState(state, fromSide, fromState, world, pos, fromPos) + + // Connecting to other blocks requires this cable to have a TE set, so wait until next tick and update. + override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { + // Only schedule this update if this cable was newly placed (not if it changed shape). + if (!prevState.is(this)) { + world match { + case srvWorld: ServerWorld if !srvWorld.isClientSide => srvWorld.getBlockTicks.scheduleTick(pos, this, 1) + case _ => + } + } + } + + override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: util.Random) { + val newState = Block.updateFromNeighbourShapes(state, world, pos) + Block.updateOrDestroy(state, newState, world, pos, 3) + } // ----------------------------------------------------------------------- // @@ -52,12 +84,6 @@ class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends // ----------------------------------------------------------------------- // - @Deprecated - override def neighborChanged(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, sourcePos: BlockPos, b: Boolean) { - world.sendBlockUpdated(pos, state, state, 3) - super.neighborChanged(state, world, pos, neighborBlock, sourcePos, b) - } - override protected def doCustomInit(tileEntity: tileentity.Cable, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) if (!tileEntity.world.isClientSide) { @@ -99,35 +125,40 @@ object Cable { def mask(side: Direction, value: Int = 0) = value | (1 << side.get3DDataValue) - def neighbors(world: IBlockReader, pos: BlockPos) = { + def shape(state: BlockState): VoxelShape = { var result = 0 - val tileEntity = world.getBlockEntity(pos) for (side <- Direction.values) { - val tpos = pos.relative(side) - val hasNode = hasNetworkNode(tileEntity, side) - if (hasNode && (world match { - case world: World => world.isLoaded(tpos) - case _ => { - val state = world.getBlockState(tpos) - state.getBlock.isAir(state, world, tpos) - } - })) { - val neighborTileEntity = world.getBlockEntity(tpos) + val sideShape = state.getValue(PropertyCableConnection.BY_DIRECTION.get(side)) + if (sideShape != PropertyCableConnection.Shape.NONE) { + result = mask(side, result) + } + } + Cable.CachedBounds(result) + } + + def updateState(state: BlockState, fromSide: Direction, fromState: BlockState, world: IBlockReader, pos: BlockPos, fromPos: BlockPos): BlockState = { + val prop = PropertyCableConnection.BY_DIRECTION.get(fromSide) + if (fromState.is(state.getBlock)) { + state.setValue(prop, PropertyCableConnection.Shape.CABLE) + } + else { + val tileEntity = world.getBlockEntity(pos) + val hasNode = hasNetworkNode(tileEntity, fromSide) + if (hasNode) { + val neighborTileEntity = world.getBlockEntity(fromPos) if (neighborTileEntity != null && neighborTileEntity.getLevel != null) { - val neighborHasNode = hasNetworkNode(neighborTileEntity, side.getOpposite) + val neighborHasNode = hasNetworkNode(neighborTileEntity, fromSide.getOpposite) val canConnectColor = canConnectBasedOnColor(tileEntity, neighborTileEntity) - val canConnectIM = canConnectFromSideIM(tileEntity, side) && canConnectFromSideIM(neighborTileEntity, side.getOpposite) + val canConnectIM = canConnectFromSideIM(tileEntity, fromSide) && canConnectFromSideIM(neighborTileEntity, fromSide.getOpposite) if (neighborHasNode && canConnectColor && canConnectIM) { - result = mask(side, result) + return state.setValue(prop, PropertyCableConnection.Shape.DEVICE) } } } + state.setValue(prop, PropertyCableConnection.Shape.NONE) } - result } - def shape(world: IBlockReader, pos: BlockPos) = Cable.CachedBounds(Cable.neighbors(world, pos)) - private def hasNetworkNode(tileEntity: TileEntity, side: Direction): Boolean = { if (tileEntity != null) { if (tileEntity.isInstanceOf[tileentity.RobotProxy]) return false diff --git a/src/main/scala/li/cil/oc/common/block/property/PropertyCableConnection.java b/src/main/scala/li/cil/oc/common/block/property/PropertyCableConnection.java new file mode 100644 index 0000000000..ce5917c306 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/property/PropertyCableConnection.java @@ -0,0 +1,54 @@ +package li.cil.oc.common.block.property; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; + +import net.minecraft.state.EnumProperty; +import net.minecraft.util.Direction; +import net.minecraft.util.IStringSerializable; + +public final class PropertyCableConnection { + public static enum Shape implements IStringSerializable { + NONE("none"), + CABLE("cable"), + DEVICE("device"); + + private final String name; + + private Shape(String name) { + this.name = name; + } + + public String getSerializedName() { + return name; + } + + public String toString() { + return name; + } + } + + public static final EnumProperty DOWN = EnumProperty.create("conn_down", Shape.class); + public static final EnumProperty UP = EnumProperty.create("conn_up", Shape.class); + public static final EnumProperty NORTH = EnumProperty.create("conn_north", Shape.class); + public static final EnumProperty SOUTH = EnumProperty.create("conn_south", Shape.class); + public static final EnumProperty WEST = EnumProperty.create("conn_west", Shape.class); + public static final EnumProperty EAST = EnumProperty.create("conn_east", Shape.class); + public static Map> BY_DIRECTION; + + static + { + EnumMap> byDir = new EnumMap<>(Direction.class); + byDir.put(Direction.DOWN, DOWN); + byDir.put(Direction.UP, UP); + byDir.put(Direction.NORTH, NORTH); + byDir.put(Direction.SOUTH, SOUTH); + byDir.put(Direction.WEST, WEST); + byDir.put(Direction.EAST, EAST); + BY_DIRECTION = Collections.unmodifiableMap(byDir); + } + + private PropertyCableConnection() { + } +} From 3a0b59d8060b2977020a2517411e4bf4927d043e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 27 Sep 2022 23:11:00 +0200 Subject: [PATCH 097/159] Fix incorrect block state model multiplexing --- .../renderer/block/ModelInitialization.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 2f05bd9b38..952fa4add4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -21,6 +21,8 @@ import net.minecraft.util.IItemProvider import net.minecraft.util.Direction import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.{ModelBakeEvent, ModelRegistryEvent} +import net.minecraftforge.client.model.data.IDynamicBakedModel +import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus @@ -118,9 +120,9 @@ object ModelInitialization { override def resolve(base: IBakedModel, stack: ItemStack, world: ClientWorld, holder: LivingEntity) = Option(custom.getModelLocation(stack)).map(registry).getOrElse(original) } - val fake = new IBakedModel { + val fake = new IDynamicBakedModel { @Deprecated - override def getQuads(state: BlockState, dir: Direction, rand: Random) = original.getQuads(state, dir, rand) + override def getQuads(state: BlockState, dir: Direction, rand: Random, data: IModelData) = original.getQuads(state, dir, rand, data) override def useAmbientOcclusion() = original.useAmbientOcclusion @@ -156,20 +158,17 @@ object ModelInitialization { registry.keySet.toArray.foreach { case location: ModelResourceLocation => { - var parent = registry.get(location) for ((name, model) <- modelOverrides) { val pattern = s"^${Settings.resourceDomain}:$name#.*" if (location.toString.matches(pattern)) { - parent = model(parent) - registry.put(location, parent) + registry.put(location, model(registry.get(location))) } } - modelRemappings.get(location) match { - case Some(remapped) => registry.put(remapped, parent) - case _ => - } } case _ => } + for ((real, virtual) <- modelRemappings) { + registry.put(real, registry.get(virtual)) + } } } From a105db8d1a7487410a84a9ac5e8fffded5f978ec Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:44:51 +0200 Subject: [PATCH 098/159] Fix opening creative computer GUIs in survival --- src/main/scala/li/cil/oc/common/block/Case.scala | 2 +- src/main/scala/li/cil/oc/common/block/RobotProxy.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index eb7205dea2..634cb7c623 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -52,7 +52,7 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t override def energyThroughput = Settings.get.caseRate(tier) override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { - case te: tileentity.Case => ContainerTypes.openCaseGui(player, te) + case te: tileentity.Case if te.stillValid(player) => ContainerTypes.openCaseGui(player, te) case _ => } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 6c92e94ef8..90fac11d39 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -202,7 +202,9 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 (player, world.getBlockEntity(pos)) match { case (srvPlr: ServerPlayerEntity, proxy: tileentity.RobotProxy) if proxy.robot.node.network != null => PacketSender.sendRobotSelectedSlotChange(proxy.robot) - ContainerTypes.openRobotGui(srvPlr, proxy.robot) + if (proxy.stillValid(player)) { + ContainerTypes.openRobotGui(srvPlr, proxy.robot) + } case _ => } } From ed519b1678eeaf476e79d963e838f1cb29fbfba5 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 30 Sep 2022 23:24:02 +0200 Subject: [PATCH 099/159] Fix some blocks breaking instantly when mined --- .../li/cil/oc/common/block/Adapter.scala | 3 +- .../li/cil/oc/common/block/Assembler.scala | 3 +- .../scala/li/cil/oc/common/block/Cable.scala | 3 +- .../li/cil/oc/common/block/Capacitor.scala | 3 +- .../oc/common/block/CarpetedCapacitor.scala | 3 +- .../scala/li/cil/oc/common/block/Case.scala | 3 +- .../cil/oc/common/block/ChameliumBlock.scala | 2 +- .../li/cil/oc/common/block/Charger.scala | 3 +- .../li/cil/oc/common/block/Disassembler.scala | 3 +- .../li/cil/oc/common/block/DiskDrive.scala | 3 +- .../li/cil/oc/common/block/FakeEndstone.scala | 2 +- .../li/cil/oc/common/block/Geolyzer.scala | 3 +- .../li/cil/oc/common/block/Hologram.scala | 3 +- .../li/cil/oc/common/block/Keyboard.scala | 3 +- .../cil/oc/common/block/Microcontroller.scala | 5 +- .../li/cil/oc/common/block/MotionSensor.scala | 3 +- .../li/cil/oc/common/block/NetSplitter.scala | 3 +- .../cil/oc/common/block/PowerConverter.scala | 3 +- .../oc/common/block/PowerDistributor.scala | 3 +- .../scala/li/cil/oc/common/block/Print.scala | 6 +- .../li/cil/oc/common/block/Printer.scala | 3 +- .../scala/li/cil/oc/common/block/Rack.scala | 4 +- .../scala/li/cil/oc/common/block/Raid.scala | 5 +- .../li/cil/oc/common/block/Redstone.scala | 3 +- .../cil/oc/common/block/RedstoneAware.scala | 3 +- .../scala/li/cil/oc/common/block/Relay.scala | 3 +- .../cil/oc/common/block/RobotAfterimage.scala | 7 +- .../li/cil/oc/common/block/RobotProxy.scala | 5 +- .../scala/li/cil/oc/common/block/Screen.scala | 5 +- .../li/cil/oc/common/block/SimpleBlock.scala | 9 +-- .../li/cil/oc/common/block/Transposer.scala | 3 +- .../li/cil/oc/common/block/Waypoint.scala | 3 +- .../scala/li/cil/oc/common/init/Blocks.scala | 75 ++++++++++--------- 33 files changed, 100 insertions(+), 91 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Adapter.scala b/src/main/scala/li/cil/oc/common/block/Adapter.scala index e1b66f5c8f..38c086d9df 100644 --- a/src/main/scala/li/cil/oc/common/block/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/block/Adapter.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.block import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity @@ -15,7 +16,7 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.IWorldReader import net.minecraft.world.World -class Adapter extends SimpleBlock with traits.GUI { +class Adapter(props: Properties) extends SimpleBlock(props) with traits.GUI { override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { case te: tileentity.Adapter => ContainerTypes.openAdapterGui(player, te) case _ => diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index 689a194d4b..3b5722afa7 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.block import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.util.Direction @@ -10,7 +11,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Assembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { +class Assembler(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { override def energyThroughput = Settings.get.assemblerRate override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index 92e9cdcfe4..5733e629c5 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity @@ -28,7 +29,7 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.collection.JavaConverters._ import scala.reflect.ClassTag -class Cable(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends SimpleBlock with IForgeBlock with traits.CustomDrops[tileentity.Cable] { +class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends SimpleBlock(props) with IForgeBlock with traits.CustomDrops[tileentity.Cable] { // For Immibis Microblock support. val ImmibisMicroblocks_TransformableBlockMarker = null diff --git a/src/main/scala/li/cil/oc/common/block/Capacitor.scala b/src/main/scala/li/cil/oc/common/block/Capacitor.scala index 75670dfe8f..1d792ef4a0 100644 --- a/src/main/scala/li/cil/oc/common/block/Capacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/Capacitor.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.block import java.util.Random import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos @@ -10,7 +11,7 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld -class Capacitor extends SimpleBlock { +class Capacitor(props: Properties) extends SimpleBlock(props) { @Deprecated override def isRandomlyTicking(state: BlockState) = true diff --git a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala index 946c0a40a2..14606b4034 100644 --- a/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala +++ b/src/main/scala/li/cil/oc/common/block/CarpetedCapacitor.scala @@ -1,8 +1,9 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.world.IBlockReader -class CarpetedCapacitor extends Capacitor { +class CarpetedCapacitor(props: Properties) extends Capacitor(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.CarpetedCapacitor(tileentity.TileEntityTypes.CARPETED_CAPACITOR) } diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 634cb7c623..4fc6e74dbb 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -8,6 +8,7 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -26,7 +27,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ -class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { +class Case(props: Properties, val tier: Int) extends RedstoneAware(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing, property.PropertyRunning.Running) diff --git a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala index c5dc8ecb93..b4a5652273 100644 --- a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala @@ -19,7 +19,7 @@ object ChameliumBlock { final val Color = EnumProperty.create("color", classOf[DyeColor]) } -class ChameliumBlock(props: Properties = Properties.of(Material.STONE).strength(2, 5)) extends SimpleBlock(props) { +class ChameliumBlock(props: Properties) extends SimpleBlock(props) { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]): Unit = { builder.add(ChameliumBlock.Color) } diff --git a/src/main/scala/li/cil/oc/common/block/Charger.scala b/src/main/scala/li/cil/oc/common/block/Charger.scala index fd831b15c7..985a6000ba 100644 --- a/src/main/scala/li/cil/oc/common/block/Charger.scala +++ b/src/main/scala/li/cil/oc/common/block/Charger.scala @@ -6,6 +6,7 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import li.cil.oc.server.PacketSender +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity @@ -18,7 +19,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Charger extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { +class Charger(props: Properties) extends RedstoneAware(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/Disassembler.scala b/src/main/scala/li/cil/oc/common/block/Disassembler.scala index 56a925812f..058cc5bbea 100644 --- a/src/main/scala/li/cil/oc/common/block/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Disassembler.scala @@ -6,6 +6,7 @@ import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity @@ -19,7 +20,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ -class Disassembler extends SimpleBlock with traits.PowerAcceptor with traits.StateAware with traits.GUI { +class Disassembler(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, (Settings.get.disassemblerBreakChance * 100).toInt.toString)) { tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) diff --git a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala index b06442df45..e42d5fb0e2 100644 --- a/src/main/scala/li/cil/oc/common/block/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/block/DiskDrive.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -24,7 +25,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ -class DiskDrive extends SimpleBlock with traits.GUI { +class DiskDrive(props: Properties) extends SimpleBlock(props) with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala b/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala index c0fb07407f..e6f17bc1ae 100644 --- a/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala +++ b/src/main/scala/li/cil/oc/common/block/FakeEndstone.scala @@ -4,5 +4,5 @@ import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.block.material.Material -class FakeEndstone extends SimpleBlock(Properties.of(Material.STONE).strength(3, 5)) { +class FakeEndstone(props: Properties) extends SimpleBlock(props) { } diff --git a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala index a8f6914c15..7a3d059b35 100644 --- a/src/main/scala/li/cil/oc/common/block/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/common/block/Geolyzer.scala @@ -1,9 +1,10 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Geolyzer extends SimpleBlock { +class Geolyzer(props: Properties) extends SimpleBlock(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.Geolyzer(tileentity.TileEntityTypes.GEOLYZER) } diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index e5a19806c0..e25d62b13e 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -5,6 +5,7 @@ import java.util import li.cil.oc.common.tileentity import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity @@ -21,7 +22,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import scala.collection.convert.ImplicitConversionsToScala._ -class Hologram(val tier: Int) extends SimpleBlock { +class Hologram(props: Properties, val tier: Int) extends SimpleBlock(props) { val shape = VoxelShapes.box(0, 0, 0, 1, 0.5, 1) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/Keyboard.scala b/src/main/scala/li/cil/oc/common/block/Keyboard.scala index aeabc49bb3..bc2a5064b7 100644 --- a/src/main/scala/li/cil/oc/common/block/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/block/Keyboard.scala @@ -15,7 +15,6 @@ import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.block.BlockState -import net.minecraft.block.material.Material import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.BlockItemUseContext import net.minecraft.item.ItemStack @@ -33,7 +32,7 @@ import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Keyboard(props: Properties = Properties.of(Material.STONE).strength(2, 5).noOcclusion()) extends SimpleBlock(props) { +class Keyboard(props: Properties) extends SimpleBlock(props) { // For Immibis Microblock support. val ImmibisMicroblocks_TransformableBlockMarker = null diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index fa77275d5d..1a80f467cf 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -16,6 +16,7 @@ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity import li.cil.oc.util.StackOption._ import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -36,7 +37,9 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) extends RedstoneAware with IForgeBlock with traits.PowerAcceptor with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { +class Microcontroller(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) + extends RedstoneAware(props) with IForgeBlock with traits.PowerAcceptor with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { + setCreativeTab(null) ItemBlacklist.hide(this) diff --git a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala index b155273e0a..de4731bf63 100644 --- a/src/main/scala/li/cil/oc/common/block/MotionSensor.scala +++ b/src/main/scala/li/cil/oc/common/block/MotionSensor.scala @@ -1,9 +1,10 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.world.IBlockReader import net.minecraft.world.World -class MotionSensor extends SimpleBlock { +class MotionSensor(props: Properties) extends SimpleBlock(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.MotionSensor(tileentity.TileEntityTypes.MOTION_SENSOR) } diff --git a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala index f44dc20950..90f2e57ce9 100644 --- a/src/main/scala/li/cil/oc/common/block/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/block/NetSplitter.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -13,7 +14,7 @@ import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.world.IBlockReader import net.minecraft.world.World -class NetSplitter extends RedstoneAware { +class NetSplitter(props: Properties) extends RedstoneAware(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.NetSplitter(tileentity.TileEntityTypes.NET_SPLITTER) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index bb2cb24871..bb28c59ce2 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -8,12 +8,13 @@ import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack import net.minecraft.world.IBlockReader import net.minecraft.world.World -class PowerConverter extends SimpleBlock with traits.PowerAcceptor { +class PowerConverter(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor { if (Settings.get.ignorePower) { setCreativeTab(null) ItemBlacklist.hide(this) diff --git a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala index 6fb32fadc8..d12d940ae5 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerDistributor.scala @@ -1,10 +1,11 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.world.IBlockReader import net.minecraft.world.World -class PowerDistributor extends SimpleBlock { +class PowerDistributor(props: Properties) extends SimpleBlock(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.PowerDistributor(tileentity.TileEntityTypes.POWER_DISTRIBUTOR) } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 9f8a48c231..283cbe7b25 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -37,7 +37,7 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.collection.convert.ImplicitConversionsToJava._ import scala.reflect.ClassTag -class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware(Properties.of(Material.METAL).strength(1, 5).noOcclusion()) +class Print(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware(props) with IForgeBlock with traits.CustomDrops[tileentity.Print] { setCreativeTab(null) ItemBlacklist.hide(this) @@ -67,8 +67,6 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends } } - override def hasDynamicShape() = true - override def getLightValue(state: BlockState, world: IBlockReader, pos: BlockPos): Int = world match { case world: World if world.isLoaded(pos) => world.getBlockEntity(pos) match { @@ -102,8 +100,6 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends } } - override def isValidSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, `type`: EntityType[_]): Boolean = true - def tickRate(world: World) = 20 override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: Random): Unit = { diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala index 2f6a8e22f0..94a022bffe 100644 --- a/src/main/scala/li/cil/oc/common/block/Printer.scala +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.block import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.util.Direction @@ -9,7 +10,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Printer extends SimpleBlock with traits.StateAware with traits.GUI { +class Printer(props: Properties) extends SimpleBlock(props) with traits.StateAware with traits.GUI { override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { case te: tileentity.Printer => ContainerTypes.openPrinterGui(player, te) case _ => diff --git a/src/main/scala/li/cil/oc/common/block/Rack.scala b/src/main/scala/li/cil/oc/common/block/Rack.scala index 38bf729140..f5de252cb9 100644 --- a/src/main/scala/li/cil/oc/common/block/Rack.scala +++ b/src/main/scala/li/cil/oc/common/block/Rack.scala @@ -5,6 +5,7 @@ import li.cil.oc.api.component.RackMountable import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity @@ -14,7 +15,6 @@ import net.minecraft.state.StateContainer import net.minecraft.util.Direction import net.minecraft.util.Direction.Axis import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.RayTraceResult @@ -22,7 +22,7 @@ import net.minecraft.util.math.vector.Vector3d import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Rack extends RedstoneAware with traits.PowerAcceptor with traits.StateAware with traits.GUI { +class Rack(props: Properties) extends RedstoneAware(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index 0cb93f3480..bbb9234757 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -8,6 +8,7 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag @@ -26,7 +27,9 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends SimpleBlock with IForgeBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { +class Raid(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Raid]) + extends SimpleBlock(props) with IForgeBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { + protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/Redstone.scala b/src/main/scala/li/cil/oc/common/block/Redstone.scala index d6ccbc5877..3cc588e5f5 100644 --- a/src/main/scala/li/cil/oc/common/block/Redstone.scala +++ b/src/main/scala/li/cil/oc/common/block/Redstone.scala @@ -5,6 +5,7 @@ import java.util import li.cil.oc.common.tileentity import li.cil.oc.integration.Mods import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -15,7 +16,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ -class Redstone extends RedstoneAware { +class Redstone(props: Properties) extends RedstoneAware(props) { override protected def tooltipTail(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { super.tooltipTail(stack, world, tooltip, advanced) // todo more generic way for redstone mods to provide lines diff --git a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala index a822aa0b1f..868b7e89ce 100644 --- a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala @@ -4,13 +4,12 @@ import li.cil.oc.common.tileentity import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState -import net.minecraft.block.material.Material import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -abstract class RedstoneAware(props: Properties = Properties.of(Material.METAL)) extends SimpleBlock(props) { +abstract class RedstoneAware(props: Properties) extends SimpleBlock(props) { override def isSignalSource(state: BlockState): Boolean = true override def canConnectRedstone(state: BlockState, world: IBlockReader, pos: BlockPos, side: Direction): Boolean = diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala index a701d53f71..9bc69b21a0 100644 --- a/src/main/scala/li/cil/oc/common/block/Relay.scala +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -3,12 +3,13 @@ package li.cil.oc.common.block import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { +class Relay(props: Properties) extends SimpleBlock(props) with traits.GUI with traits.PowerAcceptor { override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { case te: tileentity.Relay => ContainerTypes.openRelayGui(player, te) case _ => diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index 946bd9d529..f00802903f 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -12,7 +12,6 @@ import li.cil.oc.util.Rarity import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Blocks import net.minecraft.block.BlockState -import net.minecraft.block.material.Material import net.minecraft.entity.player.PlayerEntity import net.minecraft.fluid.FluidState import net.minecraft.item.ItemStack @@ -29,14 +28,12 @@ import net.minecraft.world.IBlockReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld -class RobotAfterimage(props: Properties = Properties.of(Material.STONE).strength(2, 5).noOcclusion()) extends SimpleBlock(props) { +class RobotAfterimage(props: Properties) extends SimpleBlock(props) { setCreativeTab(null) ItemBlacklist.hide(this) // ----------------------------------------------------------------------- // - override def hasDynamicShape() = true - override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = findMovingRobot(world, pos) match { case Some(robot) => robot.info.createItemStack() @@ -64,8 +61,6 @@ class RobotAfterimage(props: Properties = Properties.of(Material.STONE).strength Rarity.byTier(data.tier) } - override def isAir(state: BlockState, world: IBlockReader, pos: BlockPos): Boolean = true - // ----------------------------------------------------------------------- // override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 90fac11d39..c618711f96 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -19,7 +19,6 @@ import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState -import net.minecraft.block.material.Material import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity @@ -46,7 +45,7 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ -class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 10).noOcclusion()) extends RedstoneAware(props) with traits.StateAware { +class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.StateAware { setCreativeTab(null) ItemBlacklist.hide(this) @@ -66,8 +65,6 @@ class RobotProxy(props: Properties = Properties.of(Material.STONE).strength(2, 1 case _ => ItemStack.EMPTY } - override def hasDynamicShape() = true - override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = { world.getBlockEntity(pos) match { case proxy: tileentity.RobotProxy => diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index d1b49b90e3..7426c55301 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -14,6 +14,7 @@ import li.cil.oc.util.PackedColor import li.cil.oc.util.Rarity import li.cil.oc.util.RotationHelper import li.cil.oc.util.Tooltip +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.Minecraft @@ -26,7 +27,7 @@ import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.{AxisAlignedBB, BlockPos} +import net.minecraft.util.math.BlockPos import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.{IBlockReader, World} @@ -34,7 +35,7 @@ import net.minecraftforge.api.distmarker.{Dist, OnlyIn} import scala.collection.convert.ImplicitConversionsToScala._ -class Screen(val tier: Int) extends RedstoneAware { +class Screen(props: Properties, val tier: Int) extends RedstoneAware(props) { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Pitch, PropertyRotatable.Yaw) diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 25c9e53236..f68dc14a3d 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -10,7 +10,6 @@ import li.cil.oc.common.tileentity.traits.Rotatable import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.Tooltip -import net.minecraft.block.AbstractBlock.IExtendedPositionPredicate import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.block.BlockRenderType @@ -28,7 +27,6 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand -import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.shapes.ISelectionContext @@ -46,9 +44,7 @@ import net.minecraftforge.common.ToolType import scala.collection.convert.ImplicitConversionsToScala._ -abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).strength(2, 5)) extends ContainerBlock(props.isValidSpawn(new IExtendedPositionPredicate[EntityType[_]] { - override def test(state: BlockState, world: IBlockReader, pos: BlockPos, entity: EntityType[_]) = state.getBlock.asInstanceOf[SimpleBlock].isValidSpawn(state, world, pos, entity) -}).noOcclusion) { +abstract class SimpleBlock(props: Properties) extends ContainerBlock(props) { @Deprecated private var creativeTab: ItemGroup = CreativeTab @@ -138,9 +134,6 @@ abstract class SimpleBlock(props: Properties = Properties.of(Material.METAL).str override def canBeReplacedByLeaves(state: BlockState, world: IWorldReader, pos: BlockPos): Boolean = false - @Deprecated - def isValidSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, `type`: EntityType[_]): Boolean = false - def getValidRotations(world: World, pos: BlockPos): Array[Direction] = validRotations_ @Deprecated diff --git a/src/main/scala/li/cil/oc/common/block/Transposer.scala b/src/main/scala/li/cil/oc/common/block/Transposer.scala index 54a32daaca..a54d894fb6 100644 --- a/src/main/scala/li/cil/oc/common/block/Transposer.scala +++ b/src/main/scala/li/cil/oc/common/block/Transposer.scala @@ -1,12 +1,13 @@ package li.cil.oc.common.block import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraft.world.World -class Transposer extends SimpleBlock { +class Transposer(props: Properties) extends SimpleBlock(props) { override def newBlockEntity(world: IBlockReader) = new tileentity.Transposer(tileentity.TileEntityTypes.TRANSPOSER) } diff --git a/src/main/scala/li/cil/oc/common/block/Waypoint.scala b/src/main/scala/li/cil/oc/common/block/Waypoint.scala index b8f3a784d7..e615ab026a 100644 --- a/src/main/scala/li/cil/oc/common/block/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/block/Waypoint.scala @@ -5,6 +5,7 @@ import li.cil.oc.client.gui import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.util.RotationHelper +import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.Minecraft @@ -17,7 +18,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.world.{IBlockReader, World} -class Waypoint extends RedstoneAware { +class Waypoint(props: Properties) extends RedstoneAware(props) { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Pitch, PropertyRotatable.Yaw) diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index f16fff3197..b2e011ef8c 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -5,55 +5,58 @@ import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.common.block._ import li.cil.oc.common.tileentity +import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.material.Material import net.minecraft.tileentity.TileEntity import net.minecraftforge.fml.common.registry.GameRegistry object Blocks { def init() { - Items.registerBlock(new Adapter(), Constants.BlockName.Adapter) - Items.registerBlock(new Assembler(), Constants.BlockName.Assembler) - Items.registerBlock(new Cable(), Constants.BlockName.Cable) - Items.registerBlock(new Capacitor(), Constants.BlockName.Capacitor) - Items.registerBlock(new Case(Tier.One), Constants.BlockName.CaseTier1) - Items.registerBlock(new Case(Tier.Three), Constants.BlockName.CaseTier3) - Items.registerBlock(new Case(Tier.Two), Constants.BlockName.CaseTier2) - Items.registerBlock(new ChameliumBlock(), Constants.BlockName.ChameliumBlock) - Items.registerBlock(new Charger(), Constants.BlockName.Charger) - Items.registerBlock(new Disassembler(), Constants.BlockName.Disassembler) - Items.registerBlock(new DiskDrive(), Constants.BlockName.DiskDrive) - Items.registerBlock(new Geolyzer(), Constants.BlockName.Geolyzer) - Items.registerBlock(new Hologram(Tier.One), Constants.BlockName.HologramTier1) - Items.registerBlock(new Hologram(Tier.Two), Constants.BlockName.HologramTier2) - Items.registerBlock(new Keyboard(), Constants.BlockName.Keyboard) - Items.registerBlock(new MotionSensor(), Constants.BlockName.MotionSensor) - Items.registerBlock(new PowerConverter(), Constants.BlockName.PowerConverter) - Items.registerBlock(new PowerDistributor(), Constants.BlockName.PowerDistributor) - Items.registerBlock(new Printer(), Constants.BlockName.Printer) - Items.registerBlock(new Raid(), Constants.BlockName.Raid) - Items.registerBlock(new Redstone(), Constants.BlockName.Redstone) - Items.registerBlock(new Relay(), Constants.BlockName.Relay) - Items.registerBlock(new Screen(Tier.One), Constants.BlockName.ScreenTier1) - Items.registerBlock(new Screen(Tier.Three), Constants.BlockName.ScreenTier3) - Items.registerBlock(new Screen(Tier.Two), Constants.BlockName.ScreenTier2) - Items.registerBlock(new Rack(), Constants.BlockName.Rack) - Items.registerBlock(new Waypoint(), Constants.BlockName.Waypoint) + def defaultProps = Properties.of(Material.METAL).strength(2, 5) + Items.registerBlock(new Adapter(defaultProps), Constants.BlockName.Adapter) + Items.registerBlock(new Assembler(defaultProps), Constants.BlockName.Assembler) + Items.registerBlock(new Cable(defaultProps), Constants.BlockName.Cable) + Items.registerBlock(new Capacitor(defaultProps), Constants.BlockName.Capacitor) + Items.registerBlock(new Case(defaultProps, Tier.One), Constants.BlockName.CaseTier1) + Items.registerBlock(new Case(defaultProps, Tier.Three), Constants.BlockName.CaseTier3) + Items.registerBlock(new Case(defaultProps, Tier.Two), Constants.BlockName.CaseTier2) + Items.registerBlock(new ChameliumBlock(Properties.of(Material.STONE).strength(2, 5)), Constants.BlockName.ChameliumBlock) + Items.registerBlock(new Charger(defaultProps), Constants.BlockName.Charger) + Items.registerBlock(new Disassembler(defaultProps), Constants.BlockName.Disassembler) + Items.registerBlock(new DiskDrive(defaultProps), Constants.BlockName.DiskDrive) + Items.registerBlock(new Geolyzer(defaultProps), Constants.BlockName.Geolyzer) + Items.registerBlock(new Hologram(defaultProps, Tier.One), Constants.BlockName.HologramTier1) + Items.registerBlock(new Hologram(defaultProps, Tier.Two), Constants.BlockName.HologramTier2) + Items.registerBlock(new Keyboard(Properties.of(Material.STONE).strength(2, 5).noOcclusion), Constants.BlockName.Keyboard) + Items.registerBlock(new MotionSensor(defaultProps), Constants.BlockName.MotionSensor) + Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter) + Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor) + Items.registerBlock(new Printer(defaultProps), Constants.BlockName.Printer) + Items.registerBlock(new Raid(defaultProps), Constants.BlockName.Raid) + Items.registerBlock(new Redstone(defaultProps), Constants.BlockName.Redstone) + Items.registerBlock(new Relay(defaultProps), Constants.BlockName.Relay) + Items.registerBlock(new Screen(defaultProps, Tier.One), Constants.BlockName.ScreenTier1) + Items.registerBlock(new Screen(defaultProps, Tier.Three), Constants.BlockName.ScreenTier3) + Items.registerBlock(new Screen(defaultProps, Tier.Two), Constants.BlockName.ScreenTier2) + Items.registerBlock(new Rack(defaultProps), Constants.BlockName.Rack) + Items.registerBlock(new Waypoint(defaultProps), Constants.BlockName.Waypoint) - Items.registerBlock(new Case(Tier.Four), Constants.BlockName.CaseCreative) - Items.registerBlock(new Microcontroller(), Constants.BlockName.Microcontroller) - Items.registerBlock(new Print(), Constants.BlockName.Print) - Items.registerBlock(new RobotAfterimage(), Constants.BlockName.RobotAfterimage) - Items.registerBlock(new RobotProxy(), Constants.BlockName.Robot) + Items.registerBlock(new Case(defaultProps, Tier.Four), Constants.BlockName.CaseCreative) + Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller) + Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print) + Items.registerBlock(new RobotAfterimage(Properties.of(Material.AIR).instabreak.noOcclusion.dynamicShape.air), Constants.BlockName.RobotAfterimage) + Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot) // v1.5.10 - Items.registerBlock(new FakeEndstone(), Constants.BlockName.Endstone) + Items.registerBlock(new FakeEndstone(Properties.of(Material.STONE).strength(3, 15)), Constants.BlockName.Endstone) // v1.5.14 - Items.registerBlock(new NetSplitter(), Constants.BlockName.NetSplitter) + Items.registerBlock(new NetSplitter(defaultProps), Constants.BlockName.NetSplitter) // v1.5.16 - Items.registerBlock(new Transposer(), Constants.BlockName.Transposer) + Items.registerBlock(new Transposer(defaultProps), Constants.BlockName.Transposer) // v1.7.2 - Items.registerBlock(new CarpetedCapacitor(), Constants.BlockName.CarpetedCapacitor) + Items.registerBlock(new CarpetedCapacitor(defaultProps), Constants.BlockName.CarpetedCapacitor) } } From c798008fb22fb8a4ba94457b0e482b031d11b512 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:30:14 +0200 Subject: [PATCH 100/159] Move item property definitions into Items class --- .../scala/li/cil/oc/common/block/Item.scala | 6 +- .../scala/li/cil/oc/common/init/Items.scala | 256 +++++++++--------- .../scala/li/cil/oc/common/item/ALU.scala | 3 +- .../scala/li/cil/oc/common/item/APU.scala | 3 +- .../scala/li/cil/oc/common/item/Acid.scala | 3 +- .../li/cil/oc/common/item/Analyzer.scala | 3 +- .../li/cil/oc/common/item/ArrowKeys.scala | 3 +- .../li/cil/oc/common/item/ButtonGroup.scala | 3 +- .../scala/li/cil/oc/common/item/CPU.scala | 3 +- .../li/cil/oc/common/item/CardBase.scala | 3 +- .../li/cil/oc/common/item/Chamelium.scala | 3 +- .../li/cil/oc/common/item/CircuitBoard.scala | 3 +- .../li/cil/oc/common/item/ComponentBus.scala | 3 +- .../li/cil/oc/common/item/ControlUnit.scala | 3 +- .../li/cil/oc/common/item/CustomModel.scala | 1 - .../li/cil/oc/common/item/CuttingWire.scala | 3 +- .../li/cil/oc/common/item/DataCard.scala | 3 +- .../li/cil/oc/common/item/DebugCard.scala | 3 +- .../li/cil/oc/common/item/Debugger.scala | 3 +- .../li/cil/oc/common/item/DiamondChip.scala | 3 +- .../scala/li/cil/oc/common/item/Disk.scala | 3 +- .../oc/common/item/DiskDriveMountable.scala | 3 +- .../scala/li/cil/oc/common/item/Drone.scala | 3 +- .../li/cil/oc/common/item/DroneCase.scala | 3 +- .../scala/li/cil/oc/common/item/EEPROM.scala | 3 +- .../li/cil/oc/common/item/FloppyDisk.scala | 3 +- .../li/cil/oc/common/item/GraphicsCard.scala | 3 +- .../li/cil/oc/common/item/HardDiskDrive.scala | 3 +- .../li/cil/oc/common/item/HoverBoots.scala | 5 +- .../li/cil/oc/common/item/InkCartridge.scala | 6 +- .../oc/common/item/InkCartridgeEmpty.scala | 3 +- .../li/cil/oc/common/item/InternetCard.scala | 3 +- .../li/cil/oc/common/item/Interweb.scala | 3 +- .../li/cil/oc/common/item/LinkedCard.scala | 3 +- .../scala/li/cil/oc/common/item/Manual.scala | 3 +- .../scala/li/cil/oc/common/item/Memory.scala | 3 +- .../li/cil/oc/common/item/Microchip.scala | 3 +- .../oc/common/item/MicrocontrollerCase.scala | 3 +- .../li/cil/oc/common/item/Nanomachines.scala | 3 +- .../li/cil/oc/common/item/NetworkCard.scala | 3 +- .../scala/li/cil/oc/common/item/NumPad.scala | 3 +- .../scala/li/cil/oc/common/item/Present.scala | 3 +- .../oc/common/item/PrintedCircuitBoard.scala | 3 +- .../cil/oc/common/item/RawCircuitBoard.scala | 3 +- .../li/cil/oc/common/item/RedstoneCard.scala | 3 +- .../scala/li/cil/oc/common/item/Server.scala | 3 +- .../scala/li/cil/oc/common/item/Tablet.scala | 3 +- .../li/cil/oc/common/item/TabletCase.scala | 3 +- .../li/cil/oc/common/item/Terminal.scala | 3 +- .../cil/oc/common/item/TerminalServer.scala | 3 +- .../li/cil/oc/common/item/TexturePicker.scala | 3 +- .../li/cil/oc/common/item/Transistor.scala | 3 +- .../li/cil/oc/common/item/UpgradeAngel.scala | 3 +- .../cil/oc/common/item/UpgradeBattery.scala | 3 +- .../oc/common/item/UpgradeChunkloader.scala | 3 +- .../oc/common/item/UpgradeContainerCard.scala | 3 +- .../common/item/UpgradeContainerUpgrade.scala | 3 +- .../cil/oc/common/item/UpgradeCrafting.scala | 3 +- .../cil/oc/common/item/UpgradeDatabase.scala | 3 +- .../oc/common/item/UpgradeExperience.scala | 3 +- .../cil/oc/common/item/UpgradeGenerator.scala | 3 +- .../li/cil/oc/common/item/UpgradeHover.scala | 3 +- .../cil/oc/common/item/UpgradeInventory.scala | 3 +- .../item/UpgradeInventoryController.scala | 3 +- .../li/cil/oc/common/item/UpgradeLeash.scala | 3 +- .../li/cil/oc/common/item/UpgradeMF.scala | 3 +- .../oc/common/item/UpgradeNavigation.scala | 3 +- .../li/cil/oc/common/item/UpgradePiston.scala | 3 +- .../li/cil/oc/common/item/UpgradeSign.scala | 3 +- .../common/item/UpgradeSolarGenerator.scala | 3 +- .../oc/common/item/UpgradeStickyPiston.scala | 3 +- .../li/cil/oc/common/item/UpgradeTank.scala | 4 +- .../common/item/UpgradeTankController.scala | 3 +- .../oc/common/item/UpgradeTractorBeam.scala | 3 +- .../cil/oc/common/item/UpgradeTrading.scala | 3 +- .../oc/common/item/WirelessNetworkCard.scala | 4 +- .../scala/li/cil/oc/common/item/Wrench.scala | 9 +- .../oc/common/item/traits/SimpleItem.scala | 1 - 78 files changed, 207 insertions(+), 292 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 910c3054b6..14985222c4 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -12,8 +12,6 @@ import li.cil.oc.util.Color import li.cil.oc.util.ItemColorizer import net.minecraft.block.Block import net.minecraft.block.BlockState -import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.BlockItem import net.minecraft.item.BlockItemUseContext import net.minecraft.item.DyeColor @@ -21,11 +19,9 @@ import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraft.item.Rarity import net.minecraft.util.Direction -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent -import net.minecraft.world.World object Item { def setCreativeTab(block: Block, props: Properties): Properties = block match { @@ -35,7 +31,7 @@ object Item { } } -class Item(value: Block, props: Properties = new Properties()) extends BlockItem(value, Item.setCreativeTab(value, props)) { +class Item(value: Block, props: Properties) extends BlockItem(value, Item.setCreativeTab(value, props)) { override def getRarity(stack: ItemStack): Rarity = getBlock match { case simple: SimpleBlock => simple.rarity(stack) case _ => Rarity.COMMON diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 8f2b68037b..f2972130df 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.init import java.util.concurrent.Callable import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api.detail.ItemAPI @@ -21,15 +22,17 @@ import li.cil.oc.common.item.data.TabletData import li.cil.oc.common.item.traits.SimpleItem import li.cil.oc.server.machine.luac.LuaStateFactory import net.minecraft.block.Block -import net.minecraft.item.ItemGroup +import net.minecraft.item.BlockItem import net.minecraft.item.DyeColor import net.minecraft.item.Item -import net.minecraft.item.BlockItem +import net.minecraft.item.Item.Properties +import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT import net.minecraft.util.NonNullList import net.minecraft.util.ResourceLocation -import net.minecraftforge.registries.{GameData} +import net.minecraftforge.common.ToolType +import net.minecraftforge.registries.GameData import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -51,6 +54,8 @@ object Items extends ItemAPI { case _ => null } + private def defaultProps = new Properties().tab(CreativeTab) + def registerBlock(instance: Block, id: String): Block = { if (!descriptors.contains(id)) { instance match { @@ -59,7 +64,8 @@ object Items extends ItemAPI { simple.setRegistryName(OpenComputers.ID, id) GameData.register_impl[Block](simple) - val item : Item = new common.block.Item(simple) + val props = defaultProps.tab(simple.getCreativeTab) + val item : Item = new common.block.Item(simple, props) item.setRegistryName(OpenComputers.ID, id) GameData.register_impl(item) OpenComputers.proxy.registerModel(item, id) @@ -317,165 +323,167 @@ object Items extends ItemAPI { // Crafting materials. private def initMaterials(): Unit = { - registerItem(new item.CuttingWire(), Constants.ItemName.CuttingWire) - registerItem(new item.Acid(), Constants.ItemName.Acid) - registerItem(new item.RawCircuitBoard(), Constants.ItemName.RawCircuitBoard) - registerItem(new item.CircuitBoard(), Constants.ItemName.CircuitBoard) - registerItem(new item.PrintedCircuitBoard(), Constants.ItemName.PrintedCircuitBoard) - registerItem(new item.CardBase(), Constants.ItemName.Card) - registerItem(new item.Transistor(), Constants.ItemName.Transistor) - registerItem(new item.Microchip(Tier.One), Constants.ItemName.ChipTier1) - registerItem(new item.Microchip(Tier.Two), Constants.ItemName.ChipTier2) - registerItem(new item.Microchip(Tier.Three), Constants.ItemName.ChipTier3) - registerItem(new item.ALU(), Constants.ItemName.Alu) - registerItem(new item.ControlUnit(), Constants.ItemName.ControlUnit) - registerItem(new item.Disk(), Constants.ItemName.Disk) - registerItem(new item.Interweb(), Constants.ItemName.Interweb) - registerItem(new item.ButtonGroup(), Constants.ItemName.ButtonGroup) - registerItem(new item.ArrowKeys(), Constants.ItemName.ArrowKeys) - registerItem(new item.NumPad(), Constants.ItemName.NumPad) - - registerItem(new item.TabletCase(Tier.One), Constants.ItemName.TabletCaseTier1) - registerItem(new item.TabletCase(Tier.Two), Constants.ItemName.TabletCaseTier2) - registerItem(new item.TabletCase(Tier.Four), Constants.ItemName.TabletCaseCreative) - registerItem(new item.MicrocontrollerCase(Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) - registerItem(new item.MicrocontrollerCase(Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) - registerItem(new item.MicrocontrollerCase(Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) - registerItem(new item.DroneCase(Tier.One), Constants.ItemName.DroneCaseTier1) - registerItem(new item.DroneCase(Tier.Two), Constants.ItemName.DroneCaseTier2) - registerItem(new item.DroneCase(Tier.Four), Constants.ItemName.DroneCaseCreative) - - registerItem(new item.InkCartridgeEmpty(), Constants.ItemName.InkCartridgeEmpty) - registerItem(new item.InkCartridge(), Constants.ItemName.InkCartridge) - registerItem(new item.Chamelium(), Constants.ItemName.Chamelium) - - registerItem(new item.DiamondChip(), Constants.ItemName.DiamondChip) + registerItem(new item.CuttingWire(defaultProps), Constants.ItemName.CuttingWire) + registerItem(new item.Acid(defaultProps), Constants.ItemName.Acid) + registerItem(new item.RawCircuitBoard(defaultProps), Constants.ItemName.RawCircuitBoard) + registerItem(new item.CircuitBoard(defaultProps), Constants.ItemName.CircuitBoard) + registerItem(new item.PrintedCircuitBoard(defaultProps), Constants.ItemName.PrintedCircuitBoard) + registerItem(new item.CardBase(defaultProps), Constants.ItemName.Card) + registerItem(new item.Transistor(defaultProps), Constants.ItemName.Transistor) + registerItem(new item.Microchip(defaultProps, Tier.One), Constants.ItemName.ChipTier1) + registerItem(new item.Microchip(defaultProps, Tier.Two), Constants.ItemName.ChipTier2) + registerItem(new item.Microchip(defaultProps, Tier.Three), Constants.ItemName.ChipTier3) + registerItem(new item.ALU(defaultProps), Constants.ItemName.Alu) + registerItem(new item.ControlUnit(defaultProps), Constants.ItemName.ControlUnit) + registerItem(new item.Disk(defaultProps), Constants.ItemName.Disk) + registerItem(new item.Interweb(defaultProps), Constants.ItemName.Interweb) + registerItem(new item.ButtonGroup(defaultProps), Constants.ItemName.ButtonGroup) + registerItem(new item.ArrowKeys(defaultProps), Constants.ItemName.ArrowKeys) + registerItem(new item.NumPad(defaultProps), Constants.ItemName.NumPad) + + registerItem(new item.TabletCase(defaultProps, Tier.One), Constants.ItemName.TabletCaseTier1) + registerItem(new item.TabletCase(defaultProps, Tier.Two), Constants.ItemName.TabletCaseTier2) + registerItem(new item.TabletCase(defaultProps, Tier.Four), Constants.ItemName.TabletCaseCreative) + registerItem(new item.MicrocontrollerCase(defaultProps, Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) + registerItem(new item.MicrocontrollerCase(defaultProps, Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) + registerItem(new item.MicrocontrollerCase(defaultProps, Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) + registerItem(new item.DroneCase(defaultProps, Tier.One), Constants.ItemName.DroneCaseTier1) + registerItem(new item.DroneCase(defaultProps, Tier.Two), Constants.ItemName.DroneCaseTier2) + registerItem(new item.DroneCase(defaultProps, Tier.Four), Constants.ItemName.DroneCaseCreative) + + registerItem(new item.InkCartridgeEmpty(defaultProps), Constants.ItemName.InkCartridgeEmpty) + registerItem(new item.InkCartridge(defaultProps.craftRemainder(get(Constants.ItemName.InkCartridgeEmpty).item)), Constants.ItemName.InkCartridge) + registerItem(new item.Chamelium(defaultProps), Constants.ItemName.Chamelium) + + registerItem(new item.DiamondChip(defaultProps), Constants.ItemName.DiamondChip) } + val WrenchType: ToolType = ToolType.get("wrench") + // All kinds of tools. private def initTools(): Unit = { - registerItem(new item.Analyzer(), Constants.ItemName.Analyzer) - registerItem(new item.Debugger(), Constants.ItemName.Debugger) - registerItem(new item.Terminal(), Constants.ItemName.Terminal) - registerItem(new item.TexturePicker(), Constants.ItemName.TexturePicker) - registerItem(new item.Manual(), Constants.ItemName.Manual) - registerItem(new item.Wrench(), Constants.ItemName.Wrench) + registerItem(new item.Analyzer(defaultProps), Constants.ItemName.Analyzer) + registerItem(new item.Debugger(defaultProps), Constants.ItemName.Debugger) + registerItem(new item.Terminal(defaultProps), Constants.ItemName.Terminal) + registerItem(new item.TexturePicker(defaultProps), Constants.ItemName.TexturePicker) + registerItem(new item.Manual(defaultProps), Constants.ItemName.Manual) + registerItem(new item.Wrench(defaultProps.stacksTo(1).addToolType(WrenchType, 1)), Constants.ItemName.Wrench) // 1.5.11 - registerItem(new item.HoverBoots(), Constants.ItemName.HoverBoots) + registerItem(new item.HoverBoots(defaultProps.setNoRepair), Constants.ItemName.HoverBoots) // 1.5.18 - registerItem(new item.Nanomachines(), Constants.ItemName.Nanomachines) + registerItem(new item.Nanomachines(defaultProps), Constants.ItemName.Nanomachines) } // General purpose components. private def initComponents(): Unit = { - registerItem(new item.CPU(Tier.One), Constants.ItemName.CPUTier1) - registerItem(new item.CPU(Tier.Two), Constants.ItemName.CPUTier2) - registerItem(new item.CPU(Tier.Three), Constants.ItemName.CPUTier3) - - registerItem(new item.ComponentBus(Tier.One), Constants.ItemName.ComponentBusTier1) - registerItem(new item.ComponentBus(Tier.Two), Constants.ItemName.ComponentBusTier2) - registerItem(new item.ComponentBus(Tier.Three), Constants.ItemName.ComponentBusTier3) - - registerItem(new item.Memory(Tier.One), Constants.ItemName.RAMTier1) - registerItem(new item.Memory(Tier.Two), Constants.ItemName.RAMTier2) - registerItem(new item.Memory(Tier.Three), Constants.ItemName.RAMTier3) - registerItem(new item.Memory(Tier.Four), Constants.ItemName.RAMTier4) - registerItem(new item.Memory(Tier.Five), Constants.ItemName.RAMTier5) - registerItem(new item.Memory(Tier.Six), Constants.ItemName.RAMTier6) - - registerItem(new item.Server(Tier.Four), Constants.ItemName.ServerCreative) - registerItem(new item.Server(Tier.One), Constants.ItemName.ServerTier1) - registerItem(new item.Server(Tier.Two), Constants.ItemName.ServerTier2) - registerItem(new item.Server(Tier.Three), Constants.ItemName.ServerTier3) + registerItem(new item.CPU(defaultProps, Tier.One), Constants.ItemName.CPUTier1) + registerItem(new item.CPU(defaultProps, Tier.Two), Constants.ItemName.CPUTier2) + registerItem(new item.CPU(defaultProps, Tier.Three), Constants.ItemName.CPUTier3) + + registerItem(new item.ComponentBus(defaultProps, Tier.One), Constants.ItemName.ComponentBusTier1) + registerItem(new item.ComponentBus(defaultProps, Tier.Two), Constants.ItemName.ComponentBusTier2) + registerItem(new item.ComponentBus(defaultProps, Tier.Three), Constants.ItemName.ComponentBusTier3) + + registerItem(new item.Memory(defaultProps, Tier.One), Constants.ItemName.RAMTier1) + registerItem(new item.Memory(defaultProps, Tier.Two), Constants.ItemName.RAMTier2) + registerItem(new item.Memory(defaultProps, Tier.Three), Constants.ItemName.RAMTier3) + registerItem(new item.Memory(defaultProps, Tier.Four), Constants.ItemName.RAMTier4) + registerItem(new item.Memory(defaultProps, Tier.Five), Constants.ItemName.RAMTier5) + registerItem(new item.Memory(defaultProps, Tier.Six), Constants.ItemName.RAMTier6) + + registerItem(new item.Server(defaultProps, Tier.Four), Constants.ItemName.ServerCreative) + registerItem(new item.Server(defaultProps, Tier.One), Constants.ItemName.ServerTier1) + registerItem(new item.Server(defaultProps, Tier.Two), Constants.ItemName.ServerTier2) + registerItem(new item.Server(defaultProps, Tier.Three), Constants.ItemName.ServerTier3) // 1.5.10 - registerItem(new item.APU(Tier.One), Constants.ItemName.APUTier1) - registerItem(new item.APU(Tier.Two), Constants.ItemName.APUTier2) + registerItem(new item.APU(defaultProps, Tier.One), Constants.ItemName.APUTier1) + registerItem(new item.APU(defaultProps, Tier.Two), Constants.ItemName.APUTier2) // 1.5.12 - registerItem(new item.APU(Tier.Three), Constants.ItemName.APUCreative) + registerItem(new item.APU(defaultProps, Tier.Three), Constants.ItemName.APUCreative) // 1.6 - registerItem(new item.TerminalServer(), Constants.ItemName.TerminalServer) - registerItem(new item.DiskDriveMountable(), Constants.ItemName.DiskDriveMountable) + registerItem(new item.TerminalServer(defaultProps), Constants.ItemName.TerminalServer) + registerItem(new item.DiskDriveMountable(defaultProps), Constants.ItemName.DiskDriveMountable) } // Card components. private def initCards(): Unit = { - registerItem(new item.DebugCard(), Constants.ItemName.DebugCard) - registerItem(new item.GraphicsCard(Tier.One), Constants.ItemName.GraphicsCardTier1) - registerItem(new item.GraphicsCard(Tier.Two), Constants.ItemName.GraphicsCardTier2) - registerItem(new item.GraphicsCard(Tier.Three), Constants.ItemName.GraphicsCardTier3) - registerItem(new item.RedstoneCard(Tier.One), Constants.ItemName.RedstoneCardTier1) - registerItem(new item.RedstoneCard(Tier.Two), Constants.ItemName.RedstoneCardTier2) - registerItem(new item.NetworkCard(), Constants.ItemName.NetworkCard) - registerItem(new item.WirelessNetworkCard(Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) - registerItem(new item.InternetCard(), Constants.ItemName.InternetCard) - registerItem(new item.LinkedCard(), Constants.ItemName.LinkedCard) + registerItem(new item.DebugCard(defaultProps), Constants.ItemName.DebugCard) + registerItem(new item.GraphicsCard(defaultProps, Tier.One), Constants.ItemName.GraphicsCardTier1) + registerItem(new item.GraphicsCard(defaultProps, Tier.Two), Constants.ItemName.GraphicsCardTier2) + registerItem(new item.GraphicsCard(defaultProps, Tier.Three), Constants.ItemName.GraphicsCardTier3) + registerItem(new item.RedstoneCard(defaultProps, Tier.One), Constants.ItemName.RedstoneCardTier1) + registerItem(new item.RedstoneCard(defaultProps, Tier.Two), Constants.ItemName.RedstoneCardTier2) + registerItem(new item.NetworkCard(defaultProps), Constants.ItemName.NetworkCard) + registerItem(new item.WirelessNetworkCard(defaultProps, Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) + registerItem(new item.InternetCard(defaultProps), Constants.ItemName.InternetCard) + registerItem(new item.LinkedCard(defaultProps), Constants.ItemName.LinkedCard) // 1.5.13 - registerItem(new item.DataCard(Tier.One), Constants.ItemName.DataCardTier1) + registerItem(new item.DataCard(defaultProps, Tier.One), Constants.ItemName.DataCardTier1) // 1.5.15 - registerItem(new item.DataCard(Tier.Two), Constants.ItemName.DataCardTier2) - registerItem(new item.DataCard(Tier.Three), Constants.ItemName.DataCardTier3) + registerItem(new item.DataCard(defaultProps, Tier.Two), Constants.ItemName.DataCardTier2) + registerItem(new item.DataCard(defaultProps, Tier.Three), Constants.ItemName.DataCardTier3) } // Upgrade components. private def initUpgrades(): Unit = { - registerItem(new item.UpgradeAngel(), Constants.ItemName.AngelUpgrade) - registerItem(new item.UpgradeBattery(Tier.One), Constants.ItemName.BatteryUpgradeTier1) - registerItem(new item.UpgradeBattery(Tier.Two), Constants.ItemName.BatteryUpgradeTier2) - registerItem(new item.UpgradeBattery(Tier.Three), Constants.ItemName.BatteryUpgradeTier3) - registerItem(new item.UpgradeChunkloader(), Constants.ItemName.ChunkloaderUpgrade) - registerItem(new item.UpgradeContainerCard(Tier.One), Constants.ItemName.CardContainerTier1) - registerItem(new item.UpgradeContainerCard(Tier.Two), Constants.ItemName.CardContainerTier2) - registerItem(new item.UpgradeContainerCard(Tier.Three), Constants.ItemName.CardContainerTier3) - registerItem(new item.UpgradeContainerUpgrade(Tier.One), Constants.ItemName.UpgradeContainerTier1) - registerItem(new item.UpgradeContainerUpgrade(Tier.Two), Constants.ItemName.UpgradeContainerTier2) - registerItem(new item.UpgradeContainerUpgrade(Tier.Three), Constants.ItemName.UpgradeContainerTier3) - registerItem(new item.UpgradeCrafting(), Constants.ItemName.CraftingUpgrade) - registerItem(new item.UpgradeDatabase(Tier.One), Constants.ItemName.DatabaseUpgradeTier1) - registerItem(new item.UpgradeDatabase(Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) - registerItem(new item.UpgradeDatabase(Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) - registerItem(new item.UpgradeExperience(), Constants.ItemName.ExperienceUpgrade) - registerItem(new item.UpgradeGenerator(), Constants.ItemName.GeneratorUpgrade) - registerItem(new item.UpgradeInventory(), Constants.ItemName.InventoryUpgrade) - registerItem(new item.UpgradeInventoryController(), Constants.ItemName.InventoryControllerUpgrade) - registerItem(new item.UpgradeNavigation(), Constants.ItemName.NavigationUpgrade) - registerItem(new item.UpgradePiston(), Constants.ItemName.PistonUpgrade) - registerItem(new item.UpgradeSign(), Constants.ItemName.SignUpgrade) - registerItem(new item.UpgradeSolarGenerator(), Constants.ItemName.SolarGeneratorUpgrade) - registerItem(new item.UpgradeTank(), Constants.ItemName.TankUpgrade) - registerItem(new item.UpgradeTankController(), Constants.ItemName.TankControllerUpgrade) - registerItem(new item.UpgradeTractorBeam(), Constants.ItemName.TractorBeamUpgrade) - registerItem(new item.UpgradeLeash(), Constants.ItemName.LeashUpgrade) + registerItem(new item.UpgradeAngel(defaultProps), Constants.ItemName.AngelUpgrade) + registerItem(new item.UpgradeBattery(defaultProps, Tier.One), Constants.ItemName.BatteryUpgradeTier1) + registerItem(new item.UpgradeBattery(defaultProps, Tier.Two), Constants.ItemName.BatteryUpgradeTier2) + registerItem(new item.UpgradeBattery(defaultProps, Tier.Three), Constants.ItemName.BatteryUpgradeTier3) + registerItem(new item.UpgradeChunkloader(defaultProps), Constants.ItemName.ChunkloaderUpgrade) + registerItem(new item.UpgradeContainerCard(defaultProps, Tier.One), Constants.ItemName.CardContainerTier1) + registerItem(new item.UpgradeContainerCard(defaultProps, Tier.Two), Constants.ItemName.CardContainerTier2) + registerItem(new item.UpgradeContainerCard(defaultProps, Tier.Three), Constants.ItemName.CardContainerTier3) + registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.One), Constants.ItemName.UpgradeContainerTier1) + registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.Two), Constants.ItemName.UpgradeContainerTier2) + registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.Three), Constants.ItemName.UpgradeContainerTier3) + registerItem(new item.UpgradeCrafting(defaultProps), Constants.ItemName.CraftingUpgrade) + registerItem(new item.UpgradeDatabase(defaultProps, Tier.One), Constants.ItemName.DatabaseUpgradeTier1) + registerItem(new item.UpgradeDatabase(defaultProps, Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) + registerItem(new item.UpgradeDatabase(defaultProps, Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) + registerItem(new item.UpgradeExperience(defaultProps), Constants.ItemName.ExperienceUpgrade) + registerItem(new item.UpgradeGenerator(defaultProps), Constants.ItemName.GeneratorUpgrade) + registerItem(new item.UpgradeInventory(defaultProps), Constants.ItemName.InventoryUpgrade) + registerItem(new item.UpgradeInventoryController(defaultProps), Constants.ItemName.InventoryControllerUpgrade) + registerItem(new item.UpgradeNavigation(defaultProps), Constants.ItemName.NavigationUpgrade) + registerItem(new item.UpgradePiston(defaultProps), Constants.ItemName.PistonUpgrade) + registerItem(new item.UpgradeSign(defaultProps), Constants.ItemName.SignUpgrade) + registerItem(new item.UpgradeSolarGenerator(defaultProps), Constants.ItemName.SolarGeneratorUpgrade) + registerItem(new item.UpgradeTank(defaultProps), Constants.ItemName.TankUpgrade) + registerItem(new item.UpgradeTankController(defaultProps), Constants.ItemName.TankControllerUpgrade) + registerItem(new item.UpgradeTractorBeam(defaultProps), Constants.ItemName.TractorBeamUpgrade) + registerItem(new item.UpgradeLeash(defaultProps), Constants.ItemName.LeashUpgrade) // 1.5.8 - registerItem(new item.UpgradeHover(Tier.One), Constants.ItemName.HoverUpgradeTier1) - registerItem(new item.UpgradeHover(Tier.Two), Constants.ItemName.HoverUpgradeTier2) + registerItem(new item.UpgradeHover(defaultProps, Tier.One), Constants.ItemName.HoverUpgradeTier1) + registerItem(new item.UpgradeHover(defaultProps, Tier.Two), Constants.ItemName.HoverUpgradeTier2) // 1.6 - registerItem(new item.UpgradeTrading(), Constants.ItemName.TradingUpgrade) - registerItem(new item.UpgradeMF(), Constants.ItemName.MFU) + registerItem(new item.UpgradeTrading(defaultProps), Constants.ItemName.TradingUpgrade) + registerItem(new item.UpgradeMF(defaultProps), Constants.ItemName.MFU) // 1.7.2 - registerItem(new item.WirelessNetworkCard(Tier.One), Constants.ItemName.WirelessNetworkCardTier1) - registerItem(new item.ComponentBus(Tier.Four), Constants.ItemName.ComponentBusCreative) + registerItem(new item.WirelessNetworkCard(defaultProps, Tier.One), Constants.ItemName.WirelessNetworkCardTier1) + registerItem(new item.ComponentBus(defaultProps, Tier.Four), Constants.ItemName.ComponentBusCreative) // 1.8 - registerItem(new item.UpgradeStickyPiston(), Constants.ItemName.StickyPistonUpgrade) + registerItem(new item.UpgradeStickyPiston(defaultProps), Constants.ItemName.StickyPistonUpgrade) } // Storage media of all kinds. private def initStorage(): Unit = { - registerItem(new item.EEPROM(), Constants.ItemName.EEPROM) - registerItem(new item.FloppyDisk(), Constants.ItemName.Floppy) - registerItem(new item.HardDiskDrive(Tier.One), Constants.ItemName.HDDTier1) - registerItem(new item.HardDiskDrive(Tier.Two), Constants.ItemName.HDDTier2) - registerItem(new item.HardDiskDrive(Tier.Three), Constants.ItemName.HDDTier3) + registerItem(new item.EEPROM(defaultProps), Constants.ItemName.EEPROM) + registerItem(new item.FloppyDisk(defaultProps), Constants.ItemName.Floppy) + registerItem(new item.HardDiskDrive(defaultProps, Tier.One), Constants.ItemName.HDDTier1) + registerItem(new item.HardDiskDrive(defaultProps, Tier.Two), Constants.ItemName.HDDTier2) + registerItem(new item.HardDiskDrive(defaultProps, Tier.Three), Constants.ItemName.HDDTier3) val luaBios = { val code = new Array[Byte](4 * 1024) @@ -488,9 +496,9 @@ object Items extends ItemAPI { // Special purpose items that don't fit into any other category. private def initSpecial(): Unit = { - registerItem(new item.Tablet(), Constants.ItemName.Tablet) - registerItem(new item.Drone(), Constants.ItemName.Drone) - registerItem(new item.Present(), Constants.ItemName.Present) + registerItem(new item.Tablet(defaultProps), Constants.ItemName.Tablet) + registerItem(new item.Drone(defaultProps), Constants.ItemName.Drone) + registerItem(new item.Present(defaultProps), Constants.ItemName.Present) } def decorateCreativeTab(list: NonNullList[ItemStack]) { diff --git a/src/main/scala/li/cil/oc/common/item/ALU.scala b/src/main/scala/li/cil/oc/common/item/ALU.scala index ea5cc930bc..804824109b 100644 --- a/src/main/scala/li/cil/oc/common/item/ALU.scala +++ b/src/main/scala/li/cil/oc/common/item/ALU.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class ALU(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class ALU(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/APU.scala b/src/main/scala/li/cil/oc/common/item/APU.scala index 88fe8adc4f..087e5f476a 100644 --- a/src/main/scala/li/cil/oc/common/item/APU.scala +++ b/src/main/scala/li/cil/oc/common/item/APU.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.common.Tier import li.cil.oc.util.Rarity import net.minecraft.item // Rarity @@ -11,7 +10,7 @@ import net.minecraftforge.common.extensions.IForgeItem import scala.language.existentials -class APU(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike with traits.GPULike { +class APU(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike with traits.GPULike { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Acid.scala b/src/main/scala/li/cil/oc/common/item/Acid.scala index 2a1550040e..a567e94d0c 100644 --- a/src/main/scala/li/cil/oc/common/item/Acid.scala +++ b/src/main/scala/li/cil/oc/common/item/Acid.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import javax.annotation.Nonnull -import li.cil.oc.CreativeTab import li.cil.oc.api import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity @@ -19,7 +18,7 @@ import net.minecraft.util.Hand import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class Acid(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Acid(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) diff --git a/src/main/scala/li/cil/oc/common/item/Analyzer.scala b/src/main/scala/li/cil/oc/common/item/Analyzer.scala index c2cf3837c2..89379cd5b7 100644 --- a/src/main/scala/li/cil/oc/common/item/Analyzer.scala +++ b/src/main/scala/li/cil/oc/common/item/Analyzer.scala @@ -1,7 +1,6 @@ package li.cil.oc.common.item import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api @@ -105,7 +104,7 @@ object Analyzer { } } -class Analyzer(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Analyzer(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (player.isCrouching && stack.hasTag) { stack.removeTagKey(Settings.namespace + "clipboard") diff --git a/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala b/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala index 89d151c78f..7a63055992 100644 --- a/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala +++ b/src/main/scala/li/cil/oc/common/item/ArrowKeys.scala @@ -1,10 +1,9 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class ArrowKeys(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class ArrowKeys(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipName = None } diff --git a/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala b/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala index bad6bc9fe6..030f51a438 100644 --- a/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala +++ b/src/main/scala/li/cil/oc/common/item/ButtonGroup.scala @@ -1,10 +1,9 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class ButtonGroup(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class ButtonGroup(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipName = None } diff --git a/src/main/scala/li/cil/oc/common/item/CPU.scala b/src/main/scala/li/cil/oc/common/item/CPU.scala index 8a2d5643af..9a7c6e4974 100644 --- a/src/main/scala/li/cil/oc/common/item/CPU.scala +++ b/src/main/scala/li/cil/oc/common/item/CPU.scala @@ -1,13 +1,12 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem import scala.language.existentials -class CPU(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike { +class CPU(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.CPULike { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/CardBase.scala b/src/main/scala/li/cil/oc/common/item/CardBase.scala index 6647eae72c..af0667686a 100644 --- a/src/main/scala/li/cil/oc/common/item/CardBase.scala +++ b/src/main/scala/li/cil/oc/common/item/CardBase.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class CardBase(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class CardBase(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/Chamelium.scala b/src/main/scala/li/cil/oc/common/item/Chamelium.scala index 2e2768bbc7..8be92fd7c0 100644 --- a/src/main/scala/li/cil/oc/common/item/Chamelium.scala +++ b/src/main/scala/li/cil/oc/common/item/Chamelium.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity @@ -17,7 +16,7 @@ import net.minecraft.util.Hand import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class Chamelium(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Chamelium(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (Settings.get.chameliumEdible) { player.startUsingItem(if (player.getItemInHand(Hand.MAIN_HAND) == stack) Hand.MAIN_HAND else Hand.OFF_HAND) diff --git a/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala index 3a69acccd7..b88c00c253 100644 --- a/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/CircuitBoard.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class CircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class CircuitBoard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala index 53424ce4a5..5dad3cf6a4 100644 --- a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala +++ b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.util.Rarity @@ -10,7 +9,7 @@ import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class ComponentBus(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class ComponentBus(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/ControlUnit.scala b/src/main/scala/li/cil/oc/common/item/ControlUnit.scala index d2fcb976ec..637cdcca40 100644 --- a/src/main/scala/li/cil/oc/common/item/ControlUnit.scala +++ b/src/main/scala/li/cil/oc/common/item/ControlUnit.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class ControlUnit(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class ControlUnit(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/CustomModel.scala b/src/main/scala/li/cil/oc/common/item/CustomModel.scala index 193de8bcc1..e8a114d05c 100644 --- a/src/main/scala/li/cil/oc/common/item/CustomModel.scala +++ b/src/main/scala/li/cil/oc/common/item/CustomModel.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.item.ItemStack import net.minecraftforge.client.event.ModelBakeEvent diff --git a/src/main/scala/li/cil/oc/common/item/CuttingWire.scala b/src/main/scala/li/cil/oc/common/item/CuttingWire.scala index b0d523d7a1..07399930aa 100644 --- a/src/main/scala/li/cil/oc/common/item/CuttingWire.scala +++ b/src/main/scala/li/cil/oc/common/item/CuttingWire.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class CuttingWire(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class CuttingWire(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/DataCard.scala b/src/main/scala/li/cil/oc/common/item/DataCard.scala index d87f9d3244..488ea499ac 100644 --- a/src/main/scala/li/cil/oc/common/item/DataCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DataCard.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class DataCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class DataCard(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/DebugCard.scala b/src/main/scala/li/cil/oc/common/item/DebugCard.scala index 7e18f60a1b..d12821576c 100644 --- a/src/main/scala/li/cil/oc/common/item/DebugCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DebugCard.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.Settings.DebugCardAccess import li.cil.oc.common.item.data.DebugCardData @@ -20,7 +19,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class DebugCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class DebugCard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { super.tooltipExtended(stack, tooltip) val data = new DebugCardData(stack) diff --git a/src/main/scala/li/cil/oc/common/item/Debugger.scala b/src/main/scala/li/cil/oc/common/item/Debugger.scala index 9b7009dce4..bb6daeb819 100644 --- a/src/main/scala/li/cil/oc/common/item/Debugger.scala +++ b/src/main/scala/li/cil/oc/common/item/Debugger.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.api.network._ @@ -15,7 +14,7 @@ import net.minecraft.util.Direction import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.common.extensions.IForgeItem -class Debugger(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Debugger(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float) = { val world = position.world.get player match { diff --git a/src/main/scala/li/cil/oc/common/item/DiamondChip.scala b/src/main/scala/li/cil/oc/common/item/DiamondChip.scala index d96aaf9ca0..28cc9b7cac 100644 --- a/src/main/scala/li/cil/oc/common/item/DiamondChip.scala +++ b/src/main/scala/li/cil/oc/common/item/DiamondChip.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class DiamondChip(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class DiamondChip(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/Disk.scala b/src/main/scala/li/cil/oc/common/item/Disk.scala index a9c9266334..afd23bb19d 100644 --- a/src/main/scala/li/cil/oc/common/item/Disk.scala +++ b/src/main/scala/li/cil/oc/common/item/Disk.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class Disk(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class Disk(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala index a81509449f..52da058b81 100644 --- a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.DiskDriveMountableInventory @@ -13,7 +12,7 @@ import net.minecraft.util.{ActionResult, ActionResultType, Hand} import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class DiskDriveMountable(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class DiskDriveMountable(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 override def use(stack: ItemStack, world: World, player: PlayerEntity) = { diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 6a56af79d1..16a4377ae8 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -3,7 +3,6 @@ package li.cil.oc.common.item import java.util import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.client.KeyBindings import li.cil.oc.client.renderer.block.DroneModel @@ -30,7 +29,7 @@ import net.minecraftforge.common.extensions.IForgeItem import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn -class Drone(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { +class Drone(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { ItemBlacklist.hide(this) @OnlyIn(Dist.CLIENT) diff --git a/src/main/scala/li/cil/oc/common/item/DroneCase.scala b/src/main/scala/li/cil/oc/common/item/DroneCase.scala index 6b0d306c68..5cab2ea526 100644 --- a/src/main/scala/li/cil/oc/common/item/DroneCase.scala +++ b/src/main/scala/li/cil/oc/common/item/DroneCase.scala @@ -1,12 +1,11 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class DroneCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class DroneCase(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/EEPROM.scala b/src/main/scala/li/cil/oc/common/item/EEPROM.scala index c4c5385d3f..c19dde357e 100644 --- a/src/main/scala/li/cil/oc/common/item/EEPROM.scala +++ b/src/main/scala/li/cil/oc/common/item/EEPROM.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.BlockPosition import net.minecraft.entity.player.PlayerEntity @@ -13,7 +12,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IWorldReader import net.minecraftforge.common.extensions.IForgeItem -class EEPROM(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class EEPROM(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def getName(stack: ItemStack): ITextComponent = { if (stack.hasTag) { val tag = stack.getTag diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index ebccb184c5..32ee450ae9 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -1,7 +1,6 @@ package li.cil.oc.common.item import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.Color import net.minecraft.client.renderer.model.ModelBakery @@ -18,7 +17,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.extensions.IForgeItem -class FloppyDisk(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.FileSystemLike { +class FloppyDisk(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.FileSystemLike { // Necessary for anonymous subclasses used for loot disks. unlocalizedName = "floppydisk" diff --git a/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala b/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala index 28736f19af..777e1ddda5 100644 --- a/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/common/item/GraphicsCard.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class GraphicsCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.GPULike { +class GraphicsCard(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.GPULike { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala index f9502f07e5..d4457b5131 100644 --- a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.item.Item import net.minecraft.item.Item.Properties @@ -9,7 +8,7 @@ import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraftforge.common.extensions.IForgeItem -class HardDiskDrive(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.FileSystemLike { +class HardDiskDrive(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.FileSystemLike { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index 86e1d891b9..79047aa3aa 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.client.renderer.item.HoverBootRenderer import li.cil.oc.common.init.Items @@ -30,9 +29,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem -class HoverBoots(props: Properties = new Properties().tab(CreativeTab).setNoRepair()) - extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with IForgeItem with traits.SimpleItem with traits.Chargeable { - +class HoverBoots(props: Properties) extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with IForgeItem with traits.SimpleItem with traits.Chargeable { @Deprecated override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala index 6b75e2c336..3bce24c760 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala @@ -1,13 +1,9 @@ package li.cil.oc.common.item -import li.cil.oc.Constants -import li.cil.oc.CreativeTab -import li.cil.oc.api import net.minecraft.item.Item import net.minecraft.item.Item.Properties -import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class InkCartridge(props: Properties = new Properties().tab(CreativeTab).craftRemainder(api.Items.get(Constants.ItemName.InkCartridgeEmpty).item)) extends Item(props) with IForgeItem with traits.SimpleItem { +class InkCartridge(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 } diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala index 882fb05f4e..3c89199dad 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala @@ -1,10 +1,9 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class InkCartridgeEmpty(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class InkCartridgeEmpty(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def maxStackSize = 1 } diff --git a/src/main/scala/li/cil/oc/common/item/InternetCard.scala b/src/main/scala/li/cil/oc/common/item/InternetCard.scala index 4b693c00a0..fc0cc79e24 100644 --- a/src/main/scala/li/cil/oc/common/item/InternetCard.scala +++ b/src/main/scala/li/cil/oc/common/item/InternetCard.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class InternetCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class InternetCard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/Interweb.scala b/src/main/scala/li/cil/oc/common/item/Interweb.scala index 8fc2556e25..bd13b0f0cc 100644 --- a/src/main/scala/li/cil/oc/common/item/Interweb.scala +++ b/src/main/scala/li/cil/oc/common/item/Interweb.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class Interweb(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class Interweb(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala index 7e6eb2ce95..e97ba02620 100644 --- a/src/main/scala/li/cil/oc/common/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/common/item/LinkedCard.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag @@ -19,7 +18,7 @@ import net.minecraftforge.common.extensions.IForgeItem import scala.collection.convert.ImplicitConversionsToScala._ -class LinkedCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class LinkedCard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/item/Manual.scala b/src/main/scala/li/cil/oc/common/item/Manual.scala index 4aa0f1d1fd..5a4c667338 100644 --- a/src/main/scala/li/cil/oc/common/item/Manual.scala +++ b/src/main/scala/li/cil/oc/common/item/Manual.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.BlockPosition @@ -22,7 +21,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem -class Manual(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Manual(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/item/Memory.scala b/src/main/scala/li/cil/oc/common/item/Memory.scala index 34b3dc909b..f3c1a66e27 100644 --- a/src/main/scala/li/cil/oc/common/item/Memory.scala +++ b/src/main/scala/li/cil/oc/common/item/Memory.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class Memory(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class Memory(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Microchip.scala b/src/main/scala/li/cil/oc/common/item/Microchip.scala index 13a8292192..86551bc551 100644 --- a/src/main/scala/li/cil/oc/common/item/Microchip.scala +++ b/src/main/scala/li/cil/oc/common/item/Microchip.scala @@ -1,13 +1,12 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.util.Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class Microchip(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Microchip(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala index 77ebab2543..8483e67594 100644 --- a/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala +++ b/src/main/scala/li/cil/oc/common/item/MicrocontrollerCase.scala @@ -1,12 +1,11 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class MicrocontrollerCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class MicrocontrollerCase(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala index 5efa18ae55..a755ca96f0 100644 --- a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala @@ -3,7 +3,6 @@ package li.cil.oc.common.item import java.util import com.google.common.base.Strings -import li.cil.oc.CreativeTab import li.cil.oc.api import li.cil.oc.common.item.data.NanomachineData import li.cil.oc.common.nanomachines.ControllerImpl @@ -25,7 +24,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem -class Nanomachines(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Nanomachines(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { @Deprecated override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON diff --git a/src/main/scala/li/cil/oc/common/item/NetworkCard.scala b/src/main/scala/li/cil/oc/common/item/NetworkCard.scala index 3c2cb1c784..6fabdf6c28 100644 --- a/src/main/scala/li/cil/oc/common/item/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/common/item/NetworkCard.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class NetworkCard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class NetworkCard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/NumPad.scala b/src/main/scala/li/cil/oc/common/item/NumPad.scala index 5085a84d22..775b312da5 100644 --- a/src/main/scala/li/cil/oc/common/item/NumPad.scala +++ b/src/main/scala/li/cil/oc/common/item/NumPad.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class NumPad(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file +class NumPad(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/Present.scala b/src/main/scala/li/cil/oc/common/item/Present.scala index 5169b725ba..5a612f8cd1 100644 --- a/src/main/scala/li/cil/oc/common/item/Present.scala +++ b/src/main/scala/li/cil/oc/common/item/Present.scala @@ -3,7 +3,6 @@ package li.cil.oc.common.item import java.util.Random import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.util.InventoryUtils @@ -24,7 +23,7 @@ import net.minecraftforge.common.extensions.IForgeItem import scala.collection.mutable -class Present(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Present(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) {} override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { diff --git a/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala index f0d4efd9fe..d1d68e5ac7 100644 --- a/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/PrintedCircuitBoard.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class PrintedCircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem +class PrintedCircuitBoard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala b/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala index 6f0d70ae5d..b6d6483e31 100644 --- a/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala +++ b/src/main/scala/li/cil/oc/common/item/RawCircuitBoard.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class RawCircuitBoard(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file +class RawCircuitBoard(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala index 406cd0b4ec..69ee306719 100644 --- a/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala +++ b/src/main/scala/li/cil/oc/common/item/RedstoneCard.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.common.Tier import li.cil.oc.integration.opencomputers.ModOpenComputers import net.minecraft.item.Item @@ -12,7 +11,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.NonNullList import net.minecraftforge.common.extensions.IForgeItem -class RedstoneCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class RedstoneCard(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index 557f02fd7a..7e32f876d4 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.client.KeyBindings import li.cil.oc.common.container.ContainerTypes @@ -26,7 +25,7 @@ import net.minecraftforge.common.extensions.IForgeItem import scala.collection.mutable import scala.collection.convert.ImplicitConversionsToScala._ -class Server(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class Server(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index 056d417681..ef3cb603fc 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -9,7 +9,6 @@ import java.util.concurrent.TimeUnit import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotification} import com.google.common.collect.ImmutableMap import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -78,7 +77,7 @@ import scala.collection.JavaConverters.asJavaIterable import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ -class Tablet(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.Chargeable { +class Tablet(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.Chargeable { final val TimeToAnalyze = 10 override def maxStackSize = 1 diff --git a/src/main/scala/li/cil/oc/common/item/TabletCase.scala b/src/main/scala/li/cil/oc/common/item/TabletCase.scala index fe3717818b..62264077a8 100644 --- a/src/main/scala/li/cil/oc/common/item/TabletCase.scala +++ b/src/main/scala/li/cil/oc/common/item/TabletCase.scala @@ -1,12 +1,11 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class TabletCase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class TabletCase(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 130d713143..dcab7dc636 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -4,7 +4,6 @@ import java.util import com.google.common.base.Strings import li.cil.oc.Constants -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings @@ -32,7 +31,7 @@ import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.extensions.IForgeItem -class Terminal(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { +class Terminal(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { override def maxStackSize = 1 def hasServer(stack: ItemStack) = stack.hasTag && stack.getTag.contains(Settings.namespace + "server") diff --git a/src/main/scala/li/cil/oc/common/item/TerminalServer.scala b/src/main/scala/li/cil/oc/common/item/TerminalServer.scala index 0f8b2edfd9..86a4f07ab2 100644 --- a/src/main/scala/li/cil/oc/common/item/TerminalServer.scala +++ b/src/main/scala/li/cil/oc/common/item/TerminalServer.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class TerminalServer(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class TerminalServer(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override protected def tooltipData = Seq(Settings.get.terminalsPerServer) } diff --git a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala index ebfab17f51..936435d5b3 100644 --- a/src/main/scala/li/cil/oc/common/item/TexturePicker.scala +++ b/src/main/scala/li/cil/oc/common/item/TexturePicker.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ @@ -15,7 +14,7 @@ import net.minecraft.util.Util import net.minecraftforge.client.model.ModelDataManager import net.minecraftforge.common.extensions.IForgeItem -class TexturePicker(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem { +class TexturePicker(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { override def onItemUse(stack: ItemStack, player: PlayerEntity, position: BlockPosition, side: Direction, hitX: Float, hitY: Float, hitZ: Float): Boolean = { player.level.getBlock(position) match { case block: Block => diff --git a/src/main/scala/li/cil/oc/common/item/Transistor.scala b/src/main/scala/li/cil/oc/common/item/Transistor.scala index 6e0d23f9aa..5457754df2 100644 --- a/src/main/scala/li/cil/oc/common/item/Transistor.scala +++ b/src/main/scala/li/cil/oc/common/item/Transistor.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class Transistor(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file +class Transistor(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala b/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala index 3355c6b843..4f65c741d8 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeAngel.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeAngel(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class UpgradeAngel(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala index 2e9a9809e3..a5116eb79e 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeBattery.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.api.driver.item.Chargeable import li.cil.oc.common.item.data.NodeData @@ -9,7 +8,7 @@ import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack import net.minecraftforge.common.extensions.IForgeItem -class UpgradeBattery(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.Chargeable { +class UpgradeBattery(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier with traits.Chargeable { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala b/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala index e17d93145a..786deef739 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeChunkloader.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeChunkloader(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class UpgradeChunkloader(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala b/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala index 3da6fdb36f..a6eb7ee47b 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeContainerCard.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeContainerCard(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeContainerCard(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala b/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala index e9f0a89910..a4e4267fa4 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeContainerUpgrade.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeContainerUpgrade(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeContainerUpgrade(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala index 3c5d293091..b9f877faf7 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeCrafting.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeCrafting(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class UpgradeCrafting(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala index 9dd035f72b..85e57b3cd6 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes @@ -17,7 +16,7 @@ import net.minecraft.util.Hand import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class UpgradeDatabase(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeDatabase(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala index b0f01c1afe..98b6e790d7 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeExperience.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.util.Tooltip import li.cil.oc.util.{UpgradeExperience => ExperienceUtil} @@ -16,7 +15,7 @@ import net.minecraft.world.World import net.minecraftforge.api.distmarker.{Dist, OnlyIn} import net.minecraftforge.common.extensions.IForgeItem -class UpgradeExperience(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeExperience(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala index 51e29f9d1a..79be7fa62b 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeGenerator.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeGenerator(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeGenerator(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipData = Seq((Settings.get.generatorEfficiency * 100).toInt) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala b/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala index 4b0cb7b7d6..ae56bdc64a 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeHover.scala @@ -1,12 +1,11 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeHover(val tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeHover(props: Properties, val tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala b/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala index c00da29fd1..32026c51d6 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeInventory.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeInventory(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradeInventory(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala b/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala index d1e831842e..c06f369bf4 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeInventoryController.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeInventoryController(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradeInventoryController(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala index ce586bd5d5..97a7cc4e03 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeLeash(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class UpgradeLeash(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala index 4e50e18bfb..7d451b3cc8 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala @@ -2,7 +2,6 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.util.BlockPosition @@ -21,7 +20,7 @@ import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem -class UpgradeMF(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeMF(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { if (!player.level.isClientSide && player.isCrouching) { val data = stack.getOrCreateTag diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala index b092e29013..d2e847e767 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeNavigation.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeNavigation(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradeNavigation(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala b/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala index fbcbd269c0..e8dff7e8fa 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradePiston.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradePiston(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradePiston(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala b/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala index 73f0c23256..7ca4cb9635 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeSign.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeSign(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier +class UpgradeSign(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala index 5f98298d59..f04fb24726 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeSolarGenerator.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.Settings import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeSolarGenerator(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeSolarGenerator(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipData = Seq((Settings.get.solarGeneratorEfficiency * 100).toInt) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala b/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala index 75a687f891..fbfaeb5cec 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeStickyPiston.scala @@ -1,11 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeStickyPiston(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeStickyPiston(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipName: Option[String] = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala index 84e930957a..7945cbd4b5 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTank.scala @@ -2,11 +2,9 @@ package li.cil.oc.common.item import java.util -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.util.Tooltip import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -18,7 +16,7 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem -class UpgradeTank(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeTank(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala index 01f3deba4e..7968768509 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeTankController(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradeTankController(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala index 00ed41f24b..eec70e8dfe 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTractorBeam.scala @@ -1,8 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeTractorBeam(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file +class UpgradeTractorBeam(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala index e54be35a16..7762236a61 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala @@ -1,10 +1,9 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class UpgradeTrading(props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class UpgradeTrading(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { override protected def tooltipName: Option[String] = Option(unlocalizedName) } diff --git a/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala index 48de2d44b4..acbed73313 100644 --- a/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/common/item/WirelessNetworkCard.scala @@ -1,12 +1,10 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab -import li.cil.oc.common.Tier import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class WirelessNetworkCard(var tier: Int, props: Properties = new Properties().tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { +class WirelessNetworkCard(props: Properties, var tier: Int) extends Item(props) with IForgeItem with traits.SimpleItem with traits.ItemTier { @Deprecated override def getDescriptionId = super.getDescriptionId + tier diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index 9fff0177a8..9820d28a50 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -1,9 +1,7 @@ package li.cil.oc.common.item -import li.cil.oc.CreativeTab import li.cil.oc.api import li.cil.oc.common.block.SimpleBlock -import li.cil.oc.integration.Mods import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.entity.player.PlayerEntity @@ -17,14 +15,9 @@ import net.minecraft.util.Rotation import net.minecraft.util.math.BlockPos import net.minecraft.world.IWorldReader import net.minecraft.world.World -import net.minecraftforge.common.ToolType import net.minecraftforge.common.extensions.IForgeItem -object Wrench { - val WrenchType: ToolType = ToolType.get("wrench") -} - -class Wrench(props: Properties = new Properties().stacksTo(1).addToolType(Wrench.WrenchType, 1).tab(CreativeTab)) extends Item(props) with IForgeItem with traits.SimpleItem with api.internal.Wrench { +class Wrench(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with api.internal.Wrench { override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = true override def onItemUseFirst(stack: ItemStack, player: PlayerEntity, world: World, pos: BlockPos, side: Direction, hitX: Float, hitY: Float, hitZ: Float, hand: Hand): ActionResultType = { diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 2abea2e9f3..194a53fa39 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -3,7 +3,6 @@ package li.cil.oc.common.item.traits import java.util import com.mojang.blaze3d.matrix.MatrixStack -import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.event.RobotRenderEvent.MountPoint From 781907b5eadec4941500ef08920fcd979664f2af Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 1 Oct 2022 23:01:16 +0200 Subject: [PATCH 101/159] Remove the RobotAfterimage item representation --- .../models/item/robotafterimage.json | 3 -- .../renderer/block/ModelInitialization.scala | 10 +++---- .../scala/li/cil/oc/common/init/Blocks.scala | 2 +- .../scala/li/cil/oc/common/init/Items.scala | 28 ++++++++++++++++++- 4 files changed, 33 insertions(+), 10 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/models/item/robotafterimage.json diff --git a/src/main/resources/assets/opencomputers/models/item/robotafterimage.json b/src/main/resources/assets/opencomputers/models/item/robotafterimage.json deleted file mode 100644 index 5485f33463..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/robotafterimage.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "minecraft:block/air" -} diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 952fa4add4..63218e4714 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -41,7 +41,6 @@ object ModelInitialization { final val RobotBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Robot, "") final val RobotItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Robot, "inventory") final val RobotAfterimageBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.RobotAfterimage, "") - final val RobotAfterimageItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.RobotAfterimage, "inventory") final val RackBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Rack, "") private val meshableItems = mutable.ArrayBuffer.empty[Item] @@ -52,7 +51,7 @@ object ModelInitialization { registerModel(Constants.BlockName.NetSplitter, NetSplitterBlockLocation, NetSplitterItemLocation) registerModel(Constants.BlockName.Print, PrintBlockLocation, PrintItemLocation) registerModel(Constants.BlockName.Robot, RobotBlockLocation, RobotItemLocation) - registerModel(Constants.BlockName.RobotAfterimage, RobotAfterimageBlockLocation, RobotAfterimageItemLocation) + registerModel(Constants.BlockName.RobotAfterimage, RobotAfterimageBlockLocation, null) } @SubscribeEvent @@ -86,8 +85,10 @@ object ModelInitialization { val block = descriptor.block() val stack = descriptor.createItemStack(1) - val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper - shaper.register(stack.getItem, itemLocation) + if (!stack.isEmpty) { + val shaper = Minecraft.getInstance.getItemRenderer.getItemModelShaper + shaper.register(stack.getItem, itemLocation) + } block.getStateDefinition.getPossibleStates.foreach { modelRemappings += BlockModelShapes.stateToModelLocation(_) -> blockLocation } @@ -108,7 +109,6 @@ object ModelInitialization { registry.put(RobotBlockLocation, RobotModel) registry.put(RobotItemLocation, RobotModel) registry.put(RobotAfterimageBlockLocation, NullModel) - registry.put(RobotAfterimageItemLocation, NullModel) for (item <- meshableItems) item match { case custom: CustomModel => { diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index b2e011ef8c..f687c551a0 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -44,7 +44,7 @@ object Blocks { Items.registerBlock(new Case(defaultProps, Tier.Four), Constants.BlockName.CaseCreative) Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller) Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print) - Items.registerBlock(new RobotAfterimage(Properties.of(Material.AIR).instabreak.noOcclusion.dynamicShape.air), Constants.BlockName.RobotAfterimage) + Items.registerBlockOnly(new RobotAfterimage(Properties.of(Material.AIR).instabreak.noOcclusion.dynamicShape.air), Constants.BlockName.RobotAfterimage) Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot) // v1.5.10 diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index f2972130df..f841f05690 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -56,6 +56,32 @@ object Items extends ItemAPI { private def defaultProps = new Properties().tab(CreativeTab) + def registerBlockOnly(instance: Block, id: String): Block = { + if (!descriptors.contains(id)) { + instance match { + case simple: SimpleBlock => + simple.setUnlocalizedName("oc." + id) + simple.setRegistryName(OpenComputers.ID, id) + GameData.register_impl[Block](simple) + case _ => + } + descriptors += id -> new ItemInfo { + override def name: String = id + + override def block = instance + + override def item = null + + override def createItemStack(size: Int): ItemStack = { + OpenComputers.log.warn(s"Attempt to get ItemStack for block ${instance} without item form") + ItemStack.EMPTY + } + } + names += instance -> id + } + instance + } + def registerBlock(instance: Block, id: String): Block = { if (!descriptors.contains(id)) { instance match { @@ -76,7 +102,7 @@ object Items extends ItemAPI { override def block = instance - override def item = null + override def item = item override def createItemStack(size: Int): ItemStack = instance match { case simple: SimpleBlock => simple.createItemStack(size) From f7a603d104bc4fc86f22d62478a7a2abed64daaf Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 2 Oct 2022 20:36:24 +0200 Subject: [PATCH 102/159] Fix absent item stacking limits --- .../scala/li/cil/oc/common/init/Items.scala | 22 +++++++++---------- .../oc/common/item/DiskDriveMountable.scala | 2 -- .../li/cil/oc/common/item/InkCartridge.scala | 4 +--- .../oc/common/item/InkCartridgeEmpty.scala | 4 +--- .../scala/li/cil/oc/common/item/Server.scala | 2 -- .../scala/li/cil/oc/common/item/Tablet.scala | 2 -- .../li/cil/oc/common/item/Terminal.scala | 2 -- .../oc/common/item/traits/SimpleItem.scala | 10 --------- 8 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index f841f05690..8a7a406399 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -377,8 +377,8 @@ object Items extends ItemAPI { registerItem(new item.DroneCase(defaultProps, Tier.Two), Constants.ItemName.DroneCaseTier2) registerItem(new item.DroneCase(defaultProps, Tier.Four), Constants.ItemName.DroneCaseCreative) - registerItem(new item.InkCartridgeEmpty(defaultProps), Constants.ItemName.InkCartridgeEmpty) - registerItem(new item.InkCartridge(defaultProps.craftRemainder(get(Constants.ItemName.InkCartridgeEmpty).item)), Constants.ItemName.InkCartridge) + registerItem(new item.InkCartridgeEmpty(defaultProps.stacksTo(1)), Constants.ItemName.InkCartridgeEmpty) + registerItem(new item.InkCartridge(defaultProps.stacksTo(1).craftRemainder(get(Constants.ItemName.InkCartridgeEmpty).item)), Constants.ItemName.InkCartridge) registerItem(new item.Chamelium(defaultProps), Constants.ItemName.Chamelium) registerItem(new item.DiamondChip(defaultProps), Constants.ItemName.DiamondChip) @@ -390,13 +390,13 @@ object Items extends ItemAPI { private def initTools(): Unit = { registerItem(new item.Analyzer(defaultProps), Constants.ItemName.Analyzer) registerItem(new item.Debugger(defaultProps), Constants.ItemName.Debugger) - registerItem(new item.Terminal(defaultProps), Constants.ItemName.Terminal) + registerItem(new item.Terminal(defaultProps.stacksTo(1)), Constants.ItemName.Terminal) registerItem(new item.TexturePicker(defaultProps), Constants.ItemName.TexturePicker) registerItem(new item.Manual(defaultProps), Constants.ItemName.Manual) registerItem(new item.Wrench(defaultProps.stacksTo(1).addToolType(WrenchType, 1)), Constants.ItemName.Wrench) // 1.5.11 - registerItem(new item.HoverBoots(defaultProps.setNoRepair), Constants.ItemName.HoverBoots) + registerItem(new item.HoverBoots(defaultProps.stacksTo(1).setNoRepair), Constants.ItemName.HoverBoots) // 1.5.18 registerItem(new item.Nanomachines(defaultProps), Constants.ItemName.Nanomachines) @@ -419,10 +419,10 @@ object Items extends ItemAPI { registerItem(new item.Memory(defaultProps, Tier.Five), Constants.ItemName.RAMTier5) registerItem(new item.Memory(defaultProps, Tier.Six), Constants.ItemName.RAMTier6) - registerItem(new item.Server(defaultProps, Tier.Four), Constants.ItemName.ServerCreative) - registerItem(new item.Server(defaultProps, Tier.One), Constants.ItemName.ServerTier1) - registerItem(new item.Server(defaultProps, Tier.Two), Constants.ItemName.ServerTier2) - registerItem(new item.Server(defaultProps, Tier.Three), Constants.ItemName.ServerTier3) + registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Four), Constants.ItemName.ServerCreative) + registerItem(new item.Server(defaultProps.stacksTo(1), Tier.One), Constants.ItemName.ServerTier1) + registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Two), Constants.ItemName.ServerTier2) + registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Three), Constants.ItemName.ServerTier3) // 1.5.10 registerItem(new item.APU(defaultProps, Tier.One), Constants.ItemName.APUTier1) @@ -432,8 +432,8 @@ object Items extends ItemAPI { registerItem(new item.APU(defaultProps, Tier.Three), Constants.ItemName.APUCreative) // 1.6 - registerItem(new item.TerminalServer(defaultProps), Constants.ItemName.TerminalServer) - registerItem(new item.DiskDriveMountable(defaultProps), Constants.ItemName.DiskDriveMountable) + registerItem(new item.TerminalServer(defaultProps.stacksTo(1)), Constants.ItemName.TerminalServer) + registerItem(new item.DiskDriveMountable(defaultProps.stacksTo(1)), Constants.ItemName.DiskDriveMountable) } // Card components. @@ -522,7 +522,7 @@ object Items extends ItemAPI { // Special purpose items that don't fit into any other category. private def initSpecial(): Unit = { - registerItem(new item.Tablet(defaultProps), Constants.ItemName.Tablet) + registerItem(new item.Tablet(defaultProps.stacksTo(1)), Constants.ItemName.Tablet) registerItem(new item.Drone(defaultProps), Constants.ItemName.Drone) registerItem(new item.Present(defaultProps), Constants.ItemName.Present) } diff --git a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala index 52da058b81..36b89310b1 100644 --- a/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala +++ b/src/main/scala/li/cil/oc/common/item/DiskDriveMountable.scala @@ -13,8 +13,6 @@ import net.minecraft.world.World import net.minecraftforge.common.extensions.IForgeItem class DiskDriveMountable(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { - override def maxStackSize = 1 - override def use(stack: ItemStack, world: World, player: PlayerEntity) = { if (!world.isClientSide) player match { case srvPlr: ServerPlayerEntity => ContainerTypes.openDiskDriveGui(srvPlr, new DiskDriveMountableInventory { diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala index 3bce24c760..a85168a066 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridge.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridge.scala @@ -4,6 +4,4 @@ import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class InkCartridge(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { - override def maxStackSize = 1 -} +class InkCartridge(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala index 3c89199dad..cb68dc3dd3 100644 --- a/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala +++ b/src/main/scala/li/cil/oc/common/item/InkCartridgeEmpty.scala @@ -4,6 +4,4 @@ import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraftforge.common.extensions.IForgeItem -class InkCartridgeEmpty(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { - override def maxStackSize = 1 -} +class InkCartridgeEmpty(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index 7e32f876d4..31a15cd8ac 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -34,8 +34,6 @@ class Server(props: Properties, val tier: Int) extends Item(props) with IForgeIt @Deprecated override def getRarity(stack: ItemStack): item.Rarity = Rarity.byTier(tier) - override def maxStackSize = 1 - private object HelperInventory extends ServerInventory { var container = ItemStack.EMPTY diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index ef3cb603fc..e3d6b28f7e 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -80,8 +80,6 @@ import scala.collection.convert.ImplicitConversionsToScala._ class Tablet(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel with traits.Chargeable { final val TimeToAnalyze = 10 - override def maxStackSize = 1 - // ----------------------------------------------------------------------- // override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[ITextComponent]): Unit = { diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index dcab7dc636..7b582ca53f 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -32,8 +32,6 @@ import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.extensions.IForgeItem class Terminal(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { - override def maxStackSize = 1 - def hasServer(stack: ItemStack) = stack.hasTag && stack.getTag.contains(Settings.namespace + "server") @OnlyIn(Dist.CLIENT) diff --git a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala index 194a53fa39..0e6ca4b855 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/SimpleItem.scala @@ -43,16 +43,6 @@ trait SimpleItem extends Item with api.driver.item.UpgradeRenderer { @Deprecated override def getDescriptionId = "item.oc." + unlocalizedName - @Deprecated - def maxStackSize = 64 - - @Deprecated - override def getItemStackLimit(stack: ItemStack): Int = - OpenComputersItem.address(stack) match { - case Some(address) => 1 - case _ => maxStackSize - } - override def doesSneakBypassUse(stack: ItemStack, world: IWorldReader, pos: BlockPos, player: PlayerEntity): Boolean = { world.getBlockEntity(pos) match { case drive: tileentity.DiskDrive => true From 8641954022697978897181f0350c6b829d6f1250 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 2 Oct 2022 23:44:23 +0200 Subject: [PATCH 103/159] Fix different colored cables connecting visually --- .../scala/li/cil/oc/common/block/Cable.scala | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index 5733e629c5..f9d8821780 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -7,12 +7,13 @@ import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ +import li.cil.oc.util.ItemColorizer import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.{Entity, LivingEntity} -import net.minecraft.item.{DyeColor, ItemStack} +import net.minecraft.item.{BlockItemUseContext, DyeColor, ItemStack} import net.minecraft.state.StateContainer import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction @@ -52,6 +53,18 @@ class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileenti setValue(PropertyCableConnection.WEST, PropertyCableConnection.Shape.NONE). setValue(PropertyCableConnection.EAST, PropertyCableConnection.Shape.NONE)) + override def getStateForPlacement(ctx: BlockItemUseContext): BlockState = { + var state = defaultBlockState + val color = Cable.getConnectionColor(ctx.getItemInHand) + val fromPos = new BlockPos.Mutable(); + for (fromSide <- Direction.values) { + fromPos.setWithOffset(ctx.getClickedPos, fromSide) + val fromState = ctx.getLevel.getBlockState(fromPos) + state = Cable.updateState(state, null, color, fromSide, fromState, ctx.getLevel, fromPos) + } + state + } + override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity) = world.getBlockEntity(pos) match { case t: tileentity.Cable => t.createItemStack() @@ -61,23 +74,7 @@ class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileenti override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = Cable.shape(state) override def updateShape(state: BlockState, fromSide: Direction, fromState: BlockState, world: IWorld, pos: BlockPos, fromPos: BlockPos): BlockState = - Cable.updateState(state, fromSide, fromState, world, pos, fromPos) - - // Connecting to other blocks requires this cable to have a TE set, so wait until next tick and update. - override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { - // Only schedule this update if this cable was newly placed (not if it changed shape). - if (!prevState.is(this)) { - world match { - case srvWorld: ServerWorld if !srvWorld.isClientSide => srvWorld.getBlockTicks.scheduleTick(pos, this, 1) - case _ => - } - } - } - - override def tick(state: BlockState, world: ServerWorld, pos: BlockPos, rand: util.Random) { - val newState = Block.updateFromNeighbourShapes(state, world, pos) - Block.updateOrDestroy(state, newState, world, pos, 3) - } + Cable.updateState(state, world.getBlockEntity(pos), -1, fromSide, fromState, world, fromPos) // ----------------------------------------------------------------------- // @@ -87,9 +84,8 @@ class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileenti override protected def doCustomInit(tileEntity: tileentity.Cable, player: LivingEntity, stack: ItemStack): Unit = { super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isClientSide) { - tileEntity.fromItemStack(stack) - } + tileEntity.fromItemStack(stack) + tileEntity.getBlockState.updateNeighbourShapes(tileEntity.getLevel, tileEntity.getBlockPos, 2) } override protected def doCustomDrops(tileEntity: tileentity.Cable, player: PlayerEntity, willHarvest: Boolean): Unit = { @@ -137,27 +133,23 @@ object Cable { Cable.CachedBounds(result) } - def updateState(state: BlockState, fromSide: Direction, fromState: BlockState, world: IBlockReader, pos: BlockPos, fromPos: BlockPos): BlockState = { + def updateState(state: BlockState, tileEntity: TileEntity, defaultColor: Int, fromSide: Direction, fromState: BlockState, world: IBlockReader, fromPos: BlockPos): BlockState = { val prop = PropertyCableConnection.BY_DIRECTION.get(fromSide) - if (fromState.is(state.getBlock)) { - state.setValue(prop, PropertyCableConnection.Shape.CABLE) - } - else { - val tileEntity = world.getBlockEntity(pos) - val hasNode = hasNetworkNode(tileEntity, fromSide) - if (hasNode) { - val neighborTileEntity = world.getBlockEntity(fromPos) - if (neighborTileEntity != null && neighborTileEntity.getLevel != null) { - val neighborHasNode = hasNetworkNode(neighborTileEntity, fromSide.getOpposite) - val canConnectColor = canConnectBasedOnColor(tileEntity, neighborTileEntity) - val canConnectIM = canConnectFromSideIM(tileEntity, fromSide) && canConnectFromSideIM(neighborTileEntity, fromSide.getOpposite) - if (neighborHasNode && canConnectColor && canConnectIM) { - return state.setValue(prop, PropertyCableConnection.Shape.DEVICE) - } + val neighborTileEntity = world.getBlockEntity(fromPos) + if (neighborTileEntity != null && neighborTileEntity.getLevel != null) { + val neighborHasNode = hasNetworkNode(neighborTileEntity, fromSide.getOpposite) + val canConnectColor = canConnectBasedOnColor(tileEntity, neighborTileEntity, defaultColor) + val canConnectIM = canConnectFromSideIM(tileEntity, fromSide) && canConnectFromSideIM(neighborTileEntity, fromSide.getOpposite) + if (neighborHasNode && canConnectColor && canConnectIM) { + if (fromState.is(state.getBlock)) { + return state.setValue(prop, PropertyCableConnection.Shape.CABLE) + } + else { + return state.setValue(prop, PropertyCableConnection.Shape.DEVICE) } } - state.setValue(prop, PropertyCableConnection.Shape.NONE) } + state.setValue(prop, PropertyCableConnection.Shape.NONE) } private def hasNetworkNode(tileEntity: TileEntity, side: Direction): Boolean = { @@ -180,6 +172,11 @@ object Cable { false } + private def getConnectionColor(stack: ItemStack): Int = { + val color = ItemColorizer.getColor(stack) + if (color == -1) Color.rgbValues(DyeColor.LIGHT_GRAY) else color + } + private def getConnectionColor(tileEntity: TileEntity): Int = { if (tileEntity != null) { if (tileEntity.getCapability(Capabilities.ColoredCapability, null).isPresent) { @@ -191,8 +188,8 @@ object Cable { Color.rgbValues(DyeColor.LIGHT_GRAY) } - private def canConnectBasedOnColor(te1: TileEntity, te2: TileEntity) = { - val (c1, c2) = (getConnectionColor(te1), getConnectionColor(te2)) + private def canConnectBasedOnColor(te1: TileEntity, te2: TileEntity, c1Default: Int = Color.rgbValues(DyeColor.LIGHT_GRAY)) = { + val (c1, c2) = (if (te1 == null) c1Default else getConnectionColor(te1), getConnectionColor(te2)) c1 == c2 || c1 == Color.rgbValues(DyeColor.LIGHT_GRAY) || c2 == Color.rgbValues(DyeColor.LIGHT_GRAY) } From 122fe26cf5e6683cd6d2717ab8a38a754ba92192 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:08:40 +0200 Subject: [PATCH 104/159] Fix incorrect drops in survival mode --- .../loot_tables/blocks/adapter.json | 28 ++ .../loot_tables/blocks/assembler.json | 28 ++ .../loot_tables/blocks/cable.json | 24 ++ .../loot_tables/blocks/capacitor.json | 19 ++ .../loot_tables/blocks/carpetedcapacitor.json | 19 ++ .../loot_tables/blocks/case1.json | 28 ++ .../loot_tables/blocks/case2.json | 28 ++ .../loot_tables/blocks/case3.json | 28 ++ .../loot_tables/blocks/chameliumblock.json | 315 ++++++++++++++++++ .../loot_tables/blocks/charger.json | 28 ++ .../loot_tables/blocks/disassembler.json | 28 ++ .../loot_tables/blocks/diskdrive.json | 28 ++ .../loot_tables/blocks/endstone.json | 19 ++ .../loot_tables/blocks/geolyzer.json | 19 ++ .../loot_tables/blocks/hologram1.json | 19 ++ .../loot_tables/blocks/hologram2.json | 19 ++ .../loot_tables/blocks/keyboard.json | 19 ++ .../loot_tables/blocks/microcontroller.json | 19 ++ .../loot_tables/blocks/motionsensor.json | 19 ++ .../loot_tables/blocks/netsplitter.json | 19 ++ .../loot_tables/blocks/powerconverter.json | 19 ++ .../loot_tables/blocks/powerdistributor.json | 19 ++ .../loot_tables/blocks/print.json | 19 ++ .../loot_tables/blocks/printer.json | 28 ++ .../loot_tables/blocks/rack.json | 28 ++ .../loot_tables/blocks/raid.json | 19 ++ .../loot_tables/blocks/redstone.json | 19 ++ .../loot_tables/blocks/relay.json | 28 ++ .../loot_tables/blocks/robot.json | 28 ++ .../loot_tables/blocks/screen1.json | 19 ++ .../loot_tables/blocks/screen2.json | 19 ++ .../loot_tables/blocks/screen3.json | 19 ++ .../loot_tables/blocks/transposer.json | 19 ++ .../loot_tables/blocks/waypoint.json | 19 ++ .../scala/li/cil/oc/common/block/Cable.scala | 20 +- .../cil/oc/common/block/ChameliumBlock.scala | 20 +- .../cil/oc/common/block/Microcontroller.scala | 48 ++- .../scala/li/cil/oc/common/block/Print.scala | 44 ++- .../scala/li/cil/oc/common/block/Raid.scala | 69 ++-- .../li/cil/oc/common/block/RobotProxy.scala | 47 ++- .../li/cil/oc/common/block/SimpleBlock.scala | 23 +- .../oc/common/block/traits/CustomDrops.scala | 55 --- .../common/tileentity/Microcontroller.scala | 10 + .../li/cil/oc/common/tileentity/Raid.scala | 8 + .../li/cil/oc/common/tileentity/Robot.scala | 15 + .../cil/oc/common/tileentity/RobotProxy.scala | 3 + .../common/tileentity/traits/Inventory.scala | 4 + .../li/cil/oc/server/loot/CopyColor.java | 60 ++++ .../li/cil/oc/server/loot/LootFunctions.java | 29 ++ .../scala/li/cil/oc/server/loot/SetColor.java | 88 +++++ .../scala/li/cil/oc/util/InventoryUtils.scala | 14 + 51 files changed, 1454 insertions(+), 158 deletions(-) create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/adapter.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/assembler.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/cable.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/capacitor.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/carpetedcapacitor.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/case1.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/case2.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/case3.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/chameliumblock.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/charger.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/disassembler.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/diskdrive.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/endstone.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/geolyzer.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/hologram1.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/hologram2.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/keyboard.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/microcontroller.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/motionsensor.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/netsplitter.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/powerconverter.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/powerdistributor.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/print.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/printer.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/rack.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/raid.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/redstone.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/relay.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/robot.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/screen1.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/screen2.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/screen3.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/transposer.json create mode 100644 src/main/resources/data/opencomputers/loot_tables/blocks/waypoint.json delete mode 100644 src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala create mode 100644 src/main/scala/li/cil/oc/server/loot/CopyColor.java create mode 100644 src/main/scala/li/cil/oc/server/loot/LootFunctions.java create mode 100644 src/main/scala/li/cil/oc/server/loot/SetColor.java diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/adapter.json b/src/main/resources/data/opencomputers/loot_tables/blocks/adapter.json new file mode 100644 index 0000000000..31c6823d58 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/adapter.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:adapter" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/assembler.json b/src/main/resources/data/opencomputers/loot_tables/blocks/assembler.json new file mode 100644 index 0000000000..3a2337bd51 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/assembler.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:assembler" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/cable.json b/src/main/resources/data/opencomputers/loot_tables/blocks/cable.json new file mode 100644 index 0000000000..86950b69dd --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/cable.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "functions": [ + { + "function": "opencomputers:copy_color" + } + ], + "name": "opencomputers:cable" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/capacitor.json b/src/main/resources/data/opencomputers/loot_tables/blocks/capacitor.json new file mode 100644 index 0000000000..4a12dee16b --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/capacitor.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:capacitor" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/carpetedcapacitor.json b/src/main/resources/data/opencomputers/loot_tables/blocks/carpetedcapacitor.json new file mode 100644 index 0000000000..6e9658bbcd --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/carpetedcapacitor.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:carpetedcapacitor" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/case1.json b/src/main/resources/data/opencomputers/loot_tables/blocks/case1.json new file mode 100644 index 0000000000..0c75840b1c --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/case1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:case1" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/case2.json b/src/main/resources/data/opencomputers/loot_tables/blocks/case2.json new file mode 100644 index 0000000000..0b176c7fff --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/case2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:case2" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/case3.json b/src/main/resources/data/opencomputers/loot_tables/blocks/case3.json new file mode 100644 index 0000000000..8124f0267f --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/case3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:case3" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/chameliumblock.json b/src/main/resources/data/opencomputers/loot_tables/blocks/chameliumblock.json new file mode 100644 index 0000000000..efec4024f1 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/chameliumblock.json @@ -0,0 +1,315 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:alternatives", + "children": [ + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "white" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 0}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "orange" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 1}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "magenta" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 2}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "light_blue" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 3}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "yellow" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 4}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "lime" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 5}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "pink" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 6}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "gray" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 7}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "light_gray" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 8}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "cyan" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 9}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "purple" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 10}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "blue" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 11}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "brown" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 12}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "green" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 13}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "block_state_property", + "block": "opencomputers:chameliumblock", + "properties": { + "color": "red" + } + } + ], + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 14}" + } + ], + "name": "opencomputers:chameliumblock" + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "set_nbt", + "tag": "{\"Damage\": 15}" + } + ], + "name": "opencomputers:chameliumblock" + } + ] + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/charger.json b/src/main/resources/data/opencomputers/loot_tables/blocks/charger.json new file mode 100644 index 0000000000..160cb66781 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/charger.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:charger" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/disassembler.json b/src/main/resources/data/opencomputers/loot_tables/blocks/disassembler.json new file mode 100644 index 0000000000..3f8705d01b --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/disassembler.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:disassembler" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/diskdrive.json b/src/main/resources/data/opencomputers/loot_tables/blocks/diskdrive.json new file mode 100644 index 0000000000..ba6a6807ca --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/diskdrive.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:diskdrive" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/endstone.json b/src/main/resources/data/opencomputers/loot_tables/blocks/endstone.json new file mode 100644 index 0000000000..839f84ffef --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/endstone.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:endstone" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/geolyzer.json b/src/main/resources/data/opencomputers/loot_tables/blocks/geolyzer.json new file mode 100644 index 0000000000..f011e9b434 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/geolyzer.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:geolyzer" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/hologram1.json b/src/main/resources/data/opencomputers/loot_tables/blocks/hologram1.json new file mode 100644 index 0000000000..8e5d34e7f2 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/hologram1.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:hologram1" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/hologram2.json b/src/main/resources/data/opencomputers/loot_tables/blocks/hologram2.json new file mode 100644 index 0000000000..0e0f30749d --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/hologram2.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:hologram2" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/keyboard.json b/src/main/resources/data/opencomputers/loot_tables/blocks/keyboard.json new file mode 100644 index 0000000000..df15874146 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/keyboard.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:keyboard" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/microcontroller.json b/src/main/resources/data/opencomputers/loot_tables/blocks/microcontroller.json new file mode 100644 index 0000000000..1749fc2bfb --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/microcontroller.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:item_data" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/motionsensor.json b/src/main/resources/data/opencomputers/loot_tables/blocks/motionsensor.json new file mode 100644 index 0000000000..baa71c8b2f --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/motionsensor.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:motionsensor" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/netsplitter.json b/src/main/resources/data/opencomputers/loot_tables/blocks/netsplitter.json new file mode 100644 index 0000000000..66b2d6404d --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/netsplitter.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:netsplitter" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/powerconverter.json b/src/main/resources/data/opencomputers/loot_tables/blocks/powerconverter.json new file mode 100644 index 0000000000..867418f5f1 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/powerconverter.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:powerconverter" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/powerdistributor.json b/src/main/resources/data/opencomputers/loot_tables/blocks/powerdistributor.json new file mode 100644 index 0000000000..ba25535d42 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/powerdistributor.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:powerdistributor" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/print.json b/src/main/resources/data/opencomputers/loot_tables/blocks/print.json new file mode 100644 index 0000000000..1749fc2bfb --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/print.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:item_data" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/printer.json b/src/main/resources/data/opencomputers/loot_tables/blocks/printer.json new file mode 100644 index 0000000000..81a05e0e6b --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/printer.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:printer" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/rack.json b/src/main/resources/data/opencomputers/loot_tables/blocks/rack.json new file mode 100644 index 0000000000..1fa79578fb --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/rack.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:rack" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/raid.json b/src/main/resources/data/opencomputers/loot_tables/blocks/raid.json new file mode 100644 index 0000000000..1749fc2bfb --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/raid.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:item_data" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/redstone.json b/src/main/resources/data/opencomputers/loot_tables/blocks/redstone.json new file mode 100644 index 0000000000..5bfd376475 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/redstone.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:redstone" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/relay.json b/src/main/resources/data/opencomputers/loot_tables/blocks/relay.json new file mode 100644 index 0000000000..47e2fe3750 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/relay.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:relay" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/robot.json b/src/main/resources/data/opencomputers/loot_tables/blocks/robot.json new file mode 100644 index 0000000000..e6847c1658 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/robot.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:item_data" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:dynamic", + "name": "opencomputers:volatile_contents" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/screen1.json b/src/main/resources/data/opencomputers/loot_tables/blocks/screen1.json new file mode 100644 index 0000000000..eea4e8dbe7 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/screen1.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:screen1" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/screen2.json b/src/main/resources/data/opencomputers/loot_tables/blocks/screen2.json new file mode 100644 index 0000000000..7be03aa442 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/screen2.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:screen2" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/screen3.json b/src/main/resources/data/opencomputers/loot_tables/blocks/screen3.json new file mode 100644 index 0000000000..97e952e40c --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/screen3.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:screen3" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/transposer.json b/src/main/resources/data/opencomputers/loot_tables/blocks/transposer.json new file mode 100644 index 0000000000..891fce4664 --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/transposer.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:transposer" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/loot_tables/blocks/waypoint.json b/src/main/resources/data/opencomputers/loot_tables/blocks/waypoint.json new file mode 100644 index 0000000000..e80460addc --- /dev/null +++ b/src/main/resources/data/opencomputers/loot_tables/blocks/waypoint.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "opencomputers:waypoint" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/block/Cable.scala b/src/main/scala/li/cil/oc/common/block/Cable.scala index f9d8821780..e8f8c4ead1 100644 --- a/src/main/scala/li/cil/oc/common/block/Cable.scala +++ b/src/main/scala/li/cil/oc/common/block/Cable.scala @@ -30,7 +30,7 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.collection.JavaConverters._ import scala.reflect.ClassTag -class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Cable]) extends SimpleBlock(props) with IForgeBlock with traits.CustomDrops[tileentity.Cable] { +class Cable(props: Properties) extends SimpleBlock(props) with IForgeBlock { // For Immibis Microblock support. val ImmibisMicroblocks_TransformableBlockMarker = null @@ -82,16 +82,14 @@ class Cable(props: Properties)(protected implicit val tileTag: ClassTag[tileenti // ----------------------------------------------------------------------- // - override protected def doCustomInit(tileEntity: tileentity.Cable, player: LivingEntity, stack: ItemStack): Unit = { - super.doCustomInit(tileEntity, player, stack) - tileEntity.fromItemStack(stack) - tileEntity.getBlockState.updateNeighbourShapes(tileEntity.getLevel, tileEntity.getBlockPos, 2) - } - - override protected def doCustomDrops(tileEntity: tileentity.Cable, player: PlayerEntity, willHarvest: Boolean): Unit = { - super.doCustomDrops(tileEntity, player, willHarvest) - if (!player.isCreative) { - Block.popResource(tileEntity.world, tileEntity.getBlockPos, tileEntity.createItemStack()) + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { + super.setPlacedBy(world, pos, state, placer, stack) + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Cable => { + tileEntity.fromItemStack(stack) + state.updateNeighbourShapes(world, pos, 2) + } + case _ => } } } diff --git a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala index b4a5652273..4dc38c6d90 100644 --- a/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/ChameliumBlock.scala @@ -1,19 +1,19 @@ package li.cil.oc.common.block -import java.util.Collections import java.util.List import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.AbstractBlock.Properties -import net.minecraft.block.material.Material -import net.minecraft.state.EnumProperty +import net.minecraft.item.BlockItemUseContext import net.minecraft.item.DyeColor import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack -import net.minecraft.loot.LootContext -import net.minecraft.util.NonNullList +import net.minecraft.state.EnumProperty import net.minecraft.state.StateContainer +import net.minecraft.util.NonNullList +import net.minecraft.util.math.BlockPos +import net.minecraft.world.IBlockReader object ChameliumBlock { final val Color = EnumProperty.create("color", classOf[DyeColor]) @@ -25,13 +25,15 @@ class ChameliumBlock(props: Properties) extends SimpleBlock(props) { } registerDefaultState(stateDefinition.any.setValue(ChameliumBlock.Color, DyeColor.BLACK)) - @Deprecated - override def getDrops(state: BlockState, ctx: LootContext.Builder): List[ItemStack] = { - val stack = new ItemStack(this, 1) + override def getCloneItemStack(world: IBlockReader, pos: BlockPos, state: BlockState): ItemStack = { + val stack = new ItemStack(this) stack.setDamageValue(state.getValue(ChameliumBlock.Color).getId) - Collections.singletonList(stack) + stack } + override def getStateForPlacement(ctx: BlockItemUseContext): BlockState = + defaultBlockState.setValue(ChameliumBlock.Color, DyeColor.byId(ctx.getItemInHand.getDamageValue)) + override def fillItemCategory(tab: ItemGroup, list: NonNullList[ItemStack]) { val stack = new ItemStack(this, 1) stack.setDamageValue(defaultBlockState.getValue(ChameliumBlock.Color).getId) diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index 1a80f467cf..c040d1aee4 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -12,6 +12,7 @@ import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.integration.util.Wrench +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity import li.cil.oc.util.StackOption._ @@ -24,6 +25,8 @@ import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item // Rarity import net.minecraft.item.ItemStack +import net.minecraft.loot.LootContext +import net.minecraft.loot.LootParameters import net.minecraft.state.StateContainer import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -37,8 +40,8 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Microcontroller(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Microcontroller]) - extends RedstoneAware(props) with IForgeBlock with traits.PowerAcceptor with traits.StateAware with traits.CustomDrops[tileentity.Microcontroller] { +class Microcontroller(props: Properties) + extends RedstoneAware(props) with IForgeBlock with traits.PowerAcceptor with traits.StateAware { setCreativeTab(null) ItemBlacklist.hide(this) @@ -110,18 +113,39 @@ class Microcontroller(props: Properties)(protected implicit val tileTag: ClassTa else false } - override protected def doCustomInit(tileEntity: tileentity.Microcontroller, player: LivingEntity, stack: ItemStack): Unit = { - super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isClientSide) { - tileEntity.info.loadData(stack) - tileEntity.snooperNode.changeBuffer(tileEntity.info.storedEnergy - tileEntity.snooperNode.localBuffer) + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { + super.setPlacedBy(world, pos, state, placer, stack) + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Microcontroller if !world.isClientSide => { + tileEntity.info.loadData(stack) + tileEntity.snooperNode.changeBuffer(tileEntity.info.storedEnergy - tileEntity.snooperNode.localBuffer) + } + case _ => } } - override protected def doCustomDrops(tileEntity: tileentity.Microcontroller, player: PlayerEntity, willHarvest: Boolean): Unit = { - super.doCustomDrops(tileEntity, player, willHarvest) - tileEntity.saveComponents() - tileEntity.info.storedEnergy = tileEntity.snooperNode.localBuffer.toInt - Block.popResource(tileEntity.world, tileEntity.getBlockPos, tileEntity.info.createItemStack()) + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { + val newCtx = ctx.withDynamicDrop(LootFunctions.DYN_ITEM_DATA, (c, f) => { + c.getParamOrNull(LootParameters.BLOCK_ENTITY) match { + case tileEntity: tileentity.Microcontroller => { + tileEntity.saveComponents() + tileEntity.info.storedEnergy = tileEntity.snooperNode.localBuffer.toInt + f.accept(tileEntity.info.createItemStack()) + } + case _ => + } + }) + super.getDrops(state, newCtx) + } + + override def playerWillDestroy(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { + if (!world.isClientSide && player.isCreative) { + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Microcontroller => + Block.dropResources(state, world, pos, tileEntity, player, player.getMainHandItem) + case _ => + } + } + super.playerWillDestroy(world, pos, state, player) } } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 283cbe7b25..4776767374 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -8,6 +8,7 @@ import li.cil.oc.Settings import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties @@ -19,6 +20,8 @@ import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.loot.LootContext +import net.minecraft.loot.LootParameters import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand @@ -37,8 +40,7 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.collection.convert.ImplicitConversionsToJava._ import scala.reflect.ClassTag -class Print(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Print]) extends RedstoneAware(props) - with IForgeBlock with traits.CustomDrops[tileentity.Print] { +class Print(props: Properties) extends RedstoneAware(props) with IForgeBlock { setCreativeTab(null) ItemBlacklist.hide(this) @@ -131,21 +133,6 @@ class Print(props: Properties)(protected implicit val tileTag: ClassTag[tileenti } } - override protected def doCustomInit(tileEntity: tileentity.Print, player: LivingEntity, stack: ItemStack): Unit = { - super.doCustomInit(tileEntity, player, stack) - tileEntity.data.loadData(stack) - tileEntity.updateShape() - tileEntity.updateRedstone() - tileEntity.getLevel.getLightEngine.checkBlock(tileEntity.getBlockPos) - } - - override protected def doCustomDrops(tileEntity: tileentity.Print, player: PlayerEntity, willHarvest: Boolean): Unit = { - super.doCustomDrops(tileEntity, player, willHarvest) - if (!player.isCreative) { - InventoryUtils.spawnStackInWorld(tileEntity.position, tileEntity.data.createItemStack()) - } - } - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { world.getBlockEntity(pos) match { case print: tileentity.Print if print.data.emitRedstone(print.state) => @@ -157,4 +144,27 @@ class Print(props: Properties)(protected implicit val tileTag: ClassTag[tileenti } super.onRemove(state, world, pos, newState, moved) } + + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { + super.setPlacedBy(world, pos, state, placer, stack) + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Print => { + tileEntity.data.loadData(stack) + tileEntity.updateShape() + tileEntity.updateRedstone() + tileEntity.getLevel.getLightEngine.checkBlock(tileEntity.getBlockPos) + } + case _ => + } + } + + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { + val newCtx = ctx.withDynamicDrop(LootFunctions.DYN_ITEM_DATA, (c, f) => { + c.getParamOrNull(LootParameters.BLOCK_ENTITY) match { + case tileEntity: tileentity.Print => f.accept(tileEntity.data.createItemStack()) + case _ => + } + }) + super.getDrops(state, newCtx) + } } diff --git a/src/main/scala/li/cil/oc/common/block/Raid.scala b/src/main/scala/li/cil/oc/common/block/Raid.scala index bbb9234757..0bb5fb63aa 100644 --- a/src/main/scala/li/cil/oc/common/block/Raid.scala +++ b/src/main/scala/li/cil/oc/common/block/Raid.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.RaidData import li.cil.oc.common.tileentity +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block @@ -17,6 +18,8 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateContainer +import net.minecraft.loot.LootContext +import net.minecraft.loot.LootParameters import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.util.text.ITextComponent @@ -27,8 +30,7 @@ import net.minecraftforge.common.extensions.IForgeBlock import scala.reflect.ClassTag -class Raid(props: Properties)(protected implicit val tileTag: ClassTag[tileentity.Raid]) - extends SimpleBlock(props) with IForgeBlock with traits.GUI with traits.CustomDrops[tileentity.Raid] { +class Raid(props: Properties) extends SimpleBlock(props) with IForgeBlock with traits.GUI { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) @@ -62,31 +64,52 @@ class Raid(props: Properties)(protected implicit val tileTag: ClassTag[tileentit case _ => 0 } - override protected def doCustomInit(tileEntity: tileentity.Raid, player: LivingEntity, stack: ItemStack): Unit = { - super.doCustomInit(tileEntity, player, stack) - if (!tileEntity.world.isClientSide) { - val data = new RaidData(stack) - for (i <- 0 until math.min(data.disks.length, tileEntity.getContainerSize)) { - tileEntity.setItem(i, data.disks(i)) - } - data.label.foreach(tileEntity.label.setLabel) - if (!data.filesystem.isEmpty) { - tileEntity.tryCreateRaid(data.filesystem.getCompound("node").getString("address")) - tileEntity.filesystem.foreach(_.loadData(data.filesystem)) + override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { + super.setPlacedBy(world, pos, state, placer, stack) + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Raid if !world.isClientSide => { + val data = new RaidData(stack) + for (i <- 0 until math.min(data.disks.length, tileEntity.getContainerSize)) { + tileEntity.setItem(i, data.disks(i)) + } + data.label.foreach(tileEntity.label.setLabel) + if (!data.filesystem.isEmpty) { + tileEntity.tryCreateRaid(data.filesystem.getCompound("node").getString("address")) + tileEntity.filesystem.foreach(_.loadData(data.filesystem)) + } } + case _ => } } - override protected def doCustomDrops(tileEntity: tileentity.Raid, player: PlayerEntity, willHarvest: Boolean): Unit = { - super.doCustomDrops(tileEntity, player, willHarvest) - val stack = createItemStack() - if (tileEntity.items.exists(!_.isEmpty)) { - val data = new RaidData() - data.disks = tileEntity.items.clone() - tileEntity.filesystem.foreach(_.saveData(data.filesystem)) - data.label = Option(tileEntity.label.getLabel) - data.saveData(stack) + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { + val newCtx = ctx.withDynamicDrop(LootFunctions.DYN_ITEM_DATA, (c, f) => { + c.getParamOrNull(LootParameters.BLOCK_ENTITY) match { + case tileEntity: tileentity.Raid => { + val stack = createItemStack() + if (tileEntity.items.exists(!_.isEmpty)) { + val data = new RaidData() + data.disks = tileEntity.items.clone() + tileEntity.filesystem.foreach(_.saveData(data.filesystem)) + data.label = Option(tileEntity.label.getLabel) + data.saveData(stack) + } + f.accept(stack) + } + case _ => + } + }) + super.getDrops(state, newCtx) + } + + override def playerWillDestroy(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { + if (!world.isClientSide && player.isCreative) { + world.getBlockEntity(pos) match { + case tileEntity: tileentity.Raid if tileEntity.items.exists(!_.isEmpty) => + Block.dropResources(state, world, pos, tileEntity, player, player.getMainHandItem) + case _ => + } } - Block.popResource(tileEntity.world, tileEntity.getBlockPos, stack) + super.playerWillDestroy(world, pos, state, player) } } diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index c618711f96..6fb29c37c6 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -13,6 +13,7 @@ import li.cil.oc.common.tileentity import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.PacketSender import li.cil.oc.server.agent +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Rarity @@ -149,8 +150,6 @@ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.Sta // ----------------------------------------------------------------------- // override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { - val list = new java.util.ArrayList[ItemStack]() - // Superspecial hack... usually this will not work, because Minecraft calls // this method *after* the block has already been destroyed. Meaning we // won't have access to the tile entity. @@ -162,24 +161,26 @@ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.Sta // mod calls this before the block is broken *and* calls removedByPlayer // this will lead to dupes, but in some initial testing this wasn't the // case anywhere (TE autonomous activator, CC turtles). - ctx.getParameter(LootParameters.BLOCK_ENTITY) match { - case proxy: tileentity.RobotProxy => - val robot = proxy.robot - if (robot.node != null) { - // Update: even more special hack! As discussed here http://git.io/IcNAyg - // some mods call this even when they're not about to actually break the - // block... soooo we need a whitelist to know when to generate a *proper* - // drop (i.e. with file systems closed / open handles not saved, e.g.). - if (gettingDropsForActualDrop) { - robot.node.remove() - robot.saveComponents() + val newCtx = ctx.withDynamicDrop(LootFunctions.DYN_ITEM_DATA, (c, f) => { + c.getParamOrNull(LootParameters.BLOCK_ENTITY) match { + case proxy: tileentity.RobotProxy => + val robot = proxy.robot + if (robot.node != null) { + // Update: even more special hack! As discussed here http://git.io/IcNAyg + // some mods call this even when they're not about to actually break the + // block... soooo we need a whitelist to know when to generate a *proper* + // drop (i.e. with file systems closed / open handles not saved, e.g.). + if (gettingDropsForActualDrop) { + robot.node.remove() + robot.saveComponents() + } + f.accept(robot.info.createItemStack()) } - list.add(robot.info.createItemStack()) - } - case _ => - } + case _ => + } + }) - list + super.getDrops(state, newCtx) } private val getDropForRealDropCallers = Set( @@ -251,7 +252,7 @@ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.Sta if (robot.player == player) return false robot.node.remove() robot.saveComponents() - InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), robot.info.createItemStack()) + if (player.isCreative) InventoryUtils.spawnStackInWorld(BlockPosition(pos, world), robot.info.createItemStack()) } robot.moveFrom.foreach(fromPos => if (world.getBlockState(fromPos).getBlock == api.Items.get(Constants.BlockName.RobotAfterimage).block) { world.setBlock(fromPos, net.minecraft.block.Blocks.AIR.defaultBlockState, 1) @@ -260,12 +261,4 @@ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.Sta } super.removedByPlayer(state, world, pos, player, willHarvest, fluid) } - - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { - if (moving.get.isEmpty) - super.onRemove(state, world, pos, newState, moved) - else if (!state.is(newState.getBlock)) - // Can't use super.onRemove as that would drop inventory while moving. - world.removeBlockEntity(pos) - } } diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index f68dc14a3d..9be27e17aa 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity.traits.Colored import li.cil.oc.common.tileentity.traits.Inventory import li.cil.oc.common.tileentity.traits.Rotatable +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.Color import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.Tooltip @@ -23,6 +24,8 @@ import net.minecraft.item.DyeColor import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.item.Rarity +import net.minecraft.loot.LootContext +import net.minecraft.loot.LootParameters import net.minecraft.tileentity.TileEntity import net.minecraft.util.ActionResultType import net.minecraft.util.Direction @@ -136,13 +139,25 @@ abstract class SimpleBlock(props: Properties) extends ContainerBlock(props) { def getValidRotations(world: World, pos: BlockPos): Array[Direction] = validRotations_ - @Deprecated - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { - if (!world.isClientSide && !newState.is(state.getBlock)) world.getBlockEntity(pos) match { + override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = { + val newCtx = ctx.getOptionalParameter(LootParameters.BLOCK_ENTITY) match { + case _: Inventory => ctx.withDynamicDrop(LootFunctions.DYN_VOLATILE_CONTENTS, (c, f) => { + c.getParamOrNull(LootParameters.BLOCK_ENTITY) match { + case inventory: Inventory => inventory.forAllLoot(f) + case _ => + } + }) + case _ => ctx + } + super.getDrops(state, newCtx) + } + + override def playerWillDestroy(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { + if (!world.isClientSide && player.isCreative) world.getBlockEntity(pos) match { case inventory: Inventory => inventory.dropAllSlots() case _ => // Ignore. } - super.onRemove(state, world, pos, newState, moved) + super.playerWillDestroy(world, pos, state, player) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala b/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala deleted file mode 100644 index 3841b5c89d..0000000000 --- a/src/main/scala/li/cil/oc/common/block/traits/CustomDrops.scala +++ /dev/null @@ -1,55 +0,0 @@ -package li.cil.oc.common.block.traits - -import java.util - -import li.cil.oc.common.block.SimpleBlock -import net.minecraft.block.BlockState -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.fluid.FluidState -import net.minecraft.item.ItemStack -import net.minecraft.loot.LootContext -import net.minecraft.tileentity.TileEntity -import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockReader -import net.minecraft.world.World - -import scala.reflect.ClassTag - -trait CustomDrops[Tile <: TileEntity] extends SimpleBlock { - protected def tileTag: ClassTag[Tile] - - override def getDrops(state: BlockState, ctx: LootContext.Builder): util.List[ItemStack] = new util.ArrayList[ItemStack]() - - @Deprecated - override def onRemove(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean): Unit = { - // Copied from vanilla, can't use super as that also drops the contents. - if (state.hasTileEntity && (!state.is(newState.getBlock) || !newState.hasTileEntity)) { - world.removeBlockEntity(pos) - } - } - - override def removedByPlayer(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, willHarvest: Boolean, fluid: FluidState): Boolean = { - if (!world.isClientSide) { - val matcher = tileTag - world.getBlockEntity(pos) match { - case matcher(tileEntity) => doCustomDrops(tileEntity, player, willHarvest) - case _ => - } - } - super.removedByPlayer(state, world, pos, player, willHarvest, fluid) - } - - override def setPlacedBy(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity, stack: ItemStack): Unit = { - super.setPlacedBy(world, pos, state, placer, stack) - val matcher = tileTag - world.getBlockEntity(pos) match { - case matcher(tileEntity) => doCustomInit(tileEntity, placer, stack) - case _ => - } - } - - protected def doCustomInit(tileEntity: Tile, player: LivingEntity, stack: ItemStack): Unit = {} - - protected def doCustomDrops(tileEntity: Tile, player: PlayerEntity, willHarvest: Boolean): Unit = {} -} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 3b87a685b9..33f87eaf6e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.tileentity import java.util +import java.util.function.Consumer import li.cil.oc.Constants import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute @@ -288,4 +289,13 @@ class Microcontroller(selfType: TileEntityType[_ <: Microcontroller]) extends Ti EmptyStack } } + + // Uses the loot system, so still nope. + override def forAllLoot(dst: Consumer[ItemStack]) = Unit + + // Nope. + override def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction]) = false + + // Nope. + override def dropAllSlots() = Unit } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index b748b68c77..1e533ceeb7 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.tileentity import java.util.UUID +import java.util.function.Consumer import li.cil.oc.Settings import li.cil.oc.api @@ -83,6 +84,13 @@ class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) wit } } + // Uses the loot system, so nope. + override def forAllLoot(dst: Consumer[ItemStack]) = Unit + + override def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction]) = false + + override def dropAllSlots() = Unit + def tryCreateRaid(id: String) { if (items.count(!_.isEmpty) == items.length && filesystem.fold(true)(fs => fs.node == null || fs.node.address != id)) { filesystem.foreach(fs => if (fs.node != null) fs.node.remove()) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 7bdf4c0e2f..ec7a82e547 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.tileentity import java.util.UUID +import java.util.function.Consumer import li.cil.oc._ import li.cil.oc.api.Driver @@ -778,6 +779,20 @@ class Robot extends TileEntity(TileEntityTypes.ROBOT) with traits.Computer with // ----------------------------------------------------------------------- // + override def forAllLoot(dst: Consumer[ItemStack]) { + Option(getItem(0)) match { + case Some(stack) if stack.getCount > 0 => dst.accept(stack) + case _ => + } + for (slot <- containerSlots) { + Option(getItem(slot)) match { + case Some(stack) if stack.getCount > 0 => dst.accept(stack) + case _ => + } + } + InventoryUtils.forAllSlots(mainInventory, dst) + } + override def dropSlot(slot: Int, count: Int, direction: Option[Direction]): Boolean = InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), mainInventory, slot, count, direction) diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 6149c0281b..20651441e0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.tileentity import java.util.UUID +import java.util.function.Consumer import li.cil.oc.api import li.cil.oc.api.internal @@ -279,6 +280,8 @@ class RobotProxy(selfType: TileEntityType[_ <: RobotProxy], val robot: Robot) ex override def stillValid(player: PlayerEntity): Boolean = robot.stillValid(player) + override def forAllLoot(dst: Consumer[ItemStack]): Unit = robot.forAllLoot(dst) + override def dropSlot(slot: Int, count: Int, direction: Option[Direction]): Boolean = robot.dropSlot(slot, count, direction) override def dropAllSlots(): Unit = robot.dropAllSlots() diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala index 52ad951e11..f08db57d6a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala @@ -1,5 +1,7 @@ package li.cil.oc.common.tileentity.traits +import java.util.function.Consumer + import li.cil.oc.common.inventory import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils @@ -35,6 +37,8 @@ trait Inventory extends TileEntity with inventory.Inventory { // ----------------------------------------------------------------------- // + def forAllLoot(dst: Consumer[ItemStack]): Unit = InventoryUtils.forAllSlots(this, dst) + def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction] = None) = InventoryUtils.dropSlot(BlockPosition(x, y, z, getLevel), this, slot, count, direction) diff --git a/src/main/scala/li/cil/oc/server/loot/CopyColor.java b/src/main/scala/li/cil/oc/server/loot/CopyColor.java new file mode 100644 index 0000000000..5266a33918 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/loot/CopyColor.java @@ -0,0 +1,60 @@ +package li.cil.oc.server.loot; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import li.cil.oc.api.internal.Colored; +import li.cil.oc.util.ItemColorizer; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.loot.LootContext; +import net.minecraft.loot.LootFunction; +import net.minecraft.loot.LootFunctionType; +import net.minecraft.loot.LootParameters; +import net.minecraft.loot.functions.ILootFunction; +import net.minecraft.loot.conditions.ILootCondition; + +public final class CopyColor extends LootFunction { + private CopyColor(ILootCondition[] conditions) { + super(conditions); + } + + @Override + public LootFunctionType getType() { + return LootFunctions.COPY_COLOR; + } + + public static class Builder extends LootFunction.Builder { + @Override + protected Builder getThis() { + return this; + } + + @Override + public ILootFunction build() { + return new CopyColor(getConditions()); + } + } + + public static Builder copyColor() { + return new Builder(); + } + + @Override + public ItemStack run(ItemStack stack, LootContext ctx) { + if (stack.isEmpty()) return stack; + TileEntity te = ctx.getParamOrNull(LootParameters.BLOCK_ENTITY); + if (te != null && te instanceof Colored) { + // Can't use capability because it's already invalid - block breaks before drops are calculated. + ItemColorizer.setColor(stack, ((Colored) te).getColor()); + } + else ItemColorizer.removeColor(stack); + return stack; + } + + public static class Serializer extends LootFunction.Serializer { + @Override + public CopyColor deserialize(JsonObject src, JsonDeserializationContext ctx, ILootCondition[] conditions) { + return new CopyColor(conditions); + } + } +} diff --git a/src/main/scala/li/cil/oc/server/loot/LootFunctions.java b/src/main/scala/li/cil/oc/server/loot/LootFunctions.java new file mode 100644 index 0000000000..5233f90e06 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/loot/LootFunctions.java @@ -0,0 +1,29 @@ +package li.cil.oc.server.loot; + +import li.cil.oc.OpenComputers; +import net.minecraft.loot.ILootSerializer; +import net.minecraft.loot.LootFunctionType; +import net.minecraft.loot.functions.ILootFunction; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; + +// No registry events or ObjectHolder - this is to load the class. +@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) +public final class LootFunctions { + public static final ResourceLocation DYN_ITEM_DATA = new ResourceLocation(OpenComputers.ID(), "item_data"); + public static final ResourceLocation DYN_VOLATILE_CONTENTS = new ResourceLocation(OpenComputers.ID(), "volatile_contents"); + + public static final LootFunctionType SET_COLOR = register("set_color", new SetColor.Serializer()); + public static final LootFunctionType COPY_COLOR = register("copy_color", new CopyColor.Serializer()); + + private static LootFunctionType register(String name, ILootSerializer serializer) { + LootFunctionType type = new LootFunctionType(serializer); + Registry.register(Registry.LOOT_FUNCTION_TYPE, new ResourceLocation(OpenComputers.ID(), name), type); + return type; + } + + private LootFunctions() { + } +} diff --git a/src/main/scala/li/cil/oc/server/loot/SetColor.java b/src/main/scala/li/cil/oc/server/loot/SetColor.java new file mode 100644 index 0000000000..61b247fb29 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/loot/SetColor.java @@ -0,0 +1,88 @@ +package li.cil.oc.server.loot; + +import java.util.OptionalInt; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import li.cil.oc.util.ItemColorizer; +import net.minecraft.item.ItemStack; +import net.minecraft.loot.LootContext; +import net.minecraft.loot.LootFunction; +import net.minecraft.loot.LootFunctionType; +import net.minecraft.loot.functions.ILootFunction; +import net.minecraft.loot.conditions.ILootCondition; +import net.minecraft.util.JSONUtils; + +public final class SetColor extends LootFunction { + private OptionalInt color; + + private SetColor(ILootCondition[] conditions, OptionalInt color) { + super(conditions); + this.color = color; + } + + @Override + public LootFunctionType getType() { + return LootFunctions.SET_COLOR; + } + + @Override + public ItemStack run(ItemStack stack, LootContext ctx) { + if (stack.isEmpty()) return stack; + if (color.isPresent()) { + ItemColorizer.setColor(stack, color.getAsInt()); + } + else ItemColorizer.removeColor(stack); + return stack; + } + + public static class Builder extends LootFunction.Builder { + private OptionalInt color = OptionalInt.empty(); + + @Override + protected Builder getThis() { + return this; + } + + public Builder withoutColor() { + color = OptionalInt.empty(); + return this; + } + + public Builder withColor(int color) { + if (color < 0 || color > 0xFFFFFF) throw new IllegalArgumentException("Invalid RGB color: " + color); + this.color = OptionalInt.of(color); + return this; + } + + @Override + public ILootFunction build() { + return new SetColor(getConditions(), color); + } + } + + public static Builder setColor() { + return new Builder(); + } + + public static class Serializer extends LootFunction.Serializer { + @Override + public void serialize(JsonObject dst, SetColor src, JsonSerializationContext ctx) { + super.serialize(dst, src, ctx); + src.color.ifPresent(v -> dst.add("color", new JsonPrimitive(v))); + } + + @Override + public SetColor deserialize(JsonObject src, JsonDeserializationContext ctx, ILootCondition[] conditions) { + if (src.has("color")) { + int color = JSONUtils.getAsInt(src, "color"); + if (color < 0 || color > 0xFFFFFF) throw new JsonParseException("Invalid RGB color: " + color); + return new SetColor(conditions, OptionalInt.of(color)); + } + else return new SetColor(conditions, OptionalInt.empty()); + } + } +} diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 4dcc4e079d..5157528e2e 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -1,6 +1,7 @@ package li.cil.oc.util import java.util.Optional +import java.util.function.Consumer import li.cil.oc.OpenComputers import li.cil.oc.util.ExtendedWorld._ @@ -329,6 +330,19 @@ object InventoryUtils { case _ => null } + /** + * Utility method mirroring dropAllSlots but instead piping slots into + * a provided consumer for use with LootContext. + */ + def forAllSlots(inventory: IInventory, dst: Consumer[ItemStack]): Unit = { + for (slot <- 0 until inventory.getContainerSize) { + StackOption(inventory.getItem(slot)) match { + case SomeStack(stack) if stack.getCount > 0 => dst.accept(stack) + case _ => // Nothing. + } + } + } + /** * Utility method for dropping contents from a single inventory slot into * the world. From 0a7311fdbad80c9cb77c3f6cee069aaba27f7cb4 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 5 Oct 2022 20:17:47 +0200 Subject: [PATCH 105/159] Remove creative tab facade from blocks --- src/main/scala/li/cil/oc/common/block/Item.scala | 10 +--------- .../scala/li/cil/oc/common/block/Microcontroller.scala | 1 - .../scala/li/cil/oc/common/block/PowerConverter.scala | 1 - src/main/scala/li/cil/oc/common/block/Print.scala | 1 - .../scala/li/cil/oc/common/block/RobotAfterimage.scala | 1 - src/main/scala/li/cil/oc/common/block/RobotProxy.scala | 1 - .../scala/li/cil/oc/common/block/SimpleBlock.scala | 9 --------- src/main/scala/li/cil/oc/common/init/Blocks.scala | 10 ++++++---- src/main/scala/li/cil/oc/common/init/Items.scala | 5 ++--- 9 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 14985222c4..978a53f5f4 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -23,15 +23,7 @@ import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent -object Item { - def setCreativeTab(block: Block, props: Properties): Properties = block match { - case simple: SimpleBlock => - if (simple.getCreativeTab != null) props.tab(simple.getCreativeTab) else props - case _ => props - } -} - -class Item(value: Block, props: Properties) extends BlockItem(value, Item.setCreativeTab(value, props)) { +class Item(value: Block, props: Properties) extends BlockItem(value, props) { override def getRarity(stack: ItemStack): Rarity = getBlock match { case simple: SimpleBlock => simple.rarity(stack) case _ => Rarity.COMMON diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index c040d1aee4..101f363a4d 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -43,7 +43,6 @@ import scala.reflect.ClassTag class Microcontroller(props: Properties) extends RedstoneAware(props) with IForgeBlock with traits.PowerAcceptor with traits.StateAware { - setCreativeTab(null) ItemBlacklist.hide(this) protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index bb28c59ce2..4b50df9253 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -16,7 +16,6 @@ import net.minecraft.world.World class PowerConverter(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor { if (Settings.get.ignorePower) { - setCreativeTab(null) ItemBlacklist.hide(this) } diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 4776767374..50c7b26382 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -41,7 +41,6 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.reflect.ClassTag class Print(props: Properties) extends RedstoneAware(props) with IForgeBlock { - setCreativeTab(null) ItemBlacklist.hide(this) @Deprecated diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index f00802903f..4bd2191c0f 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -29,7 +29,6 @@ import net.minecraft.world.World import net.minecraft.world.server.ServerWorld class RobotAfterimage(props: Properties) extends SimpleBlock(props) { - setCreativeTab(null) ItemBlacklist.hide(this) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 6fb29c37c6..85f957798f 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -47,7 +47,6 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.StateAware { - setCreativeTab(null) ItemBlacklist.hide(this) val shape = VoxelShapes.box(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 9be27e17aa..1d08b105e6 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -48,15 +48,6 @@ import net.minecraftforge.common.ToolType import scala.collection.convert.ImplicitConversionsToScala._ abstract class SimpleBlock(props: Properties) extends ContainerBlock(props) { - @Deprecated - private var creativeTab: ItemGroup = CreativeTab - - @Deprecated - def getCreativeTab = creativeTab - - @Deprecated - protected def setCreativeTab(tab: ItemGroup) = creativeTab = tab - @Deprecated private var unlocalizedName = super.getDescriptionId() diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index f687c551a0..a679c38714 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.init import li.cil.oc.Constants +import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.common.block._ @@ -30,7 +31,8 @@ object Blocks { Items.registerBlock(new Keyboard(Properties.of(Material.STONE).strength(2, 5).noOcclusion), Constants.BlockName.Keyboard) Items.registerBlock(new MotionSensor(defaultProps), Constants.BlockName.MotionSensor) Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter) - Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor) + Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor, + tab = if (Settings.get.ignorePower) CreativeTab else null) Items.registerBlock(new Printer(defaultProps), Constants.BlockName.Printer) Items.registerBlock(new Raid(defaultProps), Constants.BlockName.Raid) Items.registerBlock(new Redstone(defaultProps), Constants.BlockName.Redstone) @@ -42,10 +44,10 @@ object Blocks { Items.registerBlock(new Waypoint(defaultProps), Constants.BlockName.Waypoint) Items.registerBlock(new Case(defaultProps, Tier.Four), Constants.BlockName.CaseCreative) - Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller) - Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print) + Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller, tab = null) + Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print, tab = null) Items.registerBlockOnly(new RobotAfterimage(Properties.of(Material.AIR).instabreak.noOcclusion.dynamicShape.air), Constants.BlockName.RobotAfterimage) - Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot) + Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot, tab = null) // v1.5.10 Items.registerBlock(new FakeEndstone(Properties.of(Material.STONE).strength(3, 15)), Constants.BlockName.Endstone) diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 8a7a406399..96ebb932b2 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -82,7 +82,7 @@ object Items extends ItemAPI { instance } - def registerBlock(instance: Block, id: String): Block = { + def registerBlock(instance: Block, id: String, tab: ItemGroup = CreativeTab): Block = { if (!descriptors.contains(id)) { instance match { case simple: SimpleBlock => @@ -90,8 +90,7 @@ object Items extends ItemAPI { simple.setRegistryName(OpenComputers.ID, id) GameData.register_impl[Block](simple) - val props = defaultProps.tab(simple.getCreativeTab) - val item : Item = new common.block.Item(simple, props) + val item : Item = new common.block.Item(simple, new Properties().tab(tab)) item.setRegistryName(OpenComputers.ID, id) GameData.register_impl(item) OpenComputers.proxy.registerModel(item, id) From 6d1e73d5adb82d4bec059ed45ec3e21d1daba353 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 5 Oct 2022 22:45:47 +0200 Subject: [PATCH 106/159] Move item and block rarities into Properties --- .../scala/li/cil/oc/common/block/Case.scala | 3 - .../li/cil/oc/common/block/Hologram.scala | 2 - .../scala/li/cil/oc/common/block/Item.scala | 18 ++- .../cil/oc/common/block/Microcontroller.scala | 5 - .../cil/oc/common/block/RobotAfterimage.scala | 7 - .../li/cil/oc/common/block/RobotProxy.scala | 5 - .../scala/li/cil/oc/common/block/Screen.scala | 3 - .../li/cil/oc/common/block/SimpleBlock.scala | 3 - .../scala/li/cil/oc/common/init/Blocks.scala | 76 +++++------ .../scala/li/cil/oc/common/init/Items.scala | 126 +++++++++--------- .../scala/li/cil/oc/common/item/APU.scala | 7 - .../li/cil/oc/common/item/ComponentBus.scala | 10 -- .../scala/li/cil/oc/common/item/Drone.scala | 1 - .../li/cil/oc/common/item/HoverBoots.scala | 3 - .../li/cil/oc/common/item/Microchip.scala | 4 - .../li/cil/oc/common/item/Nanomachines.scala | 3 - .../scala/li/cil/oc/common/item/Server.scala | 5 - .../scala/li/cil/oc/common/item/Tablet.scala | 1 - .../cil/oc/common/item/UpgradeDatabase.scala | 4 - 19 files changed, 115 insertions(+), 171 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala index 4fc6e74dbb..58ab6f67db 100644 --- a/src/main/scala/li/cil/oc/common/block/Case.scala +++ b/src/main/scala/li/cil/oc/common/block/Case.scala @@ -6,7 +6,6 @@ import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity -import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Block @@ -33,8 +32,6 @@ class Case(props: Properties, val tier: Int) extends RedstoneAware(props) with t // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase, slots)) { tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) diff --git a/src/main/scala/li/cil/oc/common/block/Hologram.scala b/src/main/scala/li/cil/oc/common/block/Hologram.scala index e25d62b13e..1dae9a89ec 100644 --- a/src/main/scala/li/cil/oc/common/block/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/block/Hologram.scala @@ -31,8 +31,6 @@ class Hologram(props: Properties, val tier: Int) extends SimpleBlock(props) { // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { for (curr <- Tooltip.get(getClass.getSimpleName.toLowerCase() + tier)) { tooltip.add(new StringTextComponent(curr).setStyle(Tooltip.DefaultStyle)) diff --git a/src/main/scala/li/cil/oc/common/block/Item.scala b/src/main/scala/li/cil/oc/common/block/Item.scala index 978a53f5f4..6f11d82e38 100644 --- a/src/main/scala/li/cil/oc/common/block/Item.scala +++ b/src/main/scala/li/cil/oc/common/block/Item.scala @@ -5,28 +5,38 @@ import java.util import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.common.block +import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity import li.cil.oc.util.Color import li.cil.oc.util.ItemColorizer +import li.cil.oc.util.Rarity import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.item // Rarity import net.minecraft.item.BlockItem import net.minecraft.item.BlockItemUseContext import net.minecraft.item.DyeColor import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack -import net.minecraft.item.Rarity import net.minecraft.util.Direction import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent class Item(value: Block, props: Properties) extends BlockItem(value, props) { - override def getRarity(stack: ItemStack): Rarity = getBlock match { - case simple: SimpleBlock => simple.rarity(stack) - case _ => Rarity.COMMON + override def getRarity(stack: ItemStack): item.Rarity = getBlock match { + case _: block.Microcontroller => { + val data = new MicrocontrollerData(stack) + Rarity.byTier(data.tier) + } + case _: block.RobotProxy => { + val data = new RobotData(stack) + Rarity.byTier(data.tier) + } + case _ => super.getRarity(stack) } override def getName(stack: ItemStack): ITextComponent = { diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index 101f363a4d..5a54346ce1 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -68,11 +68,6 @@ class Microcontroller(props: Properties) } } - override def rarity(stack: ItemStack): item.Rarity = { - val data = new MicrocontrollerData(stack) - Rarity.byTier(data.tier) - } - // ----------------------------------------------------------------------- // override def energyThroughput: Double = Settings.get.caseRate(Tier.One) diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index 4bd2191c0f..b9a94e564e 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -55,13 +55,6 @@ class RobotAfterimage(props: Properties) extends SimpleBlock(props) { // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack) = { - val data = new RobotData(stack) - Rarity.byTier(data.tier) - } - - // ----------------------------------------------------------------------- // - override def onPlace(state: BlockState, world: World, pos: BlockPos, prevState: BlockState, moved: Boolean): Unit = { if (!world.isClientSide) { world.asInstanceOf[ServerWorld].getBlockTicks.scheduleTick(pos, this, Math.max((Settings.get.moveDelay * 20).toInt, 1) - 1) diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 85f957798f..2e2ea73ea4 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -83,11 +83,6 @@ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.Sta // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack): item.Rarity = { - val data = new RobotData(stack) - Rarity.byTier(data.tier) - } - override protected def tooltipHead(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { super.tooltipHead(stack, world, tooltip, advanced) addLines(stack, tooltip) diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 7426c55301..65a443bb75 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -11,7 +11,6 @@ import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.tileentity import li.cil.oc.integration.util.Wrench import li.cil.oc.util.PackedColor -import li.cil.oc.util.Rarity import li.cil.oc.util.RotationHelper import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties @@ -41,8 +40,6 @@ class Screen(props: Properties, val tier: Int) extends RedstoneAware(props) { // ----------------------------------------------------------------------- // - override def rarity(stack: ItemStack) = Rarity.byTier(tier) - override protected def tooltipBody(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], advanced: ITooltipFlag) { val (w, h) = Settings.screenResolutionsByTier(tier) val depth = PackedColor.Depth.bits(Settings.screenDepthsByTier(tier)) diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 1d08b105e6..5249dc1216 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -23,7 +23,6 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.DyeColor import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack -import net.minecraft.item.Rarity import net.minecraft.loot.LootContext import net.minecraft.loot.LootParameters import net.minecraft.tileentity.TileEntity @@ -69,8 +68,6 @@ abstract class SimpleBlock(props: Properties) extends ContainerBlock(props) { // BlockItem // ----------------------------------------------------------------------- // - def rarity(stack: ItemStack) = Rarity.COMMON - @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: IBlockReader, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { tooltipHead(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index a679c38714..647f669e23 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -5,60 +5,60 @@ import li.cil.oc.CreativeTab import li.cil.oc.Settings import li.cil.oc.common.Tier import li.cil.oc.common.block._ -import li.cil.oc.common.tileentity import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.material.Material -import net.minecraft.tileentity.TileEntity -import net.minecraftforge.fml.common.registry.GameRegistry +import net.minecraft.item.Rarity +import net.minecraft.item.Item object Blocks { def init() { def defaultProps = Properties.of(Material.METAL).strength(2, 5) - Items.registerBlock(new Adapter(defaultProps), Constants.BlockName.Adapter) - Items.registerBlock(new Assembler(defaultProps), Constants.BlockName.Assembler) - Items.registerBlock(new Cable(defaultProps), Constants.BlockName.Cable) - Items.registerBlock(new Capacitor(defaultProps), Constants.BlockName.Capacitor) - Items.registerBlock(new Case(defaultProps, Tier.One), Constants.BlockName.CaseTier1) - Items.registerBlock(new Case(defaultProps, Tier.Three), Constants.BlockName.CaseTier3) - Items.registerBlock(new Case(defaultProps, Tier.Two), Constants.BlockName.CaseTier2) - Items.registerBlock(new ChameliumBlock(Properties.of(Material.STONE).strength(2, 5)), Constants.BlockName.ChameliumBlock) - Items.registerBlock(new Charger(defaultProps), Constants.BlockName.Charger) - Items.registerBlock(new Disassembler(defaultProps), Constants.BlockName.Disassembler) - Items.registerBlock(new DiskDrive(defaultProps), Constants.BlockName.DiskDrive) - Items.registerBlock(new Geolyzer(defaultProps), Constants.BlockName.Geolyzer) - Items.registerBlock(new Hologram(defaultProps, Tier.One), Constants.BlockName.HologramTier1) - Items.registerBlock(new Hologram(defaultProps, Tier.Two), Constants.BlockName.HologramTier2) - Items.registerBlock(new Keyboard(Properties.of(Material.STONE).strength(2, 5).noOcclusion), Constants.BlockName.Keyboard) - Items.registerBlock(new MotionSensor(defaultProps), Constants.BlockName.MotionSensor) - Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter) + def defaultItemProps = new Item.Properties().tab(CreativeTab) + Items.registerBlock(new Adapter(defaultProps), Constants.BlockName.Adapter, defaultItemProps) + Items.registerBlock(new Assembler(defaultProps), Constants.BlockName.Assembler, defaultItemProps) + Items.registerBlock(new Cable(defaultProps), Constants.BlockName.Cable, defaultItemProps) + Items.registerBlock(new Capacitor(defaultProps), Constants.BlockName.Capacitor, defaultItemProps) + Items.registerBlock(new Case(defaultProps, Tier.One), Constants.BlockName.CaseTier1, defaultItemProps) + Items.registerBlock(new Case(defaultProps, Tier.Three), Constants.BlockName.CaseTier3, defaultItemProps.rarity(Rarity.RARE)) + Items.registerBlock(new Case(defaultProps, Tier.Two), Constants.BlockName.CaseTier2, defaultItemProps.rarity(Rarity.UNCOMMON)) + Items.registerBlock(new ChameliumBlock(Properties.of(Material.STONE).strength(2, 5)), Constants.BlockName.ChameliumBlock, defaultItemProps) + Items.registerBlock(new Charger(defaultProps), Constants.BlockName.Charger, defaultItemProps) + Items.registerBlock(new Disassembler(defaultProps), Constants.BlockName.Disassembler, defaultItemProps) + Items.registerBlock(new DiskDrive(defaultProps), Constants.BlockName.DiskDrive, defaultItemProps) + Items.registerBlock(new Geolyzer(defaultProps), Constants.BlockName.Geolyzer, defaultItemProps) + Items.registerBlock(new Hologram(defaultProps, Tier.One), Constants.BlockName.HologramTier1, defaultItemProps) + Items.registerBlock(new Hologram(defaultProps, Tier.Two), Constants.BlockName.HologramTier2, defaultItemProps.rarity(Rarity.UNCOMMON)) + Items.registerBlock(new Keyboard(Properties.of(Material.STONE).strength(2, 5).noOcclusion), Constants.BlockName.Keyboard, defaultItemProps) + Items.registerBlock(new MotionSensor(defaultProps), Constants.BlockName.MotionSensor, defaultItemProps) + Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter, defaultItemProps) Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor, - tab = if (Settings.get.ignorePower) CreativeTab else null) - Items.registerBlock(new Printer(defaultProps), Constants.BlockName.Printer) - Items.registerBlock(new Raid(defaultProps), Constants.BlockName.Raid) - Items.registerBlock(new Redstone(defaultProps), Constants.BlockName.Redstone) - Items.registerBlock(new Relay(defaultProps), Constants.BlockName.Relay) - Items.registerBlock(new Screen(defaultProps, Tier.One), Constants.BlockName.ScreenTier1) - Items.registerBlock(new Screen(defaultProps, Tier.Three), Constants.BlockName.ScreenTier3) - Items.registerBlock(new Screen(defaultProps, Tier.Two), Constants.BlockName.ScreenTier2) - Items.registerBlock(new Rack(defaultProps), Constants.BlockName.Rack) - Items.registerBlock(new Waypoint(defaultProps), Constants.BlockName.Waypoint) + new Item.Properties().tab(if (Settings.get.ignorePower) CreativeTab else null)) + Items.registerBlock(new Printer(defaultProps), Constants.BlockName.Printer, defaultItemProps) + Items.registerBlock(new Raid(defaultProps), Constants.BlockName.Raid, defaultItemProps) + Items.registerBlock(new Redstone(defaultProps), Constants.BlockName.Redstone, defaultItemProps) + Items.registerBlock(new Relay(defaultProps), Constants.BlockName.Relay, defaultItemProps) + Items.registerBlock(new Screen(defaultProps, Tier.One), Constants.BlockName.ScreenTier1, defaultItemProps) + Items.registerBlock(new Screen(defaultProps, Tier.Three), Constants.BlockName.ScreenTier3, defaultItemProps.rarity(Rarity.RARE)) + Items.registerBlock(new Screen(defaultProps, Tier.Two), Constants.BlockName.ScreenTier2, defaultItemProps.rarity(Rarity.UNCOMMON)) + Items.registerBlock(new Rack(defaultProps), Constants.BlockName.Rack, defaultItemProps) + Items.registerBlock(new Waypoint(defaultProps), Constants.BlockName.Waypoint, defaultItemProps) - Items.registerBlock(new Case(defaultProps, Tier.Four), Constants.BlockName.CaseCreative) - Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller, tab = null) - Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print, tab = null) + Items.registerBlock(new Case(defaultProps, Tier.Four), Constants.BlockName.CaseCreative, defaultItemProps.rarity(Rarity.EPIC)) + Items.registerBlock(new Microcontroller(defaultProps), Constants.BlockName.Microcontroller, new Item.Properties()) + Items.registerBlock(new Print(Properties.of(Material.METAL).strength(1, 5).noOcclusion.dynamicShape), Constants.BlockName.Print, new Item.Properties()) Items.registerBlockOnly(new RobotAfterimage(Properties.of(Material.AIR).instabreak.noOcclusion.dynamicShape.air), Constants.BlockName.RobotAfterimage) - Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot, tab = null) + Items.registerBlock(new RobotProxy(defaultProps.noOcclusion.dynamicShape), Constants.BlockName.Robot, new Item.Properties()) // v1.5.10 - Items.registerBlock(new FakeEndstone(Properties.of(Material.STONE).strength(3, 15)), Constants.BlockName.Endstone) + Items.registerBlock(new FakeEndstone(Properties.of(Material.STONE).strength(3, 15)), Constants.BlockName.Endstone, defaultItemProps) // v1.5.14 - Items.registerBlock(new NetSplitter(defaultProps), Constants.BlockName.NetSplitter) + Items.registerBlock(new NetSplitter(defaultProps), Constants.BlockName.NetSplitter, defaultItemProps) // v1.5.16 - Items.registerBlock(new Transposer(defaultProps), Constants.BlockName.Transposer) + Items.registerBlock(new Transposer(defaultProps), Constants.BlockName.Transposer, defaultItemProps) // v1.7.2 - Items.registerBlock(new CarpetedCapacitor(defaultProps), Constants.BlockName.CarpetedCapacitor) + Items.registerBlock(new CarpetedCapacitor(defaultProps), Constants.BlockName.CarpetedCapacitor, defaultItemProps) } } diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 96ebb932b2..77060caf18 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -28,7 +28,7 @@ import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack -import net.minecraft.nbt.CompoundNBT +import net.minecraft.item.Rarity import net.minecraft.util.NonNullList import net.minecraft.util.ResourceLocation import net.minecraftforge.common.ToolType @@ -54,8 +54,6 @@ object Items extends ItemAPI { case _ => null } - private def defaultProps = new Properties().tab(CreativeTab) - def registerBlockOnly(instance: Block, id: String): Block = { if (!descriptors.contains(id)) { instance match { @@ -82,7 +80,7 @@ object Items extends ItemAPI { instance } - def registerBlock(instance: Block, id: String, tab: ItemGroup = CreativeTab): Block = { + def registerBlock(instance: Block, id: String, itemProps: Properties): Block = { if (!descriptors.contains(id)) { instance match { case simple: SimpleBlock => @@ -90,7 +88,7 @@ object Items extends ItemAPI { simple.setRegistryName(OpenComputers.ID, id) GameData.register_impl[Block](simple) - val item : Item = new common.block.Item(simple, new Properties().tab(tab)) + val item : Item = new common.block.Item(simple, itemProps) item.setRegistryName(OpenComputers.ID, id) GameData.register_impl(item) OpenComputers.proxy.registerModel(item, id) @@ -331,6 +329,8 @@ object Items extends ItemAPI { // ----------------------------------------------------------------------- // + private def defaultProps = new Properties().tab(CreativeTab) + def init() { initMaterials() initTools() @@ -356,8 +356,8 @@ object Items extends ItemAPI { registerItem(new item.CardBase(defaultProps), Constants.ItemName.Card) registerItem(new item.Transistor(defaultProps), Constants.ItemName.Transistor) registerItem(new item.Microchip(defaultProps, Tier.One), Constants.ItemName.ChipTier1) - registerItem(new item.Microchip(defaultProps, Tier.Two), Constants.ItemName.ChipTier2) - registerItem(new item.Microchip(defaultProps, Tier.Three), Constants.ItemName.ChipTier3) + registerItem(new item.Microchip(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.ChipTier2) + registerItem(new item.Microchip(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.ChipTier3) registerItem(new item.ALU(defaultProps), Constants.ItemName.Alu) registerItem(new item.ControlUnit(defaultProps), Constants.ItemName.ControlUnit) registerItem(new item.Disk(defaultProps), Constants.ItemName.Disk) @@ -367,14 +367,14 @@ object Items extends ItemAPI { registerItem(new item.NumPad(defaultProps), Constants.ItemName.NumPad) registerItem(new item.TabletCase(defaultProps, Tier.One), Constants.ItemName.TabletCaseTier1) - registerItem(new item.TabletCase(defaultProps, Tier.Two), Constants.ItemName.TabletCaseTier2) - registerItem(new item.TabletCase(defaultProps, Tier.Four), Constants.ItemName.TabletCaseCreative) + registerItem(new item.TabletCase(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.TabletCaseTier2) + registerItem(new item.TabletCase(defaultProps.rarity(Rarity.EPIC), Tier.Four), Constants.ItemName.TabletCaseCreative) registerItem(new item.MicrocontrollerCase(defaultProps, Tier.One), Constants.ItemName.MicrocontrollerCaseTier1) - registerItem(new item.MicrocontrollerCase(defaultProps, Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) - registerItem(new item.MicrocontrollerCase(defaultProps, Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) + registerItem(new item.MicrocontrollerCase(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.MicrocontrollerCaseTier2) + registerItem(new item.MicrocontrollerCase(defaultProps.rarity(Rarity.EPIC), Tier.Four), Constants.ItemName.MicrocontrollerCaseCreative) registerItem(new item.DroneCase(defaultProps, Tier.One), Constants.ItemName.DroneCaseTier1) - registerItem(new item.DroneCase(defaultProps, Tier.Two), Constants.ItemName.DroneCaseTier2) - registerItem(new item.DroneCase(defaultProps, Tier.Four), Constants.ItemName.DroneCaseCreative) + registerItem(new item.DroneCase(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.DroneCaseTier2) + registerItem(new item.DroneCase(defaultProps.rarity(Rarity.EPIC), Tier.Four), Constants.ItemName.DroneCaseCreative) registerItem(new item.InkCartridgeEmpty(defaultProps.stacksTo(1)), Constants.ItemName.InkCartridgeEmpty) registerItem(new item.InkCartridge(defaultProps.stacksTo(1).craftRemainder(get(Constants.ItemName.InkCartridgeEmpty).item)), Constants.ItemName.InkCartridge) @@ -395,40 +395,40 @@ object Items extends ItemAPI { registerItem(new item.Wrench(defaultProps.stacksTo(1).addToolType(WrenchType, 1)), Constants.ItemName.Wrench) // 1.5.11 - registerItem(new item.HoverBoots(defaultProps.stacksTo(1).setNoRepair), Constants.ItemName.HoverBoots) + registerItem(new item.HoverBoots(defaultProps.stacksTo(1).rarity(Rarity.UNCOMMON).setNoRepair), Constants.ItemName.HoverBoots) // 1.5.18 - registerItem(new item.Nanomachines(defaultProps), Constants.ItemName.Nanomachines) + registerItem(new item.Nanomachines(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.Nanomachines) } // General purpose components. private def initComponents(): Unit = { registerItem(new item.CPU(defaultProps, Tier.One), Constants.ItemName.CPUTier1) - registerItem(new item.CPU(defaultProps, Tier.Two), Constants.ItemName.CPUTier2) - registerItem(new item.CPU(defaultProps, Tier.Three), Constants.ItemName.CPUTier3) + registerItem(new item.CPU(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.CPUTier2) + registerItem(new item.CPU(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.CPUTier3) registerItem(new item.ComponentBus(defaultProps, Tier.One), Constants.ItemName.ComponentBusTier1) - registerItem(new item.ComponentBus(defaultProps, Tier.Two), Constants.ItemName.ComponentBusTier2) - registerItem(new item.ComponentBus(defaultProps, Tier.Three), Constants.ItemName.ComponentBusTier3) + registerItem(new item.ComponentBus(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.ComponentBusTier2) + registerItem(new item.ComponentBus(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.ComponentBusTier3) registerItem(new item.Memory(defaultProps, Tier.One), Constants.ItemName.RAMTier1) registerItem(new item.Memory(defaultProps, Tier.Two), Constants.ItemName.RAMTier2) - registerItem(new item.Memory(defaultProps, Tier.Three), Constants.ItemName.RAMTier3) - registerItem(new item.Memory(defaultProps, Tier.Four), Constants.ItemName.RAMTier4) - registerItem(new item.Memory(defaultProps, Tier.Five), Constants.ItemName.RAMTier5) - registerItem(new item.Memory(defaultProps, Tier.Six), Constants.ItemName.RAMTier6) + registerItem(new item.Memory(defaultProps.rarity(Rarity.UNCOMMON), Tier.Three), Constants.ItemName.RAMTier3) + registerItem(new item.Memory(defaultProps.rarity(Rarity.UNCOMMON), Tier.Four), Constants.ItemName.RAMTier4) + registerItem(new item.Memory(defaultProps.rarity(Rarity.RARE), Tier.Five), Constants.ItemName.RAMTier5) + registerItem(new item.Memory(defaultProps.rarity(Rarity.RARE), Tier.Six), Constants.ItemName.RAMTier6) - registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Four), Constants.ItemName.ServerCreative) + registerItem(new item.Server(defaultProps.stacksTo(1).rarity(Rarity.EPIC), Tier.Four), Constants.ItemName.ServerCreative) registerItem(new item.Server(defaultProps.stacksTo(1), Tier.One), Constants.ItemName.ServerTier1) - registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Two), Constants.ItemName.ServerTier2) - registerItem(new item.Server(defaultProps.stacksTo(1), Tier.Three), Constants.ItemName.ServerTier3) + registerItem(new item.Server(defaultProps.stacksTo(1).rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.ServerTier2) + registerItem(new item.Server(defaultProps.stacksTo(1).rarity(Rarity.RARE), Tier.Three), Constants.ItemName.ServerTier3) // 1.5.10 - registerItem(new item.APU(defaultProps, Tier.One), Constants.ItemName.APUTier1) - registerItem(new item.APU(defaultProps, Tier.Two), Constants.ItemName.APUTier2) + registerItem(new item.APU(defaultProps.rarity(Rarity.UNCOMMON), Tier.One), Constants.ItemName.APUTier1) + registerItem(new item.APU(defaultProps.rarity(Rarity.RARE), Tier.Two), Constants.ItemName.APUTier2) // 1.5.12 - registerItem(new item.APU(defaultProps, Tier.Three), Constants.ItemName.APUCreative) + registerItem(new item.APU(defaultProps.rarity(Rarity.EPIC), Tier.Three), Constants.ItemName.APUCreative) // 1.6 registerItem(new item.TerminalServer(defaultProps.stacksTo(1)), Constants.ItemName.TerminalServer) @@ -439,64 +439,64 @@ object Items extends ItemAPI { private def initCards(): Unit = { registerItem(new item.DebugCard(defaultProps), Constants.ItemName.DebugCard) registerItem(new item.GraphicsCard(defaultProps, Tier.One), Constants.ItemName.GraphicsCardTier1) - registerItem(new item.GraphicsCard(defaultProps, Tier.Two), Constants.ItemName.GraphicsCardTier2) - registerItem(new item.GraphicsCard(defaultProps, Tier.Three), Constants.ItemName.GraphicsCardTier3) + registerItem(new item.GraphicsCard(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.GraphicsCardTier2) + registerItem(new item.GraphicsCard(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.GraphicsCardTier3) registerItem(new item.RedstoneCard(defaultProps, Tier.One), Constants.ItemName.RedstoneCardTier1) - registerItem(new item.RedstoneCard(defaultProps, Tier.Two), Constants.ItemName.RedstoneCardTier2) + registerItem(new item.RedstoneCard(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.RedstoneCardTier2) registerItem(new item.NetworkCard(defaultProps), Constants.ItemName.NetworkCard) - registerItem(new item.WirelessNetworkCard(defaultProps, Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) - registerItem(new item.InternetCard(defaultProps), Constants.ItemName.InternetCard) - registerItem(new item.LinkedCard(defaultProps), Constants.ItemName.LinkedCard) + registerItem(new item.WirelessNetworkCard(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.WirelessNetworkCardTier2) + registerItem(new item.InternetCard(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.InternetCard) + registerItem(new item.LinkedCard(defaultProps.rarity(Rarity.RARE)), Constants.ItemName.LinkedCard) // 1.5.13 registerItem(new item.DataCard(defaultProps, Tier.One), Constants.ItemName.DataCardTier1) // 1.5.15 - registerItem(new item.DataCard(defaultProps, Tier.Two), Constants.ItemName.DataCardTier2) - registerItem(new item.DataCard(defaultProps, Tier.Three), Constants.ItemName.DataCardTier3) + registerItem(new item.DataCard(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.DataCardTier2) + registerItem(new item.DataCard(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.DataCardTier3) } // Upgrade components. private def initUpgrades(): Unit = { - registerItem(new item.UpgradeAngel(defaultProps), Constants.ItemName.AngelUpgrade) + registerItem(new item.UpgradeAngel(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.AngelUpgrade) registerItem(new item.UpgradeBattery(defaultProps, Tier.One), Constants.ItemName.BatteryUpgradeTier1) - registerItem(new item.UpgradeBattery(defaultProps, Tier.Two), Constants.ItemName.BatteryUpgradeTier2) - registerItem(new item.UpgradeBattery(defaultProps, Tier.Three), Constants.ItemName.BatteryUpgradeTier3) - registerItem(new item.UpgradeChunkloader(defaultProps), Constants.ItemName.ChunkloaderUpgrade) + registerItem(new item.UpgradeBattery(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.BatteryUpgradeTier2) + registerItem(new item.UpgradeBattery(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.BatteryUpgradeTier3) + registerItem(new item.UpgradeChunkloader(defaultProps.rarity(Rarity.RARE)), Constants.ItemName.ChunkloaderUpgrade) registerItem(new item.UpgradeContainerCard(defaultProps, Tier.One), Constants.ItemName.CardContainerTier1) - registerItem(new item.UpgradeContainerCard(defaultProps, Tier.Two), Constants.ItemName.CardContainerTier2) - registerItem(new item.UpgradeContainerCard(defaultProps, Tier.Three), Constants.ItemName.CardContainerTier3) + registerItem(new item.UpgradeContainerCard(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.CardContainerTier2) + registerItem(new item.UpgradeContainerCard(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.CardContainerTier3) registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.One), Constants.ItemName.UpgradeContainerTier1) - registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.Two), Constants.ItemName.UpgradeContainerTier2) - registerItem(new item.UpgradeContainerUpgrade(defaultProps, Tier.Three), Constants.ItemName.UpgradeContainerTier3) - registerItem(new item.UpgradeCrafting(defaultProps), Constants.ItemName.CraftingUpgrade) + registerItem(new item.UpgradeContainerUpgrade(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.UpgradeContainerTier2) + registerItem(new item.UpgradeContainerUpgrade(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.UpgradeContainerTier3) + registerItem(new item.UpgradeCrafting(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.CraftingUpgrade) registerItem(new item.UpgradeDatabase(defaultProps, Tier.One), Constants.ItemName.DatabaseUpgradeTier1) - registerItem(new item.UpgradeDatabase(defaultProps, Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) - registerItem(new item.UpgradeDatabase(defaultProps, Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) - registerItem(new item.UpgradeExperience(defaultProps), Constants.ItemName.ExperienceUpgrade) - registerItem(new item.UpgradeGenerator(defaultProps), Constants.ItemName.GeneratorUpgrade) + registerItem(new item.UpgradeDatabase(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.DatabaseUpgradeTier2) + registerItem(new item.UpgradeDatabase(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.DatabaseUpgradeTier3) + registerItem(new item.UpgradeExperience(defaultProps.rarity(Rarity.RARE)), Constants.ItemName.ExperienceUpgrade) + registerItem(new item.UpgradeGenerator(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.GeneratorUpgrade) registerItem(new item.UpgradeInventory(defaultProps), Constants.ItemName.InventoryUpgrade) - registerItem(new item.UpgradeInventoryController(defaultProps), Constants.ItemName.InventoryControllerUpgrade) - registerItem(new item.UpgradeNavigation(defaultProps), Constants.ItemName.NavigationUpgrade) - registerItem(new item.UpgradePiston(defaultProps), Constants.ItemName.PistonUpgrade) - registerItem(new item.UpgradeSign(defaultProps), Constants.ItemName.SignUpgrade) - registerItem(new item.UpgradeSolarGenerator(defaultProps), Constants.ItemName.SolarGeneratorUpgrade) + registerItem(new item.UpgradeInventoryController(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.InventoryControllerUpgrade) + registerItem(new item.UpgradeNavigation(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.NavigationUpgrade) + registerItem(new item.UpgradePiston(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.PistonUpgrade) + registerItem(new item.UpgradeSign(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.SignUpgrade) + registerItem(new item.UpgradeSolarGenerator(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.SolarGeneratorUpgrade) registerItem(new item.UpgradeTank(defaultProps), Constants.ItemName.TankUpgrade) - registerItem(new item.UpgradeTankController(defaultProps), Constants.ItemName.TankControllerUpgrade) - registerItem(new item.UpgradeTractorBeam(defaultProps), Constants.ItemName.TractorBeamUpgrade) + registerItem(new item.UpgradeTankController(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.TankControllerUpgrade) + registerItem(new item.UpgradeTractorBeam(defaultProps.rarity(Rarity.RARE)), Constants.ItemName.TractorBeamUpgrade) registerItem(new item.UpgradeLeash(defaultProps), Constants.ItemName.LeashUpgrade) // 1.5.8 registerItem(new item.UpgradeHover(defaultProps, Tier.One), Constants.ItemName.HoverUpgradeTier1) - registerItem(new item.UpgradeHover(defaultProps, Tier.Two), Constants.ItemName.HoverUpgradeTier2) + registerItem(new item.UpgradeHover(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.HoverUpgradeTier2) // 1.6 - registerItem(new item.UpgradeTrading(defaultProps), Constants.ItemName.TradingUpgrade) - registerItem(new item.UpgradeMF(defaultProps), Constants.ItemName.MFU) + registerItem(new item.UpgradeTrading(defaultProps.rarity(Rarity.UNCOMMON)), Constants.ItemName.TradingUpgrade) + registerItem(new item.UpgradeMF(defaultProps.rarity(Rarity.RARE)), Constants.ItemName.MFU) // 1.7.2 registerItem(new item.WirelessNetworkCard(defaultProps, Tier.One), Constants.ItemName.WirelessNetworkCardTier1) - registerItem(new item.ComponentBus(defaultProps, Tier.Four), Constants.ItemName.ComponentBusCreative) + registerItem(new item.ComponentBus(defaultProps.rarity(Rarity.EPIC), Tier.Four), Constants.ItemName.ComponentBusCreative) // 1.8 registerItem(new item.UpgradeStickyPiston(defaultProps), Constants.ItemName.StickyPistonUpgrade) @@ -507,8 +507,8 @@ object Items extends ItemAPI { registerItem(new item.EEPROM(defaultProps), Constants.ItemName.EEPROM) registerItem(new item.FloppyDisk(defaultProps), Constants.ItemName.Floppy) registerItem(new item.HardDiskDrive(defaultProps, Tier.One), Constants.ItemName.HDDTier1) - registerItem(new item.HardDiskDrive(defaultProps, Tier.Two), Constants.ItemName.HDDTier2) - registerItem(new item.HardDiskDrive(defaultProps, Tier.Three), Constants.ItemName.HDDTier3) + registerItem(new item.HardDiskDrive(defaultProps.rarity(Rarity.UNCOMMON), Tier.Two), Constants.ItemName.HDDTier2) + registerItem(new item.HardDiskDrive(defaultProps.rarity(Rarity.RARE), Tier.Three), Constants.ItemName.HDDTier3) val luaBios = { val code = new Array[Byte](4 * 1024) diff --git a/src/main/scala/li/cil/oc/common/item/APU.scala b/src/main/scala/li/cil/oc/common/item/APU.scala index 087e5f476a..9672b8c9ad 100644 --- a/src/main/scala/li/cil/oc/common/item/APU.scala +++ b/src/main/scala/li/cil/oc/common/item/APU.scala @@ -1,8 +1,6 @@ package li.cil.oc.common.item import li.cil.oc.common.Tier -import li.cil.oc.util.Rarity -import net.minecraft.item // Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -14,11 +12,6 @@ class APU(props: Properties, val tier: Int) extends Item(props) with IForgeItem @Deprecated override def getDescriptionId = super.getDescriptionId + tier - @Deprecated - override def getRarity(stack: ItemStack): item.Rarity = - if (tier == Tier.Three) Rarity.byTier(Tier.Four) - else super.getRarity(stack) - override def cpuTier = math.min(Tier.Three, tier + 1) override def gpuTier = tier diff --git a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala index 5dad3cf6a4..2c33d2adc3 100644 --- a/src/main/scala/li/cil/oc/common/item/ComponentBus.scala +++ b/src/main/scala/li/cil/oc/common/item/ComponentBus.scala @@ -1,9 +1,6 @@ package li.cil.oc.common.item import li.cil.oc.Settings -import li.cil.oc.common.Tier -import li.cil.oc.util.Rarity -import net.minecraft.item // Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -13,13 +10,6 @@ class ComponentBus(props: Properties, val tier: Int) extends Item(props) with IF @Deprecated override def getDescriptionId = super.getDescriptionId + tier - // Because the driver considers the creative bus to be tier 3, the superclass - // will believe it has T3 rarity. We override that here. - @Deprecated - override def getRarity(stack: ItemStack): item.Rarity = - if (tier == Tier.Four) Rarity.byTier(Tier.Four) - else super.getRarity(stack) - override protected def tooltipName = Option(unlocalizedName) override protected def tooltipData = Seq(Settings.get.cpuComponentSupport(tier)) diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 16a4377ae8..504726b7c2 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -49,7 +49,6 @@ class Drone(props: Properties) extends Item(props) with IForgeItem with traits.S } } - @Deprecated override def getRarity(stack: ItemStack) = { val data = new DroneData(stack) Rarity.byTier(data.tier) diff --git a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala index 79047aa3aa..7d61f9defa 100644 --- a/src/main/scala/li/cil/oc/common/item/HoverBoots.scala +++ b/src/main/scala/li/cil/oc/common/item/HoverBoots.scala @@ -30,9 +30,6 @@ import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem class HoverBoots(props: Properties) extends ArmorItem(ArmorMaterial.DIAMOND, EquipmentSlotType.FEET, props) with IForgeItem with traits.SimpleItem with traits.Chargeable { - @Deprecated - override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON - override def maxCharge(stack: ItemStack): Double = Settings.get.bufferHoverBoots override def getCharge(stack: ItemStack): Double = diff --git a/src/main/scala/li/cil/oc/common/item/Microchip.scala b/src/main/scala/li/cil/oc/common/item/Microchip.scala index 86551bc551..cee66f9f10 100644 --- a/src/main/scala/li/cil/oc/common/item/Microchip.scala +++ b/src/main/scala/li/cil/oc/common/item/Microchip.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import li.cil.oc.util.Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -11,7 +10,4 @@ class Microchip(props: Properties, val tier: Int) extends Item(props) with IForg override def getDescriptionId = super.getDescriptionId + tier override protected def tooltipName = Option(unlocalizedName) - - @Deprecated - override def getRarity(stack: ItemStack) = Rarity.byTier(tier) } diff --git a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala index a755ca96f0..d4e132a4a7 100644 --- a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala @@ -25,9 +25,6 @@ import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.extensions.IForgeItem class Nanomachines(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem { - @Deprecated - override def getRarity(stack: ItemStack): Rarity = Rarity.UNCOMMON - @OnlyIn(Dist.CLIENT) override def appendHoverText(stack: ItemStack, world: World, tooltip: util.List[ITextComponent], flag: ITooltipFlag) { super.appendHoverText(stack, world, tooltip, flag) diff --git a/src/main/scala/li/cil/oc/common/item/Server.scala b/src/main/scala/li/cil/oc/common/item/Server.scala index 31a15cd8ac..64258a9fc3 100644 --- a/src/main/scala/li/cil/oc/common/item/Server.scala +++ b/src/main/scala/li/cil/oc/common/item/Server.scala @@ -6,11 +6,9 @@ import li.cil.oc.OpenComputers import li.cil.oc.client.KeyBindings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.ServerInventory -import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity -import net.minecraft.item // Rarity import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -31,9 +29,6 @@ class Server(props: Properties, val tier: Int) extends Item(props) with IForgeIt override protected def tooltipName = Option(unlocalizedName) - @Deprecated - override def getRarity(stack: ItemStack): item.Rarity = Rarity.byTier(tier) - private object HelperInventory extends ServerInventory { var container = ItemStack.EMPTY diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index e3d6b28f7e..7e993009cd 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -98,7 +98,6 @@ class Tablet(props: Properties) extends Item(props) with IForgeItem with traits. } } - @Deprecated override def getRarity(stack: ItemStack): item.Rarity = { val data = new TabletData(stack) Rarity.byTier(data.tier) diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala index 85e57b3cd6..20d6e404c0 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeDatabase.scala @@ -4,7 +4,6 @@ import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.inventory.DatabaseInventory -import li.cil.oc.util.Rarity import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.Item @@ -24,9 +23,6 @@ class UpgradeDatabase(props: Properties, val tier: Int) extends Item(props) with override protected def tooltipData = Seq(Settings.get.databaseEntriesPerTier(tier)) - @Deprecated - override def getRarity(stack: ItemStack) = Rarity.byTier(tier) - override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching) { if (!world.isClientSide) player match { From 4733d619b2da7f068691c61c508d578532764967 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 5 Oct 2022 23:42:24 +0200 Subject: [PATCH 107/159] Use scissor test to clip manual rendering Fixes code segments rendering out of bounds --- .../client/renderer/markdown/Document.scala | 62 +++++-------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala index 90965064b1..9ecea144b0 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/Document.scala @@ -74,50 +74,21 @@ object Document { RenderState.pushAttrib() - // On some systems/drivers/graphics cards the next calls won't update the - // depth buffer correctly if alpha test is enabled. Guess how we found out? - // By noticing that on those systems it only worked while chat messages - // were visible. Yeah. I know. - RenderSystem.disableAlphaTest() - - // Clear depth mask, then create masks in foreground above and below scroll area. RenderSystem.color4f(1, 1, 1, 1) - RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, false) - RenderSystem.enableDepthTest() - RenderSystem.depthFunc(GL11.GL_LEQUAL) - RenderSystem.depthMask(true) - RenderSystem.colorMask(false, false, false, false) - - stack.pushPose() - stack.translate(0, 0, 500) - GL11.glBegin(GL11.GL_QUADS) - val vec = new Vector4f(0, y, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(window.getGuiScaledWidth, y, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(window.getGuiScaledWidth, 0, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(0, 0, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(0, window.getGuiScaledHeight, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(window.getGuiScaledWidth, window.getGuiScaledHeight, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(window.getGuiScaledWidth, y + maxHeight, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - vec.set(0, y + maxHeight, 0, 1) - vec.transform(stack.last.pose) - GL11.glVertex3f(vec.x, vec.y, vec.z) - GL11.glEnd() - stack.popPose() - RenderSystem.colorMask(true, true, true, true) + // Clip using the scissor test to not interfere with RenderType-maintained depth testing. + GL11.glEnable(GL11.GL_SCISSOR_TEST) + val (x0, y0, x1, y1) = { + val scale = window.getGuiScale + val bottomLeft = new Vector4f(x, y + maxHeight, 0, 1) + bottomLeft.transform(stack.last.pose) + val topRight = new Vector4f(x + maxWidth, y, 0, 1) + topRight.transform(stack.last.pose) + ((bottomLeft.x * scale).floor.asInstanceOf[Int], + (window.getHeight - bottomLeft.y * scale).floor.asInstanceOf[Int], + (topRight.x * scale).ceil.asInstanceOf[Int], + (window.getHeight - topRight.y * scale).ceil.asInstanceOf[Int]) + } + GL11.glScissor(x0, y0, x1 - x0, y1 - y0); // Actual rendering. var hovered: Option[InteractiveSegment] = None @@ -139,10 +110,7 @@ object Document { if (mouseX < x || mouseX > x + maxWidth || mouseY < y || mouseY > y + maxHeight) hovered = None hovered.foreach(_.notifyHover()) - // Remove the depth mask so tooltips render properly. - RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, false) - RenderState.popAttrib() - RenderSystem.bindTexture(0) + GL11.glDisable(GL11.GL_SCISSOR_TEST) hovered } From 33cbbfb70caa5534859ee6ad468b849b06b9d090 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 6 Oct 2022 00:05:46 +0200 Subject: [PATCH 108/159] Fix mod events not running in a thread-safe way --- src/main/scala/li/cil/oc/OpenComputers.scala | 9 ++- src/main/scala/li/cil/oc/client/Proxy.scala | 82 ++++++++++---------- src/main/scala/li/cil/oc/common/Proxy.scala | 60 +++++++------- 3 files changed, 83 insertions(+), 68 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index e547ab06ba..0f57662f38 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -78,5 +78,12 @@ class OpenComputers { } @SubscribeEvent - def imc(e: InterModProcessEvent): Unit = InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) + def imc(e: InterModProcessEvent): Unit = { + // Technically requires synchronization because IMC.sendTo doesn't check the loading stage. + e.enqueueWork(() => { + InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) + + Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) + }) + } } diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 2e5d197c5e..a132dad559 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -47,47 +47,51 @@ private[oc] class Proxy extends CommonProxy { override def init(e: FMLCommonSetupEvent) { super.init(e) - ModelInitialization.preInit() - CommonPacketHandler.clientHandler = PacketHandler - ColorHandler.init() - - RenderingRegistry.registerEntityRenderingHandler(EntityTypes.DRONE, DroneRenderer) - - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ADAPTER, AdapterRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ASSEMBLER, AssemblerRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CASE, CaseRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CHARGER, ChargerRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISASSEMBLER, DisassemblerRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISK_DRIVE, DiskDriveRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.GEOLYZER, GeolyzerRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.HOLOGRAM, HologramRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.MICROCONTROLLER, MicrocontrollerRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.NET_SPLITTER, NetSplitterRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.POWER_DISTRIBUTOR, PowerDistributorRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.PRINTER, PrinterRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RAID, RaidRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RACK, RackRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RELAY, RelayRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ROBOT, RobotRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.SCREEN, ScreenRenderer) - ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.TRANSPOSER, TransposerRenderer) - - ClientRegistry.registerKeyBinding(KeyBindings.extendedTooltip) - ClientRegistry.registerKeyBinding(KeyBindings.analyzeCopyAddr) - ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) - - MinecraftForge.EVENT_BUS.register(HighlightRenderer) - MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Client) - MinecraftForge.EVENT_BUS.register(PetRenderer) - MinecraftForge.EVENT_BUS.register(RackMountableRenderHandler) - MinecraftForge.EVENT_BUS.register(Sound) - MinecraftForge.EVENT_BUS.register(TextBuffer) - MinecraftForge.EVENT_BUS.register(MFUTargetRenderer) - MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) - MinecraftForge.EVENT_BUS.register(Audio) - MinecraftForge.EVENT_BUS.register(HologramRenderer) + e.enqueueWork(() => { + ModelInitialization.preInit() + + ColorHandler.init() + + RenderingRegistry.registerEntityRenderingHandler(EntityTypes.DRONE, DroneRenderer) + + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ADAPTER, AdapterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ASSEMBLER, AssemblerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CASE, CaseRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.CHARGER, ChargerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISASSEMBLER, DisassemblerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.DISK_DRIVE, DiskDriveRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.GEOLYZER, GeolyzerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.HOLOGRAM, HologramRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.MICROCONTROLLER, MicrocontrollerRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.NET_SPLITTER, NetSplitterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.POWER_DISTRIBUTOR, PowerDistributorRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.PRINTER, PrinterRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RAID, RaidRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RACK, RackRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.RELAY, RelayRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.ROBOT, RobotRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.SCREEN, ScreenRenderer) + ClientRegistry.bindTileEntityRenderer(tileentity.TileEntityTypes.TRANSPOSER, TransposerRenderer) + + ClientRegistry.registerKeyBinding(KeyBindings.extendedTooltip) + ClientRegistry.registerKeyBinding(KeyBindings.analyzeCopyAddr) + ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) + + MinecraftForge.EVENT_BUS.register(HighlightRenderer) + MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Client) + MinecraftForge.EVENT_BUS.register(PetRenderer) + MinecraftForge.EVENT_BUS.register(RackMountableRenderHandler) + MinecraftForge.EVENT_BUS.register(Sound) + MinecraftForge.EVENT_BUS.register(TextBuffer) + MinecraftForge.EVENT_BUS.register(MFUTargetRenderer) + MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) + MinecraftForge.EVENT_BUS.register(Audio) + MinecraftForge.EVENT_BUS.register(HologramRenderer) + + Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) + }) runOnRenderThread(() => MinecraftForge.EVENT_BUS.register(TextBufferRenderCache)) } diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 4363c66376..45f3c6627e 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -86,38 +86,42 @@ class Proxy { @SubscribeEvent def init(e: FMLCommonSetupEvent) { - OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), new Supplier[String] { - override def get = "" - }, new Predicate[String] { - override def test(ver: String) = "".equals(ver) - }, new Predicate[String] { - override def test(ver: String) = "".equals(ver) - }) - OpenComputers.channel.registerMessage(0, classOf[Array[Byte]], new BiConsumer[Array[Byte], PacketBuffer] { - override def accept(msg: Array[Byte], buff: PacketBuffer) = buff.writeByteArray(msg) - }, new Function[PacketBuffer, Array[Byte]] { - override def apply(buff: PacketBuffer) = buff.readByteArray() - }, new BiConsumer[Array[Byte], Supplier[NetworkEvent.Context]] { - override def accept(msg: Array[Byte], ctx: Supplier[NetworkEvent.Context]) = { - val context = ctx.get - context.enqueueWork(new Runnable { - override def run = PacketHandler.handlePacket(context.getDirection, msg, context.getSender) - }) - context.setPacketHandled(true) - } - }) - PacketHandler.serverHandler = server.PacketHandler + e.enqueueWork(() => { + OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), new Supplier[String] { + override def get = "" + }, new Predicate[String] { + override def test(ver: String) = "".equals(ver) + }, new Predicate[String] { + override def test(ver: String) = "".equals(ver) + }) + OpenComputers.channel.registerMessage(0, classOf[Array[Byte]], new BiConsumer[Array[Byte], PacketBuffer] { + override def accept(msg: Array[Byte], buff: PacketBuffer) = buff.writeByteArray(msg) + }, new Function[PacketBuffer, Array[Byte]] { + override def apply(buff: PacketBuffer) = buff.readByteArray() + }, new BiConsumer[Array[Byte], Supplier[NetworkEvent.Context]] { + override def accept(msg: Array[Byte], ctx: Supplier[NetworkEvent.Context]) = { + val context = ctx.get + context.enqueueWork(new Runnable { + override def run = PacketHandler.handlePacket(context.getDirection, msg, context.getSender) + }) + context.setPacketHandled(true) + } + }) + PacketHandler.serverHandler = server.PacketHandler + + Loot.init() + Achievement.init() - Loot.init() - Achievement.init() + OpenComputers.log.debug("Initializing mod integration.") + Mods.init() - OpenComputers.log.debug("Initializing mod integration.") - Mods.init() + OpenComputers.log.info("Initializing capabilities.") + Capabilities.init() - OpenComputers.log.info("Initializing capabilities.") - Capabilities.init() + api.API.isPowerEnabled = !Settings.get.ignorePower - api.API.isPowerEnabled = !Settings.get.ignorePower + Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) + }) } @SubscribeEvent From 50d15b98d180b172e3d2485e1f7d7ac5e9903520 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 9 Oct 2022 22:35:04 +0200 Subject: [PATCH 109/159] Convert recipes into a datapack --- .../opencomputers/recipes/default.recipes | 700 ------------------ .../opencomputers/recipes/gregtech.recipes | 353 --------- .../opencomputers/recipes/hardmode.recipes | 411 ---------- .../opencomputers/recipes/peaceful.recipes | 79 -- .../assets/opencomputers/recipes/user.recipes | 20 - .../tags/blocks/end_stones.json} | 0 .../data/forge/tags/items/end_stones.json | 6 + .../data/opencomputers/recipes/acid.json | 23 + .../data/opencomputers/recipes/adapter.json | 25 + .../data/opencomputers/recipes/alu.json | 25 + .../data/opencomputers/recipes/analyzer.json | 25 + .../opencomputers/recipes/angelupgrade.json | 25 + .../data/opencomputers/recipes/apu1.json | 28 + .../data/opencomputers/recipes/apu2.json | 28 + .../data/opencomputers/recipes/arrowkeys.json | 15 + .../data/opencomputers/recipes/assembler.json | 28 + .../recipes/batteryupgrade1.json | 25 + .../recipes/batteryupgrade2.json | 25 + .../recipes/batteryupgrade3.json | 22 + .../opencomputers/recipes/buttongroup.json | 15 + .../data/opencomputers/recipes/cable.json | 20 + .../data/opencomputers/recipes/capacitor.json | 28 + .../data/opencomputers/recipes/card.json | 22 + .../opencomputers/recipes/cardcontainer1.json | 28 + .../opencomputers/recipes/cardcontainer2.json | 28 + .../opencomputers/recipes/cardcontainer3.json | 28 + .../recipes/carpetedcapacitor.json | 14 + .../data/opencomputers/recipes/case1.json | 28 + .../data/opencomputers/recipes/case2.json | 28 + .../data/opencomputers/recipes/case3.json | 28 + .../data/opencomputers/recipes/chamelium.json | 26 + .../opencomputers/recipes/chameliumblock.json | 19 + .../opencomputers/recipes/chameliumsplit.json | 12 + .../data/opencomputers/recipes/charger.json | 28 + .../data/opencomputers/recipes/chip1.json | 23 + .../data/opencomputers/recipes/chip2.json | 23 + .../data/opencomputers/recipes/chip3.json | 23 + .../opencomputers/recipes/chipdiamond.json | 15 + .../recipes/chunkloaderupgrade.json | 31 + .../opencomputers/recipes/componentbus1.json | 28 + .../opencomputers/recipes/componentbus2.json | 28 + .../opencomputers/recipes/componentbus3.json | 28 + .../data/opencomputers/recipes/cpu1.json | 28 + .../data/opencomputers/recipes/cpu2.json | 28 + .../data/opencomputers/recipes/cpu3.json | 28 + .../recipes/craftingupgrade.json | 25 + .../data/opencomputers/recipes/cu.json | 25 + .../opencomputers/recipes/cuttingwire.json | 17 + .../recipes/databaseupgrade1.json | 28 + .../recipes/databaseupgrade2.json | 28 + .../recipes/databaseupgrade3.json | 28 + .../data/opencomputers/recipes/datacard1.json | 24 + .../data/opencomputers/recipes/datacard2.json | 24 + .../data/opencomputers/recipes/datacard3.json | 24 + .../opencomputers/recipes/disassembler.json | 34 + .../data/opencomputers/recipes/disk.json | 16 + .../data/opencomputers/recipes/diskdrive.json | 28 + .../recipes/diskdrivemountable.json | 28 + .../opencomputers/recipes/dronecase1.json | 28 + .../opencomputers/recipes/dronecase2.json | 28 + .../data/opencomputers/recipes/eeprom.json | 28 + .../data/opencomputers/recipes/endstone.json | 19 + .../recipes/experienceupgrade.json | 25 + .../data/opencomputers/recipes/floppy.json | 28 + .../recipes/generatorupgrade.json | 25 + .../data/opencomputers/recipes/geolyzer.json | 28 + .../opencomputers/recipes/graphicscard1.json | 24 + .../opencomputers/recipes/graphicscard2.json | 24 + .../opencomputers/recipes/graphicscard3.json | 24 + .../data/opencomputers/recipes/hdd1.json | 28 + .../data/opencomputers/recipes/hdd2.json | 28 + .../data/opencomputers/recipes/hdd3.json | 28 + .../data/opencomputers/recipes/hologram1.json | 31 + .../data/opencomputers/recipes/hologram2.json | 31 + .../opencomputers/recipes/hoverboots.json | 28 + .../opencomputers/recipes/hoverupgrade1.json | 28 + .../opencomputers/recipes/hoverupgrade2.json | 28 + .../opencomputers/recipes/inkcartridge.json | 23 + .../recipes/inkcartridgeempty.json | 28 + .../opencomputers/recipes/internetcard.json | 27 + .../data/opencomputers/recipes/interweb.json | 19 + .../recipes/inventorycontrollerupgrade.json | 31 + .../recipes/inventoryupgrade.json | 31 + .../data/opencomputers/recipes/keyboard.json | 21 + .../data/opencomputers/recipes/lancard.json | 21 + .../opencomputers/recipes/leashupgrade.json | 22 + .../opencomputers/recipes/linkedcard.json | 25 + .../recipes/lootdisks/openos.json | 24 + .../opencomputers/recipes/lootdisks/oppm.json | 24 + .../data/opencomputers/recipes/manual.json | 14 + .../data/opencomputers/recipes/mfu.json | 25 + .../recipes/microcontrollercase1.json | 28 + .../recipes/microcontrollercase2.json | 28 + .../opencomputers/recipes/motionsensor.json | 25 + .../opencomputers/recipes/nanomachines.json | 31 + .../recipes/navigationupgrade.json | 28 + .../opencomputers/recipes/netsplitter.json | 25 + .../data/opencomputers/recipes/numpad.json | 16 + .../opencomputers/recipes/pistonupgrade.json | 28 + .../opencomputers/recipes/powerconverter.json | 28 + .../recipes/powerdistributor.json | 28 + .../recipes/printedcircuitboard.json | 9 + .../data/opencomputers/recipes/printer.json | 28 + .../data/opencomputers/recipes/rack.json | 34 + .../data/opencomputers/recipes/raid.json | 28 + .../data/opencomputers/recipes/ram1.json | 21 + .../data/opencomputers/recipes/ram2.json | 21 + .../data/opencomputers/recipes/ram3.json | 21 + .../data/opencomputers/recipes/ram4.json | 21 + .../data/opencomputers/recipes/ram5.json | 21 + .../data/opencomputers/recipes/ram6.json | 21 + .../recipes/rawcircuitboard.json | 18 + .../data/opencomputers/recipes/redstone.json | 28 + .../opencomputers/recipes/redstonecard1.json | 21 + .../opencomputers/recipes/redstonecard2.json | 24 + .../data/opencomputers/recipes/relay.json | 25 + .../data/opencomputers/recipes/screen1.json | 25 + .../data/opencomputers/recipes/screen2.json | 31 + .../data/opencomputers/recipes/screen3.json | 25 + .../data/opencomputers/recipes/server1.json | 31 + .../data/opencomputers/recipes/server2.json | 31 + .../data/opencomputers/recipes/server3.json | 31 + .../opencomputers/recipes/signupgrade.json | 28 + .../recipes/solargeneratorupgrade.json | 28 + .../recipes/stickypistonupgrade.json | 14 + .../opencomputers/recipes/tabletcase1.json | 31 + .../opencomputers/recipes/tabletcase2.json | 34 + .../recipes/tankcontrollerupgrade.json | 31 + .../opencomputers/recipes/tankupgrade.json | 31 + .../data/opencomputers/recipes/terminal.json | 31 + .../opencomputers/recipes/terminalserver.json | 25 + .../opencomputers/recipes/texturepicker.json | 40 + .../recipes/tractorbeamupgrade.json | 28 + .../opencomputers/recipes/tradingupgrade.json | 34 + .../opencomputers/recipes/transistor.json | 26 + .../opencomputers/recipes/transposer.json | 29 + .../recipes/upgradecontainer1.json | 28 + .../recipes/upgradecontainer2.json | 28 + .../recipes/upgradecontainer3.json | 28 + .../data/opencomputers/recipes/waypoint.json | 28 + .../data/opencomputers/recipes/wlancard1.json | 21 + .../data/opencomputers/recipes/wlancard2.json | 21 + .../data/opencomputers/recipes/wrench.json | 19 + .../opencomputers/tags/items/adapter.json | 6 + .../opencomputers/tags/items/assembler.json | 6 + .../data/opencomputers/tags/items/cable.json | 6 + .../opencomputers/tags/items/capacitor.json | 6 + .../tags/items/carpetedcapacitor.json | 6 + .../data/opencomputers/tags/items/case1.json | 6 + .../data/opencomputers/tags/items/case2.json | 6 + .../data/opencomputers/tags/items/case3.json | 6 + .../tags/items/chameliumblock.json | 6 + .../opencomputers/tags/items/charger.json | 6 + .../tags/items/disassembler.json | 6 + .../opencomputers/tags/items/diskdrive.json | 6 + .../opencomputers/tags/items/geolyzer.json | 6 + .../opencomputers/tags/items/hologram1.json | 6 + .../opencomputers/tags/items/hologram2.json | 6 + .../opencomputers/tags/items/keyboard.json | 6 + .../tags/items/motionsensor.json | 6 + .../opencomputers/tags/items/netsplitter.json | 6 + .../tags/items/powerconverter.json | 6 + .../tags/items/powerdistributor.json | 6 + .../opencomputers/tags/items/printer.json | 6 + .../data/opencomputers/tags/items/rack.json | 6 + .../data/opencomputers/tags/items/raid.json | 6 + .../opencomputers/tags/items/redstone.json | 6 + .../data/opencomputers/tags/items/relay.json | 6 + .../opencomputers/tags/items/screen1.json | 6 + .../opencomputers/tags/items/screen2.json | 6 + .../opencomputers/tags/items/screen3.json | 6 + .../opencomputers/tags/items/transposer.json | 6 + .../opencomputers/tags/items/waypoint.json | 6 + 173 files changed, 3653 insertions(+), 1563 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/recipes/default.recipes delete mode 100644 src/main/resources/assets/opencomputers/recipes/gregtech.recipes delete mode 100644 src/main/resources/assets/opencomputers/recipes/hardmode.recipes delete mode 100644 src/main/resources/assets/opencomputers/recipes/peaceful.recipes delete mode 100644 src/main/resources/assets/opencomputers/recipes/user.recipes rename src/main/resources/data/{opencomputers/tags/blocks/stoneendstone.json => forge/tags/blocks/end_stones.json} (100%) create mode 100644 src/main/resources/data/forge/tags/items/end_stones.json create mode 100644 src/main/resources/data/opencomputers/recipes/acid.json create mode 100644 src/main/resources/data/opencomputers/recipes/adapter.json create mode 100644 src/main/resources/data/opencomputers/recipes/alu.json create mode 100644 src/main/resources/data/opencomputers/recipes/analyzer.json create mode 100644 src/main/resources/data/opencomputers/recipes/angelupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/apu1.json create mode 100644 src/main/resources/data/opencomputers/recipes/apu2.json create mode 100644 src/main/resources/data/opencomputers/recipes/arrowkeys.json create mode 100644 src/main/resources/data/opencomputers/recipes/assembler.json create mode 100644 src/main/resources/data/opencomputers/recipes/batteryupgrade1.json create mode 100644 src/main/resources/data/opencomputers/recipes/batteryupgrade2.json create mode 100644 src/main/resources/data/opencomputers/recipes/batteryupgrade3.json create mode 100644 src/main/resources/data/opencomputers/recipes/buttongroup.json create mode 100644 src/main/resources/data/opencomputers/recipes/cable.json create mode 100644 src/main/resources/data/opencomputers/recipes/capacitor.json create mode 100644 src/main/resources/data/opencomputers/recipes/card.json create mode 100644 src/main/resources/data/opencomputers/recipes/cardcontainer1.json create mode 100644 src/main/resources/data/opencomputers/recipes/cardcontainer2.json create mode 100644 src/main/resources/data/opencomputers/recipes/cardcontainer3.json create mode 100644 src/main/resources/data/opencomputers/recipes/carpetedcapacitor.json create mode 100644 src/main/resources/data/opencomputers/recipes/case1.json create mode 100644 src/main/resources/data/opencomputers/recipes/case2.json create mode 100644 src/main/resources/data/opencomputers/recipes/case3.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium.json create mode 100644 src/main/resources/data/opencomputers/recipes/chameliumblock.json create mode 100644 src/main/resources/data/opencomputers/recipes/chameliumsplit.json create mode 100644 src/main/resources/data/opencomputers/recipes/charger.json create mode 100644 src/main/resources/data/opencomputers/recipes/chip1.json create mode 100644 src/main/resources/data/opencomputers/recipes/chip2.json create mode 100644 src/main/resources/data/opencomputers/recipes/chip3.json create mode 100644 src/main/resources/data/opencomputers/recipes/chipdiamond.json create mode 100644 src/main/resources/data/opencomputers/recipes/chunkloaderupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/componentbus1.json create mode 100644 src/main/resources/data/opencomputers/recipes/componentbus2.json create mode 100644 src/main/resources/data/opencomputers/recipes/componentbus3.json create mode 100644 src/main/resources/data/opencomputers/recipes/cpu1.json create mode 100644 src/main/resources/data/opencomputers/recipes/cpu2.json create mode 100644 src/main/resources/data/opencomputers/recipes/cpu3.json create mode 100644 src/main/resources/data/opencomputers/recipes/craftingupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/cu.json create mode 100644 src/main/resources/data/opencomputers/recipes/cuttingwire.json create mode 100644 src/main/resources/data/opencomputers/recipes/databaseupgrade1.json create mode 100644 src/main/resources/data/opencomputers/recipes/databaseupgrade2.json create mode 100644 src/main/resources/data/opencomputers/recipes/databaseupgrade3.json create mode 100644 src/main/resources/data/opencomputers/recipes/datacard1.json create mode 100644 src/main/resources/data/opencomputers/recipes/datacard2.json create mode 100644 src/main/resources/data/opencomputers/recipes/datacard3.json create mode 100644 src/main/resources/data/opencomputers/recipes/disassembler.json create mode 100644 src/main/resources/data/opencomputers/recipes/disk.json create mode 100644 src/main/resources/data/opencomputers/recipes/diskdrive.json create mode 100644 src/main/resources/data/opencomputers/recipes/diskdrivemountable.json create mode 100644 src/main/resources/data/opencomputers/recipes/dronecase1.json create mode 100644 src/main/resources/data/opencomputers/recipes/dronecase2.json create mode 100644 src/main/resources/data/opencomputers/recipes/eeprom.json create mode 100644 src/main/resources/data/opencomputers/recipes/endstone.json create mode 100644 src/main/resources/data/opencomputers/recipes/experienceupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy.json create mode 100644 src/main/resources/data/opencomputers/recipes/generatorupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/geolyzer.json create mode 100644 src/main/resources/data/opencomputers/recipes/graphicscard1.json create mode 100644 src/main/resources/data/opencomputers/recipes/graphicscard2.json create mode 100644 src/main/resources/data/opencomputers/recipes/graphicscard3.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd1.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd2.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd3.json create mode 100644 src/main/resources/data/opencomputers/recipes/hologram1.json create mode 100644 src/main/resources/data/opencomputers/recipes/hologram2.json create mode 100644 src/main/resources/data/opencomputers/recipes/hoverboots.json create mode 100644 src/main/resources/data/opencomputers/recipes/hoverupgrade1.json create mode 100644 src/main/resources/data/opencomputers/recipes/hoverupgrade2.json create mode 100644 src/main/resources/data/opencomputers/recipes/inkcartridge.json create mode 100644 src/main/resources/data/opencomputers/recipes/inkcartridgeempty.json create mode 100644 src/main/resources/data/opencomputers/recipes/internetcard.json create mode 100644 src/main/resources/data/opencomputers/recipes/interweb.json create mode 100644 src/main/resources/data/opencomputers/recipes/inventorycontrollerupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/inventoryupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/keyboard.json create mode 100644 src/main/resources/data/opencomputers/recipes/lancard.json create mode 100644 src/main/resources/data/opencomputers/recipes/leashupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/linkedcard.json create mode 100644 src/main/resources/data/opencomputers/recipes/lootdisks/openos.json create mode 100644 src/main/resources/data/opencomputers/recipes/lootdisks/oppm.json create mode 100644 src/main/resources/data/opencomputers/recipes/manual.json create mode 100644 src/main/resources/data/opencomputers/recipes/mfu.json create mode 100644 src/main/resources/data/opencomputers/recipes/microcontrollercase1.json create mode 100644 src/main/resources/data/opencomputers/recipes/microcontrollercase2.json create mode 100644 src/main/resources/data/opencomputers/recipes/motionsensor.json create mode 100644 src/main/resources/data/opencomputers/recipes/nanomachines.json create mode 100644 src/main/resources/data/opencomputers/recipes/navigationupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/netsplitter.json create mode 100644 src/main/resources/data/opencomputers/recipes/numpad.json create mode 100644 src/main/resources/data/opencomputers/recipes/pistonupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/powerconverter.json create mode 100644 src/main/resources/data/opencomputers/recipes/powerdistributor.json create mode 100644 src/main/resources/data/opencomputers/recipes/printedcircuitboard.json create mode 100644 src/main/resources/data/opencomputers/recipes/printer.json create mode 100644 src/main/resources/data/opencomputers/recipes/rack.json create mode 100644 src/main/resources/data/opencomputers/recipes/raid.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram1.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram2.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram3.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram4.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram5.json create mode 100644 src/main/resources/data/opencomputers/recipes/ram6.json create mode 100644 src/main/resources/data/opencomputers/recipes/rawcircuitboard.json create mode 100644 src/main/resources/data/opencomputers/recipes/redstone.json create mode 100644 src/main/resources/data/opencomputers/recipes/redstonecard1.json create mode 100644 src/main/resources/data/opencomputers/recipes/redstonecard2.json create mode 100644 src/main/resources/data/opencomputers/recipes/relay.json create mode 100644 src/main/resources/data/opencomputers/recipes/screen1.json create mode 100644 src/main/resources/data/opencomputers/recipes/screen2.json create mode 100644 src/main/resources/data/opencomputers/recipes/screen3.json create mode 100644 src/main/resources/data/opencomputers/recipes/server1.json create mode 100644 src/main/resources/data/opencomputers/recipes/server2.json create mode 100644 src/main/resources/data/opencomputers/recipes/server3.json create mode 100644 src/main/resources/data/opencomputers/recipes/signupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/solargeneratorupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/stickypistonupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/tabletcase1.json create mode 100644 src/main/resources/data/opencomputers/recipes/tabletcase2.json create mode 100644 src/main/resources/data/opencomputers/recipes/tankcontrollerupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/tankupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/terminal.json create mode 100644 src/main/resources/data/opencomputers/recipes/terminalserver.json create mode 100644 src/main/resources/data/opencomputers/recipes/texturepicker.json create mode 100644 src/main/resources/data/opencomputers/recipes/tractorbeamupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/tradingupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/transistor.json create mode 100644 src/main/resources/data/opencomputers/recipes/transposer.json create mode 100644 src/main/resources/data/opencomputers/recipes/upgradecontainer1.json create mode 100644 src/main/resources/data/opencomputers/recipes/upgradecontainer2.json create mode 100644 src/main/resources/data/opencomputers/recipes/upgradecontainer3.json create mode 100644 src/main/resources/data/opencomputers/recipes/waypoint.json create mode 100644 src/main/resources/data/opencomputers/recipes/wlancard1.json create mode 100644 src/main/resources/data/opencomputers/recipes/wlancard2.json create mode 100644 src/main/resources/data/opencomputers/recipes/wrench.json create mode 100644 src/main/resources/data/opencomputers/tags/items/adapter.json create mode 100644 src/main/resources/data/opencomputers/tags/items/assembler.json create mode 100644 src/main/resources/data/opencomputers/tags/items/cable.json create mode 100644 src/main/resources/data/opencomputers/tags/items/capacitor.json create mode 100644 src/main/resources/data/opencomputers/tags/items/carpetedcapacitor.json create mode 100644 src/main/resources/data/opencomputers/tags/items/case1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/case2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/case3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/chameliumblock.json create mode 100644 src/main/resources/data/opencomputers/tags/items/charger.json create mode 100644 src/main/resources/data/opencomputers/tags/items/disassembler.json create mode 100644 src/main/resources/data/opencomputers/tags/items/diskdrive.json create mode 100644 src/main/resources/data/opencomputers/tags/items/geolyzer.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hologram1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/hologram2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/keyboard.json create mode 100644 src/main/resources/data/opencomputers/tags/items/motionsensor.json create mode 100644 src/main/resources/data/opencomputers/tags/items/netsplitter.json create mode 100644 src/main/resources/data/opencomputers/tags/items/powerconverter.json create mode 100644 src/main/resources/data/opencomputers/tags/items/powerdistributor.json create mode 100644 src/main/resources/data/opencomputers/tags/items/printer.json create mode 100644 src/main/resources/data/opencomputers/tags/items/rack.json create mode 100644 src/main/resources/data/opencomputers/tags/items/raid.json create mode 100644 src/main/resources/data/opencomputers/tags/items/redstone.json create mode 100644 src/main/resources/data/opencomputers/tags/items/relay.json create mode 100644 src/main/resources/data/opencomputers/tags/items/screen1.json create mode 100644 src/main/resources/data/opencomputers/tags/items/screen2.json create mode 100644 src/main/resources/data/opencomputers/tags/items/screen3.json create mode 100644 src/main/resources/data/opencomputers/tags/items/transposer.json create mode 100644 src/main/resources/data/opencomputers/tags/items/waypoint.json diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes deleted file mode 100644 index d8da5c9fb8..0000000000 --- a/src/main/resources/assets/opencomputers/recipes/default.recipes +++ /dev/null @@ -1,700 +0,0 @@ -# Do not change this file, it is rewritten each time you start the game. -# Instead, use the user.recipes file to edit recipes by redefining them there. - -analyzer { - input: [[torchRedstoneActive, "", ""] - ["oc:materialTransistor", nuggetGold, ""] - ["oc:materialCircuitBoardPrinted", nuggetGold, ""]] -} -hoverboots { - input: [[iron_nugget, "oc:hoverUpgrade2", iron_nugget] - [leather, "oc:droneCase1", leather] - [iron_nugget, "oc:capacitor", iron_nugget]] -} -manual { - type: shapeless - input: [book, "oc:circuitChip1"] -} -nanomachines { - input: [["oc:chamelium", "oc:wlanCard2", "oc:chamelium"] - ["oc:cpu2", "oc:materialAcid", "oc:ram1"] - ["oc:chamelium", "oc:capacitor", "oc:chamelium"]] -} -texturepicker { - input: [[dyeBlack, dyeRed, dyeGreen] - [dyeBlue, "oc:analyzer", dyePurple] - [dyeYellow, dyeMagenta, dyeWhite]] -} -wrench { - input: [[ingotIron, "", ingotIron] - ["", "oc:circuitChip2", ""], - ["", ingotIron, ""]] -} -lootdisks: [ - { - name: "opencomputers:openos" - type: shapeless - input: ["oc:floppy", "oc:manual"] - }, - { - name: "opencomputers:oppm" - type: shapeless - input: ["oc:floppy", "oc:materialInterweb"] - } -] -luabios { - type: shapeless - input: ["oc:eeprom", "oc:manual"] -} - -dronecase1 { - input: [["oc:stoneEndstone", compass, "oc:stoneEndstone"] - ["oc:circuitChip1", "oc:microcontrollerCase1", "oc:circuitChip1"] - ["oc:stoneEndstone", "oc:componentBus2", "oc:stoneEndstone"]] -} -dronecase2 { - input: [["oc:stoneEndstone", compass, "oc:stoneEndstone"] - ["oc:circuitChip2", "oc:microcontrollerCase2", "oc:circuitChip2"] - ["oc:stoneEndstone", "oc:componentBus3", "oc:stoneEndstone"]] -} -microcontrollercase1 { - input: [[iron_nugget, "oc:circuitChip1", iron_nugget] - [redstone, chest, redstone] - [iron_nugget, "oc:materialCircuitBoardPrinted", iron_nugget]] -} -microcontrollercase2 { - input: [[nuggetGold, "oc:circuitChip3", nuggetGold] - [blockRedstone, chest, blockRedstone] - [nuggetGold, "oc:materialCircuitBoardPrinted", nuggetGold]] -} -terminal { - input: [[iron_nugget, "oc:solarGeneratorUpgrade", iron_nugget] - ["oc:circuitChip3", "oc:screen2", "oc:wlanCard2"] - [iron_nugget, "oc:keyboard", iron_nugget]] -} -tabletcase1 { - input: [[ingotGold, button, ingotGold] - ["oc:componentBus1", "oc:screen2", "oc:circuitChip3"] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -tabletcase2 { - input: [["oc:circuitChip2", button, ingotGold] - ["oc:componentBus3", "oc:screen2", "oc:circuitChip3"] - ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", ingotGold]] -} - -diskdrivemountable { - input: [[obsidian, "oc:circuitChip1", obsidian] - [fenceIron, "oc:diskDrive", fenceIron] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -server1 { - input: [[ingotIron, "oc:ram2", ingotIron] - ["oc:circuitChip1", "oc:componentBus1", "oc:circuitChip1"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -server2 { - input: [[ingotGold, "oc:ram4", ingotGold] - ["oc:circuitChip2", "oc:componentBus2", "oc:circuitChip2"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -server3 { - input: [[gemDiamond, "oc:ram6", gemDiamond] - ["oc:circuitChip3", "oc:componentBus3", "oc:circuitChip3"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -terminalserver { - input: [[obsidian, "oc:wlanCard", obsidian] - ["oc:wlanCard", "oc:circuitChip2", "oc:wlanCard2"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} - -ram1 { - input: [["oc:circuitChip1", iron_nugget, "oc:circuitChip1"] - ["", "oc:materialCircuitBoardPrinted", ""]] -} -ram2 { - input: [["oc:circuitChip1", "oc:circuitChip2", "oc:circuitChip1"] - ["", "oc:materialCircuitBoardPrinted", ""]] -} -ram3 { - input: [["oc:circuitChip2", iron_nugget, "oc:circuitChip2"] - ["", "oc:materialCircuitBoardPrinted", ""]] -} -ram4 { - input: [["oc:circuitChip2", "oc:circuitChip3", "oc:circuitChip2"] - ["", "oc:materialCircuitBoardPrinted", ""]] -} -ram5 { - input: [["oc:circuitChip3", iron_nugget, "oc:circuitChip3"] - ["", "oc:materialCircuitBoardPrinted", ""]] -} -ram6 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:circuitChip3"] - ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"]] -} - -eeprom { - input: [[nuggetGold, "oc:materialTransistor", nuggetGold] - [paper, "oc:circuitChip1", paper] - [nuggetGold, torchRedstoneActive, nuggetGold]] -} -floppy { - input: [[iron_nugget, lever, iron_nugget] - [paper, "oc:materialDisk", paper] - [iron_nugget, paper, iron_nugget]] -} -hdd1 { - input: [["oc:circuitChip1", "oc:materialDisk", ingotIron] - ["oc:materialCircuitBoardPrinted", "oc:materialDisk", craftingPiston] - ["oc:circuitChip1", "oc:materialDisk", ingotIron]] -} -hdd2 { - input: [["oc:circuitChip2", "oc:materialDisk", ingotGold] - ["oc:materialCircuitBoardPrinted", "oc:materialDisk", craftingPiston] - ["oc:circuitChip2", "oc:materialDisk", ingotGold]] -} -hdd3 { - input: [["oc:circuitChip3", "oc:materialDisk", gemDiamond] - ["oc:materialCircuitBoardPrinted", "oc:materialDisk", craftingPiston] - ["oc:circuitChip3", "oc:materialDisk", gemDiamond]] -} - -datacard1 { - input: [[iron_nugget, "oc:materialALU", "oc:circuitChip2"] - ["", "oc:materialCard", ""]] -} -datacard2 { - input: [[nuggetGold, "oc:cpu1", "oc:circuitChip3"] - ["", "oc:materialCard", ""]] -} -datacard3 { - input: [[chipDiamond, "oc:cpu2", "oc:ram5"] - ["", "oc:materialCard", ""]] -} -graphicscard1 { - input: [["oc:circuitChip1", "oc:materialALU", "oc:ram1"] - ["", "oc:materialCard", ""]] -} -graphicscard2 { - input: [["oc:circuitChip2", "oc:materialALU", "oc:ram3"] - ["", "oc:materialCard", ""]] -} -graphicscard3 { - input: [["oc:circuitChip3", "oc:materialALU", "oc:ram5"] - ["", "oc:materialCard", ""]] -} -internetcard { - input: [["oc:materialInterweb", "oc:circuitChip2", torchRedstoneActive] - ["", "oc:materialCard", obsidian]] -} -redstonecard1 { - input: [[torchRedstoneActive, "oc:circuitChip1", ""] - ["", "oc:materialCard", ""]] -} -redstonecard2 { - input: [[blockRedstone, "oc:circuitChip2", materialEnderPearl] - ["", "oc:materialCard", ""]] -} -lancard { - input: [["oc:cable", "oc:circuitChip1", ""] - ["", "oc:materialCard", ""]] -} -wlancard1 { - input: [[torchRedstoneActive, "oc:circuitChip1", torchRedstoneActive] - ["", "oc:materialCard", ""]] -} -wlancard2 { - input: [[materialEnderPearl, "oc:circuitChip2", ""] - ["", "oc:materialCard", ""]] -} -linkedcard { - input: [[eyeOfEnder, "", eyeOfEnder] - ["oc:lanCard", "oc:materialInterweb", "oc:lanCard"] - ["oc:circuitChip3", "", "oc:circuitChip3"]] - output: 2 # Note: all resulting cards are linked to each other. -} - -abstractbuscard { - input: [[{block="StargateTech2:block.busCable"}, {item="StargateTech2:naquadah", subID=3}, ""] - ["", "oc:materialCard", ""]] -} -worldsensorcard { - input: [[{item="galacticraftcore:sensor_lens"}, "oc:circuitChip2", ""] - ["", "oc:materialCard", ""]] -} - -angelupgrade { - input: [[ingotIron, materialEnderPearl, ingotIron] - ["oc:circuitChip1", pistonStickyBase, "oc:circuitChip1"] - [ingotIron, materialEnderPearl, ingotIron]] -} -batteryupgrade1 { - input: [[iron_nugget, nuggetGold, iron_nugget] - [fenceIron, "oc:capacitor", fenceIron] - [iron_nugget, nuggetGold, iron_nugget]] -} -batteryupgrade2 { - input: [[iron_nugget, "oc:capacitor", iron_nugget] - [fenceIron, nuggetGold, fenceIron] - [iron_nugget, "oc:capacitor", iron_nugget]] -} -batteryupgrade3 { - input: [[iron_nugget, "oc:capacitor", iron_nugget] - ["oc:capacitor", chipDiamond, "oc:capacitor"] - [iron_nugget, "oc:capacitor", iron_nugget]] -} -chunkloaderupgrade { - input: [[ingotGold, blockGlass, ingotGold] - ["oc:circuitChip3", eyeOfEnder, "oc:circuitChip3"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -craftingupgrade { - input: [[ingotIron, "", ingotIron] - ["oc:circuitChip1", workbench, "oc:circuitChip1"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -databaseupgrade1 { - input: [[ingotIron, "oc:analyzer", ingotIron] - ["oc:circuitChip1", "oc:hdd1", "oc:circuitChip1"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -databaseupgrade2 { - input: [[ingotIron, "oc:analyzer", ingotIron] - ["oc:circuitChip2", "oc:hdd2", "oc:circuitChip2"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -databaseupgrade3 { - input: [[ingotIron, "oc:analyzer", ingotIron] - ["oc:circuitChip3", "oc:hdd3", "oc:circuitChip3"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -experienceupgrade { - input: [[ingotGold, "", ingotGold] - ["oc:circuitChip2", emerald, "oc:circuitChip2"] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -generatorupgrade { - input: [[ingotIron, "", ingotIron] - ["oc:circuitChip1", craftingPiston, "oc:circuitChip1"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -hoverupgrade1 { - input: [[feather, "oc:circuitChip1", feather] - [iron_nugget, leather, iron_nugget] - [feather, "oc:materialCircuitBoardPrinted", feather]] -} -hoverupgrade2 { - input: [["oc:stoneEndstone", "oc:circuitChip2", "oc:stoneEndstone"] - [nuggetGold, ingotIron, nuggetGold] - ["oc:stoneEndstone", "oc:materialCircuitBoardPrinted", "oc:stoneEndstone"]] -} -inventoryupgrade { - input: [[plankWood, hopper, plankWood] - [dropper, chest, craftingPiston] - [plankWood, "oc:circuitChip1", plankWood]] -} -inventorycontrollerupgrade { - input: [[ingotGold, "oc:analyzer", ingotGold] - [dropper, "oc:circuitChip2", craftingPiston] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -mfu { - input: [["oc:chamelium", gemLapis, "oc:chamelium"] - ["oc:linkedCard", "oc:adapter", "oc:linkedCard"] - ["oc:chamelium", gemLapis, "oc:chamelium"]] -} -leashupgrade { - input: [[ingotIron, {item="minecraft:lead"}, ingotIron] - [{item="minecraft:lead"}, "oc:materialCU", {item="minecraft:lead"}] - [ingotIron, {item="minecraft:lead"}, ingotIron]] -} -navigationupgrade { - input: [[ingotGold, compass, ingotGold] - ["oc:circuitChip2", {item=filled_map, subID=any}, "oc:circuitChip2"] - [ingotGold, potion, ingotGold]] -} -pistonupgrade { - input: [[ingotIron, craftingPiston, ingotIron] - [stickWood, "oc:circuitChip1", stickWood] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -stickypistonupgrade { - type: shapeless - input: ["oc:pistonUpgrade", "minecraft:slime_ball"] - output: 1 -} -signupgrade { - input: [[ingotIron, dyeBlack, ingotIron] - ["oc:circuitChip1", stickWood, "oc:circuitChip1"] - [ingotIron, pistonStickyBase, ingotIron]] -} -solargeneratorupgrade { - input: [[blockGlass, blockGlass, blockGlass] - ["oc:circuitChip3", blockLapis, "oc:circuitChip3"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -tankupgrade { - input: [[plankWood, fenceIron, plankWood] - [dispenser, cauldron, craftingPiston] - [plankWood, "oc:circuitChip1", plankWood]] -} -tankcontrollerupgrade { - input: [[ingotGold, glassBottle, ingotGold] - [dispenser, "oc:circuitChip2", craftingPiston] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -tractorbeamupgrade { - input: [[ingotGold, craftingPiston, ingotGold] - [ingotIron, "oc:capacitor", ingotIron] - [ingotGold, "oc:circuitChip3", ingotGold]] -} -tradingupgrade { - input: [[ingotGold, chest, ingotGold] - [emerald, "oc:circuitChip2", emerald] - [dropper, "oc:materialCircuitBoardPrinted", craftingPiston]] -} - -cardcontainer1 { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [craftingPiston, chest, ""] - [ingotIron, "oc:materialCard", ingotIron]] -} -cardcontainer2 { - input: [[ingotIron, "oc:circuitChip2", ingotIron] - [craftingPiston, chest, ""] - [ingotIron, "oc:materialCard", ingotIron]] -} -cardcontainer3 { - input: [[ingotGold, "oc:circuitChip2", ingotGold] - [craftingPiston, chest, ""] - [ingotGold, "oc:materialCard", ingotGold]] -} -upgradecontainer1 { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [craftingPiston, chest, ""] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -upgradecontainer2 { - input: [[ingotIron, "oc:circuitChip2", ingotIron] - [craftingPiston, chest, ""] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -upgradecontainer3 { - input: [[ingotGold, "oc:circuitChip2", ingotGold] - [craftingPiston, chest, ""] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} - -ingotiron { - type: shapeless - input: [iron_nugget, iron_nugget, iron_nugget, - iron_nugget, iron_nugget, iron_nugget, - iron_nugget, iron_nugget, iron_nugget] -} -chipdiamond { - type: shapeless - input: ["oc:materialCuttingWire", gemDiamond] - output: 6 -} -gemDiamond = false - -cuttingwire { - input: [[stickWood, nuggetIron, stickWood]] -} -acid { - type: shapeless - input: [bucketWater, sugar, slimeball, fermentedSpiderEye, bone] -} -disk { - input: [["", iron_nugget, ""] - [iron_nugget, "", iron_nugget] - ["", iron_nugget, ""]] -} -chamelium { - input: [[gravel, redstone, gravel], - [redstone, {item=coal, subID=1}, redstone], - [gravel, {item="minecraft:water_bucket"}, gravel]] - output: 16 -} -chameliumblock { - input: [["oc:chamelium", "oc:chamelium", "oc:chamelium"], - ["oc:chamelium", "oc:chamelium", "oc:chamelium"], - ["oc:chamelium", "oc:chamelium", "oc:chamelium"]] -} -endstone { - input: [[materialEnderPearl, "oc:chameliumBlock", materialEnderPearl] - ["oc:chameliumBlock", materialEnderPearl, "oc:chameliumBlock"] - [materialEnderPearl, "oc:chameliumBlock", materialEnderPearl]] - output: 4 -} -inkcartridgeempty { - input: [[iron_nugget, dispenser, iron_nugget], - ["oc:materialTransistor", bucket, "oc:materialTransistor"], - [iron_nugget, "oc:materialCircuitBoardPrinted", iron_nugget]] -} -inkcartridge { - type: shapeless - input: [dyeCyan, dyeMagenta, dyeYellow, dyeBlack, "oc:inkCartridgeEmpty"] -} - -buttongroup { - input: [[button, button, button] - [button, button, button]] -} -arrowkeys { - input: [["", button, ""] - [button, button, button]] -} -numpad { - input: [[button, button, button] - [button, button, button] - [button, button, button]] -} - -transistor { - input: [[ingotIron, ingotIron, ingotIron] - [nuggetGold, paper, nuggetGold] - ["", redstone, ""]] - output: 8 -} -chip1 { - input: [[iron_nugget, iron_nugget, iron_nugget] - [redstone, "oc:materialTransistor", redstone] - [iron_nugget, iron_nugget, iron_nugget]] - output: 8 -} -chip2 { - input: [[nuggetGold, nuggetGold, nuggetGold] - [redstone, "oc:materialTransistor", redstone] - [nuggetGold, nuggetGold, nuggetGold]] - output: 4 -} -chip3 { - input: [[chipDiamond, chipDiamond, chipDiamond] - [redstone, "oc:materialTransistor", redstone] - [chipDiamond, chipDiamond, chipDiamond]] - output: 2 -} -alu { - input: [[iron_nugget, redstone, iron_nugget] - ["oc:materialTransistor", "oc:circuitChip1", "oc:materialTransistor"] - [iron_nugget, "oc:materialTransistor", iron_nugget]] -} -apu1 { - input: [[nuggetGold, "oc:circuitChip1", nuggetGold] - ["oc:cpu2", "oc:componentBus1", "oc:graphicsCard1"] - [nuggetGold, "oc:circuitChip1", nuggetGold]] -} -apu2 { - input: [[chipDiamond, "oc:circuitChip2", chipDiamond] - ["oc:cpu3", "oc:componentBus2", "oc:graphicsCard2"] - [chipDiamond, "oc:circuitChip2", chipDiamond]] -} -componentbus1 { - input: [[iron_nugget, redstone, iron_nugget] - ["oc:circuitChip1", "oc:materialCU", ""] - [iron_nugget, "oc:materialCircuitBoardPrinted", iron_nugget]] -} -componentbus2 { - input: [[nuggetGold, redstone, nuggetGold] - ["oc:circuitChip2", "oc:materialCU", ""] - [nuggetGold, "oc:materialCircuitBoardPrinted", nuggetGold]] -} -componentbus3 { - input: [[chipDiamond, redstone, chipDiamond] - ["oc:circuitChip3", "oc:materialCU", ""] - [chipDiamond, "oc:materialCircuitBoardPrinted", chipDiamond]] -} -cpu1 { - input: [[iron_nugget, redstone, iron_nugget] - ["oc:circuitChip1", "oc:materialCU", "oc:circuitChip1"] - [iron_nugget, "oc:materialALU", iron_nugget]] -} -cpu2 { - input: [[nuggetGold, redstone, nuggetGold] - ["oc:circuitChip2", "oc:materialCU", "oc:circuitChip2"] - [nuggetGold, "oc:materialALU", nuggetGold]] -} -cpu3 { - input: [[chipDiamond, redstone, chipDiamond] - ["oc:circuitChip3", "oc:materialCU", "oc:circuitChip3"] - [chipDiamond, "oc:materialALU", chipDiamond]] -} -cu { - input: [[nuggetGold, redstone, nuggetGold] - ["oc:materialTransistor", clock, "oc:materialTransistor"] - [nuggetGold, "oc:materialTransistor", nuggetGold]] -} - -rawcircuitboard { - type: shapeless - input: [ingotGold, clay, dyeGreen] - output: 8 -} -circuitboard = false -printedcircuitboard { - type: furnace - input: "oc:materialCircuitBoardRaw" -} -card { - input: [[iron_nugget, "", ""] - [iron_nugget, "oc:materialCircuitBoardPrinted", ""] - [iron_nugget, nuggetGold, ""]] -} - -interweb { - input: [[string, string, string] - [string, materialEnderPearl, string] - [string, string, string]] -} - -adapter { - input: [[ingotIron, "oc:cable", ingotIron] - ["oc:cable", "oc:circuitChip1", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -assembler { - input: [[ingotIron, workbench, ingotIron] - [craftingPiston, "oc:circuitChip2", craftingPiston] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -cable { - input: [["", iron_nugget, ""] - [iron_nugget, redstone, iron_nugget] - ["", iron_nugget, ""]] - output: 4 -} -luaBios { - type: shapeless - input: ["oc:eeprom", "oc:manual"] -} -carpetedcapacitor { - type: shapeless - input: [carpet, "oc:capacitor"] -} -capacitor { - input: [[ingotIron, "oc:materialTransistor", ingotIron] - [nuggetGold, paper, nuggetGold] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -charger { - input: [[ingotIron, ingotGold, ingotIron] - ["oc:capacitor", "oc:circuitChip2", "oc:capacitor"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -case1 { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [fenceIron, chest, fenceIron] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -case2 { - input: [[ingotGold, "oc:circuitChip2", ingotGold] - [fenceIron, chest, fenceIron] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -case3 { - input: [[gemDiamond, "oc:circuitChip3", gemDiamond] - [fenceIron, chest, fenceIron] - [gemDiamond, "oc:materialCircuitBoardPrinted", gemDiamond]] -} -disassembler { - input: [["oc:materialCU", paneGlass, "oc:analyzer"] - [craftingPiston, "", obsidian] - [ingotIron, bucketLava, ingotIron]] -} -diskdrive { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [craftingPiston, stickWood, ""] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -geolyzer { - input: [[ingotGold, compass, ingotGold] - [eyeOfEnder, "oc:circuitChip2", eyeOfEnder] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -hologram1 { - input: [["oc:circuitChip2", paneGlass, "oc:circuitChip2"] - ["oc:materialCircuitBoardPrinted", chipDiamond, "oc:materialCircuitBoardPrinted"] - [obsidian, yellowDust, obsidian]] -} -hologram2 { - input: [["oc:circuitChip3", blockGlass, "oc:circuitChip3"] - ["oc:materialCircuitBoardPrinted", gemDiamond, "oc:materialCircuitBoardPrinted"] - [obsidian, blazePowder, obsidian]] -} -keyboard { - input: [["oc:materialButtonGroup", "oc:materialButtonGroup", "oc:materialButtonGroup"] - ["oc:materialButtonGroup", "oc:materialArrowKey", "oc:materialNumPad"]] -} -motionsensor { - input: [[ingotGold, daylightDetector, ingotGold] - [daylightDetector, "oc:cpu2", daylightDetector] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -netsplitter { - input: [[ingotIron, "oc:cable", ingotIron] - ["oc:cable", craftingPiston, "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -printer { - input: [[ingotIron, hopper, ingotIron] - [craftingPiston, "oc:circuitChip3", craftingPiston] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -powerconverter { - input: [[ingotIron, "oc:cable", ingotIron] - [ingotGold, "oc:circuitChip1", ingotGold] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -powerdistributor { - input: [[ingotIron, ingotGold, ingotIron] - ["oc:cable", "oc:circuitChip1", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -rack { - input: [[gemDiamond, "oc:wlanCard2", gemDiamond] - [fenceIron, chest, fenceIron] - ["oc:relay", "oc:materialCircuitBoardPrinted", "oc:powerDistributor"]] -} -raid { - input: [[iron_nugget, "oc:cpu3", iron_nugget] - ["oc:ram1", "oc:diskDrive", "oc:ram1"] - [iron_nugget, "oc:circuitChip2", iron_nugget]] -} -redstone { - input: [[ingotIron, "oc:circuitChip3", ingotIron] - [blockRedstone, "oc:redstoneCard1", blockRedstone] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -relay { - input: [[ingotIron, "oc:cable", ingotIron] - ["oc:cable", "oc:lanCard", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -screen1 { - input: [[ingotIron, redstone, ingotIron] - [redstone, "oc:circuitChip1", blockGlass] - [ingotIron, redstone, ingotIron]] -} -screen2 { - input: [[ingotGold, dyeRed, ingotGold] - [dyeGreen, "oc:circuitChip2", blockGlass] - [ingotGold, dyeBlue, ingotGold]] -} -screen3 { - input: [[obsidian, yellowDust, obsidian] - [yellowDust, "oc:circuitChip3", blockGlass] - [obsidian, yellowDust, obsidian]] -} -transposer { - input: [[ingotIron, "oc:inventoryControllerUpgrade", ingotIron] - [hopper, bucket, hopper] - [ingotIron, "oc:tankControllerUpgrade", ingotIron]] - output: 4 -} -waypoint { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - ["oc:materialTransistor", "oc:materialInterweb", "oc:materialTransistor"], - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} diff --git a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes deleted file mode 100644 index 1c3a45134d..0000000000 --- a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes +++ /dev/null @@ -1,353 +0,0 @@ -# Do not change this file, it is rewritten each time you start the game. -# Instead, use the user.recipes file to edit recipes by redefining them there. - -# Note that there is support for a number of GregTech machines, by using the -# appropriate `type` specifier. Available types are: -# - gt_alloySmelter : Alloy Smelter Recipe -# - gt_assembler : Circuit Assembler Machine -# - gt_bender : Plate Bending Machine Recipe -# - gt_canner : Canning Machine Recipe -# - gt_chemical : Chemical Recipe -# - gt_cnc : CNC-Machine Recipe -# - gt_cutter : Cutter Recipe -# - gt_fluidCanner : Fluid Canner Recipe -# - gt_formingPress : Forming Press Recipe -# - gt_lathe : Lathe Machine Recipe -# - gt_laserEngraver: Laser Engraver Recipe -# - gt_wiremill : Wiremill Recipe -# -# For these types, there a few more options for inputs and outputs. A full -# recipe using all these options would look like this: -# name { -# type: gt_??? -# input: ["primaryInput", "possiblyOptionalSecondaryInput"] -# count: [1, 2] # would mean 1 of primary, 2 of secondary -# output: 2 # size of primary output stack -# eu: EU consumed for the operation -# time: time it takes to complete the operation, in ticks. -# # The following are usually optional. -# secondaryOutput: ["secondaryOutput1", "secondaryOutput2"] # Max number depends on machine. -# secondaryOutputCount: [2, 2] # Like `count` to `input`. -# inputFluid: {name="water", amount="500"} -# outputFluid: {name="lava"} # defaults to amount = 1000 -# } - -include file("hardmode.recipes") - -analyzer { - # 32762 = Portable Scanner - input: [["oc:materialTransistor", torchRedstoneActive, "oc:materialTransistor"] - ["oc:circuitChip2", {item="gt.metaitem.01", subID=32762}, "oc:circuitChip2"] - [screwAluminium, craftingToolScrewdriver, screwAluminium]] -} - -server1 { - input: [["oc:circuitChip1", "oc:ram4", "oc:circuitChip1"] - ["oc:circuitChip2", "oc:case1", "oc:circuitChip2"] - [craftingToolScrewdriver, "oc:materialCircuitBoardPrinted", craftingToolWrench]] -} -server2 { - input: [["oc:circuitChip2", "oc:ram5", "oc:circuitChip2"] - ["oc:circuitChip3", "oc:case2", "oc:circuitChip3"] - [craftingToolScrewdriver, "oc:materialCircuitBoardPrinted", craftingToolWrench]] -} -server3 { - input: [["oc:circuitChip3", "oc:ram6", "oc:circuitChip3"] - ["oc:circuitChip3", "oc:case3", "oc:circuitChip3"] - [craftingToolScrewdriver, "oc:materialCircuitBoardPrinted", craftingToolWrench]] -} - -ram1 { - type: gt_assembler - input: ["oc:circuitChip1", "oc:materialCircuitBoardPrinted"] - count: [3, 3] - eu: 32 - time: 250 -} -ram2 { - input: [["oc:circuitChip1", "oc:circuitChip1", "oc:circuitChip1"] - ["oc:ram1", "oc:materialCircuitBoardPrinted", "oc:ram1"] - ["", craftingToolWrench, ""]] -} -ram3 { - input: [["oc:circuitChip2", "oc:circuitChip2", "oc:circuitChip2"] - ["oc:ram2", "oc:materialCircuitBoardPrinted", "oc:ram2"] - [circuitElite, craftingToolWrench, circuitElite]] -} -ram4 { - input: [["oc:circuitChip2", "oc:circuitChip2", "oc:circuitChip2"] - ["oc:ram3", "oc:materialCircuitBoardPrinted", "oc:ram3"] - [circuitElite, craftingToolWrench, circuitElite]] -} -ram5 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:circuitChip3"] - ["oc:ram4", "oc:materialCircuitBoardPrinted", "oc:ram4"] - [circuitUltimate, craftingToolWrench, circuitUltimate]] -} -ram6 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:circuitChip3"] - ["oc:ram5", "oc:materialCircuitBoardPrinted", "oc:ram5"] - [circuitUltimate, craftingToolWrench, circuitUltimate]] -} - -floppy { - input: [[screwAluminium, lever, screwAluminium] - [plateAluminium, "oc:materialDisk", plateAluminium] - [screwAluminium, craftingToolScrewdriver, screwAluminium]] -} -hdd1 { - input: [["oc:ram1", "oc:materialDisk", "oc:ram1"] - ["oc:ram1", "oc:materialDisk", "oc:ram1"] - [screwAluminium, craftingToolScrewdriver, "oc:circuitChip2"]] -} -hdd2 { - input: [["oc:hdd1", "oc:materialCircuitBoardPrinted" , "oc:hdd1"] - ["oc:circuitChip2", screwTitanium, "oc:circuitChip2"] - [screwTitanium, craftingToolScrewdriver, screwTitanium]] -} -hdd3 { - input: [["oc:hdd2", "oc:materialCircuitBoardPrinted", "oc:hdd2"] - ["oc:circuitChip3", screwTungstenSteel, "oc:circuitChip3"] - [screwTungstenSteel , craftingToolScrewdriver, screwTungstenSteel]] -} - -# graphicsCard1 { fallback to default } -# graphicsCard2 { fallback to default } -# graphicsCard3 { fallback to default } -redstonecard { - input: [[screwStainlessSteel , "oc:circuitChip2" , screwStainlessSteel] - ["comparator", "oc:materialCard", diode] - [screwStainlessSteel , craftingToolScrewdriver, screwStainlessSteel]] -} -lancard { - input: [[screwStainlessSteel , "oc:circuitChip2" , screwStainlessSteel] - ["oc:cable", "oc:materialCard", "oc:cable"] - [screwStainlessSteel , craftingToolScrewdriver, screwStainlessSteel]] -} -wlancard1 { - input: [[screwAluminium , "oc:circuitChip2" , screwAluminium] - [torchRedstoneActive, "oc:lanCard", torchRedstoneActive] - [screwAluminium , craftingToolScrewdriver, screwAluminium]] -} -wlancard2 { - input: [[screwTitanium , "oc:circuitChip3" , screwTitanium] - [materialEnderPearl, "oc:lanCard", materialEnderPearl] - [screwTitanium , craftingToolScrewdriver, screwTitanium]] -} - -craftingupgrade { - # 1 = LV Casing - input: [[screwStainlessSteel, "oc:materialCircuitBoardPrinted" , screwStainlessSteel] - ["oc:circuitChip2", {block="gt.blockcasings", subID=1}, "oc:circuitChip2"] - [screwStainlessSteel, craftingToolScrewdriver, screwStainlessSteel]] -} -generatorupgrade { - input: [[screwStainlessSteel, "oc:materialCircuitBoardPrinted" , screwStainlessSteel] - ["oc:circuitChip2", craftingGenerator, "oc:circuitChip2"] - [screwStainlessSteel, craftingToolScrewdriver, screwStainlessSteel]] -} -navigationupgrade { - # 2 = MV Casing - input: [[{block="gt.blockcasings", subID=2}, compass, screwStainlessSteel] - ["oc:circuitChip3", {item=filled_map, subID=any}, "oc:circuitChip3"] - [screwStainlessSteel, potion, craftingToolScrewdriver]] -} -signupgrade { - input: [[screwAluminium, dyeBlack, screwAluminium] - ["oc:circuitChip1", stickWood, "oc:circuitChip1"] - [screwAluminium, craftingToolScrewdriver, screwAluminium]] -} -solargeneratorupgrade { - # 32750 = Solar Panel - input: [[screwTitanium, "", screwTitanium] - ["oc:circuitChip3", {item="gt.metaitem.01", subID=32750} , "oc:circuitChip3"] - [screwTitanium, craftingToolScrewdriver, screwTitanium]] -} - -cuttingwire { - input: [[stickWood, ingotTin, stickWood] - ["", craftingToolWireCutter, ""]] -} -disk { - input: [["", plateAluminium, ""] - [plateAluminium, "", plateAluminium] - ["", plateAluminium, ""]] -} - -# buttonGroup { fallback to default } -# arrowKeys { fallback to default } -# numPad { fallback to default } - -transistor { - type: gt_assembler - input: [redstone, stickIron] - count: [1, 3] - eu: 16 - time: 500 - output: 6 -} -chip1 { - type: gt_assembler - input: ["ic2.itemPartCircuit", "oc:materialTransistor"] - count: [1, 4] - eu: 25 - time: 480 - output: 4 -} -chip2 { - type: gt_assembler - input: ["ic2.itemPartCircuitAdv", "oc:materialTransistor"] - count: [1, 8] - eu: 25 - time: 640 - output: 4 -} -chip3 { - type: gt_assembler - input: [circuitData, "oc:materialTransistor"] - count: [1, 16] - eu: 25 - time: 800 - output: 4 -} -alu { - type: gt_assembler - input: [comparator, "oc:circuitChip1"] - count: [3, 1] - eu: 24 - time: 500 - output: 1 -} -cpu1 { - input: [["oc:circuitChip2", "oc:materialALU", "oc:circuitChip2"] - [plateAluminium, "oc:materialCU", plateAluminium] - [screwAluminium, craftingToolScrewdriver, screwAluminium]] -} -cpu2 { - input: [["oc:circuitChip2", plateStainlessSteel, "oc:circuitChip2"] - ["oc:ram3", "oc:cpu1", "oc:ram3"] - ["oc:circuitChip2", craftingToolScrewdriver, "oc:circuitChip2"]] -} -cpu3 { - input: [["oc:circuitChip3", plateTitanium, "oc:circuitChip3"] - ["oc:ram5", "oc:cpu2", "oc:ram5"] - ["oc:circuitChip3", craftingToolScrewdriver, "oc:circuitChip3"]] -} -cu { - type: gt_assembler - input: [circuitElite, "oc:materialTransistor"] - count: [1, 6] - eu: 32 - time: 750 - output: 3 -} - -# rawCircuitBoard { fallback to default } -circuitboard { - type: furnace - input: "oc:materialCircuitBoardRaw" -} -printedcircuitboard { - type:shaped - input: [[dustTinyGold, cellSulfuricAcid] - ["oc:materialCircuitBoard", dustTinyGold]] -} -card { - input: [[stickIron, "oc:circuitChip2", "oc:materialTransistor"] - [stickIron, "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"] - [craftingToolScrewdriver, nuggetGold, nuggetGold]] -} - -adapter { - input: [["oc:cable", "oc:circuitChip1", "oc:cable"] - ["", {block="gt.blockcasings", subID=1}, ""] - ["oc:materialCircuitBoardPrinted", "oc:cable", craftingToolWrench]] -} -cable { - type: gt_assembler - input: [craftingWireCopper, dustEmerald] - count: [8, 1] - eu: 32 - time: 64 - output: 8 -} -carpetedcapacitor { - type: shapeless - input: [carpet, "oc:capacitor"] -} -capacitor { - # 7 = CESU - input: [["", {item="ic2.blockElectric", subID=7}, ""] - [{block="gt.blockcasings", subID=1}, "oc:materialTransistor", {block="gt.blockcasings", subID=1}] - ["oc:materialCircuitBoardPrinted", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] -} -charger { - # 2 = Chargepad (MFE) - input: [["", plateStainlessSteel, ""] - [{item="ic2.blockChargepad", subID=2}, {block="gt.blockcasings", subID=3}, {item="ic2.blockChargepad", subID=2}] - ["oc:circuitChip3", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] -} -case1 { - input: [[screwAluminium, "oc:materialCircuitBoardPrinted", craftingToolWrench] - ["ic2.reactorVentSpread", {block="gt.blockcasings", subID=1}, "ic2.reactorVentSpread"] - [screwAluminium, "oc:circuitChip1", craftingToolScrewdriver]] -} -case2 { - input: [[screwStainlessSteel , "oc:materialCircuitBoardPrinted", craftingToolWrench] - [{item="ic2.reactorVentGold", subID=1}, "oc:case1", {item="ic2.reactorVentGold", subID=1}] - [screwStainlessSteel, "oc:circuitChip2", craftingToolScrewdriver]] -} -case3 { - input: [[screwTitanium , "oc:materialCircuitBoardPrinted", craftingToolWrench] - [{item="ic2.reactorVentDiamond", subID=1}, "oc:case2", {item="ic2.reactorVentDiamond", subID=1}] - [screwTitanium, "oc:circuitChip3", craftingToolScrewdriver]] -} -diskdrive { - input: [["", "oc:circuitChip2", ""] - [craftingPiston, {block="gt.blockcasings", subID=1}, craftingLensWhite] - ["oc:circuitChip2", craftingToolWrench, "oc:circuitChip2"]] -} -# keyboard { fallback to default } -powerconverter { - # 4 = MV Transformer - input: [["", "oc:circuitChip2", ""] - [plateAluminium, {item="ic2.blockElectric", subID=4}, plateAluminium] - ["oc:materialCircuitBoardPrinted", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] -} -powerdistributor { - # 7 = CESU - input: [["", plateAluminium, ""] - [{item="ic2.blockElectric", subID=7}, {block="gt.blockcasings", subID=2}, circuitMaster] - ["oc:materialCircuitBoardPrinted", plateAluminium, craftingToolWrench]] -} -rack { - input: [[craftingToolScrewdriver, "oc:wlanCard2", craftingToolWrench] - [{item="ic2.reactorVentDiamond", subID=1}, chest, {item="ic2.reactorVentDiamond", subID=1}] - ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] -} -redstone { - # 32731 = Activity Detector - input: [[plateRedstone, "oc:materialCircuitBoardPrinted", plateRedstone] - [{item="gt.metaitem.01", subID=32731}, {block="gt.blockcasings", subID=2}, "oc:redstoneCard1"] - ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"]] -} -relay { - input: [["", "oc:lanCard", ""] - ["oc:cable", {block="gt.blockcasings", subID=2}, "oc:cable"] - ["oc:materialCircuitBoardPrinted", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] -} -screen1 { - input: [[plateAluminium, plateAluminium, craftingToolWrench] - [redstone, "oc:materialTransistor", paneGlass] - [plateAluminium, plateAluminium, craftingToolScrewdriver]] -} -screen2 { - input: [[plateStainlessSteel, screwStainlessSteel, craftingToolWrench] - ["oc:circuitChip2", "oc:screen1", {item="gt.metaitem.01", subID=32740}] - [plateStainlessSteel, screwStainlessSteel, craftingToolScrewdriver]] -} -screen3 { - input: [[plateTitanium, "oc:materialCircuitBoardPrinted", craftingToolWrench] - ["oc:circuitChip3", "oc:screen2", "oc:circuitChip3"] - [plateTitanium, "oc:materialCircuitBoardPrinted", craftingToolScrewdriver]] -} diff --git a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes b/src/main/resources/assets/opencomputers/recipes/hardmode.recipes deleted file mode 100644 index 14cc0646af..0000000000 --- a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes +++ /dev/null @@ -1,411 +0,0 @@ -# Do not change this file, it is rewritten each time you start the game. -# Instead, use the user.recipes file to edit recipes by redefining them there. - -include file("default.recipes") - -analyzer { - input: [["", torchRedstoneActive, ""] - ["oc:materialTransistor", "oc:circuitChip1", nuggetGold] - ["oc:materialTransistor", "oc:materialCircuitBoardPrinted", nuggetGold]] -} -terminal { - input: [[iron_nugget, "oc:solarGeneratorUpgrade", iron_nugget] - ["oc:circuitChip3", "oc:screen2", "oc:wlanCard2"] - [iron_nugget, "oc:keyboard", iron_nugget]] -} - -server1 { - input: [["oc:circuitChip1", "oc:ram4", "oc:circuitChip1"] - ["oc:circuitChip2", "oc:case1", "oc:circuitChip2"] - ["oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"]] -} -server2 { - input: [["oc:circuitChip2", "oc:ram5", "oc:circuitChip2"] - ["oc:circuitChip3", "oc:case2", "oc:circuitChip3"] - ["oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"]] -} -server3 { - input: [["oc:circuitChip3", "oc:ram6", "oc:circuitChip3"] - ["oc:circuitChip3", "oc:case3", "oc:circuitChip3"] - ["oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"]] -} - -ram1 { - input: [["oc:circuitChip1", "oc:circuitChip1", "oc:circuitChip1"] - ["oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"]] -} -ram2 { - input: [["oc:circuitChip1", "oc:circuitChip1", "oc:circuitChip1"] - ["oc:ram1", "oc:materialCircuitBoardPrinted", "oc:ram1"]] -} -ram3 { - input: [["oc:circuitChip2", "oc:circuitChip2", "oc:circuitChip2"] - ["oc:ram2", "oc:materialCircuitBoardPrinted", "oc:ram2"]] -} -ram4 { - input: [["oc:circuitChip2", "oc:circuitChip2", "oc:circuitChip2"] - ["oc:ram3", "oc:materialCircuitBoardPrinted", "oc:ram3"]] -} -ram5 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:circuitChip3"] - ["oc:ram4", "oc:materialCircuitBoardPrinted", "oc:ram4"]] -} -ram6 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:circuitChip3"] - ["oc:ram5", "oc:materialCircuitBoardPrinted", "oc:ram5"]] -} - -floppy { - input: [[iron_nugget, lever, iron_nugget] - ["oc:materialCircuitBoard", "oc:materialDisk", "oc:materialCircuitBoard"] - [iron_nugget, paper, iron_nugget]] -} -hdd1 { - input: [["oc:circuitChip1", "oc:materialDisk", ingotIron] - ["oc:materialCircuitBoardPrinted", "oc:materialDisk", craftingPiston] - ["oc:circuitChip1", "oc:materialDisk", ingotIron]] -} -hdd2 { - input: [[ingotGold, "oc:hdd1", ingotGold] - ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"] - [ingotGold, "oc:hdd1", ingotGold]] -} -hdd3 { - input: [["oc:circuitChip3", "oc:hdd2", "oc:circuitChip3"] - ["oc:ram1", "oc:materialCircuitBoardPrinted", "oc:ram1"] - ["oc:circuitChip3", "oc:hdd2", "oc:circuitChip3"]] -} - -abstractbuscard { - input: [[{block="StargateTech2:block.busCable"}, {item="StargateTech2:naquadah", subID=3}, ""] - ["", "oc:materialCard", ""]] -} -datacard2 { - input: [[nuggetGold, "oc:cpu1", "oc:circuitChip3"] - ["", "oc:dataCard1", ""]] -} -datacard3 { - input: [[gemDiamond, "oc:cpu2", "oc:ram5"] - ["", "oc:dataCard2", ""]] -} -graphicscard1 { - input: [["oc:circuitChip1", "oc:materialALU", "oc:ram1"] - ["", "oc:materialCard", ""]] -} -graphicscard2 { - input: [["oc:circuitChip2", "oc:circuitChip2", "oc:ram3"] - ["", "oc:graphicsCard1", ""]] -} -graphicscard3 { - input: [["oc:circuitChip3", "oc:circuitChip3", "oc:ram5"] - ["", "oc:graphicsCard2", ""]] -} -internetcard { - input: [["oc:materialInterweb", "oc:circuitChip3", torchRedstoneActive] - ["", "oc:wlanCard2", obsidian]] -} -redstonecard1 { - input: [[torchRedstoneActive, "oc:circuitChip1", ""] - ["", "oc:materialCard", ""]] -} -redstonecard2 { - input: [[blockRedstone, "oc:circuitChip2", materialEnderPearl] - ["", "oc:redstoneCard1", ""]] -} -lancard { - input: [["oc:cable", "oc:circuitChip1", ""] - ["", "oc:materialCard", ""]] -} -wlancard1 { - input: [[torchRedstoneActive, "oc:circuitChip1", torchRedstoneActive] - ["", "oc:lanCard", ""]] -} -wlancard2 { - input: [[materialEnderPearl, "oc:circuitChip2", ""] - ["", "oc:lanCard", ""]] -} -linkedcard { - input: [[eyeOfEnder, "", eyeOfEnder] - ["oc:wlanCard2", "oc:materialInterweb", "oc:wlanCard2"] - ["oc:circuitChip3", "", "oc:circuitChip3"]] - output: 2 # Note: all resulting cards are linked to each other. -} - -batteryupgrade1 { - input: [[ingotIron, nuggetGold, ingotIron] - ["oc:materialTransistor", "oc:capacitor", "oc:materialTransistor"] - [ingotIron, nuggetGold, ingotIron]] -} -batteryupgrade2 { - input: [[ingotGold, "oc:capacitor", ingotGold] - ["oc:materialTransistor", nuggetGold, "oc:materialTransistor"] - [ingotGold, "oc:capacitor", ingotGold]] -} -batteryupgrade3 { - input: [[gemDiamond, "oc:capacitor", gemDiamond] - ["oc:materialTransistor", "oc:capacitor", "oc:materialTransistor"] - [gemDiamond, "oc:capacitor", gemDiamond]] -} -craftingupgrade { - input: [[ingotIron, craftingPiston, ingotIron] - ["oc:circuitChip1", workbench, "oc:circuitChip1"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -experienceupgrade { - input: [[ingotGold, "oc:analyzer", ingotGold] - ["oc:circuitChip3", emerald, "oc:circuitChip3"] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -generatorupgrade { - input: [[ingotIron, "", ingotIron] - ["oc:circuitChip1", craftingPiston, "oc:circuitChip1"] - ["oc:materialCircuitBoardPrinted", ingotIron, "oc:materialCircuitBoardPrinted"]] -} -inventoryupgrade { - input: [[ingotIron, hopper, ingotIron] - [dispenser, chest, craftingPiston] - [ingotIron, "oc:circuitChip1", ingotIron]] -} -inventorycontrollerupgrade { - input: [[ingotGold, "oc:circuitChip2", ingotGold] - [dispenser, "oc:inventoryUpgrade", craftingPiston] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -navigationupgrade { - input: [[ingotGold, compass, ingotGold] - ["oc:circuitChip3", {item=filled_map, subID=any}, "oc:circuitChip3"] - [ingotGold, potion, ingotGold]] -} -signupgrade { - input: [[ingotIron, dyeBlack, ingotIron] - ["oc:circuitChip1", stickWood, "oc:circuitChip1"] - [ingotIron, pistonStickyBase, ingotIron]] -} -solargeneratorupgrade { - input: [[blockGlass, blockGlass, blockGlass] - ["oc:circuitChip3", "oc:generatorUpgrade", "oc:circuitChip3"]] -} -tradingupgrade { - input: [["oc:circuitChip2", chest, "oc:circuitChip2"] - [emerald, "oc:circuitChip2", emerald] - [dropper, "oc:materialCircuitBoardPrinted", craftingPiston]] -} - -cuttingwire { - input: [[stickWood, iron_nugget, stickWood]] -} -disk { - input: [["", iron_nugget, ""] - [iron_nugget, "", iron_nugget] - ["", iron_nugget, ""]] -} - -buttongroup { - input: [[button, button, button] - [button, button, button]] -} -arrowkeys { - input: [["", button, ""] - [button, button, button]] -} -numpad { - input: [[button, button, button] - [button, button, button] - [button, button, button]] -} - -transistor { - input: [[iron_nugget, iron_nugget, iron_nugget] - [nuggetGold, paper, nuggetGold] - ["", redstone, ""]] -} -chip1 { - input: [[iron_nugget, "", iron_nugget] - ["oc:materialTransistor", nuggetGold, "oc:materialTransistor"] - [iron_nugget, "", iron_nugget]] -} -chip2 { - input: [[nuggetGold, {item=dyePowder, subID=4} , nuggetGold] - ["oc:circuitChip1", netherquartz, "oc:circuitChip1"] - [nuggetGold, {item=dyePowder, subID=4}, nuggetGold]] -} -chip3 { - input: [[yellowDust, comparator, yellowDust] - ["oc:circuitChip2", gemDiamond, "oc:circuitChip2"] - [yellowDust, comparator, yellowDust]] -} -alu { - input: [[diode, torchRedstoneActive, diode] - ["oc:materialTransistor", "oc:materialTransistor", "oc:materialTransistor"] - [iron_nugget, redstone, iron_nugget]] -} -apu2 { - input: [[gemDiamond, "oc:circuitChip2", gemDiamond] - ["oc:cpu3", "oc:componentBus2", "oc:graphicsCard2"] - [gemDiamond, "oc:circuitChip2", gemDiamond]] -} -componentbus1 { - input: [[iron_nugget, redstone, iron_nugget] - ["oc:circuitChip1", "oc:materialCU", ""] - [iron_nugget, "oc:materialCircuitBoardPrinted", iron_nugget]] -} -componentbus2 { - input: [[nuggetGold, "oc:ram3", nuggetGold] - ["oc:circuitChip2", "oc:componentBus1", ""] - [nuggetGold, "oc:materialCircuitBoardPrinted", nuggetGold]] -} -componentbus3 { - input: [[gemDiamond, "oc:ram5", gemDiamond] - ["oc:circuitChip3", "oc:componentBus2", ""] - [gemDiamond, "oc:materialCircuitBoardPrinted", gemDiamond]] -} -cpu1 { - input: [[iron_nugget, redstone, iron_nugget] - ["oc:circuitChip1", "oc:materialCU", "oc:circuitChip1"] - [iron_nugget, "oc:materialALU", iron_nugget]] -} -cpu2 { - input: [[nuggetGold, "oc:ram3", nuggetGold] - ["oc:circuitChip2", "oc:cpu1", "oc:circuitChip2"] - [nuggetGold, "oc:ram3", nuggetGold]] -} -cpu3 { - input: [[gemDiamond, "oc:ram5", gemDiamond] - ["oc:circuitChip3", "oc:cpu2", "oc:circuitChip3"] - [gemDiamond, "oc:ram5", gemDiamond]] -} -cu { - input: [[nuggetGold, torchRedstoneActive, nuggetGold] - ["oc:materialTransistor", clock, "oc:materialTransistor"] - [nuggetGold, redstone, nuggetGold]] -} - -rawcircuitboard { - type: shapeless - input: ["oc:materialCuttingWire", clay, dyeGreen] -} -circuitboard { - type: furnace - input: "oc:materialCircuitBoardRaw" -} -printedcircuitboard { - type: shapeless - input: ["oc:materialCircuitBoard", nuggetGold, "oc:materialAcid"] - output: 1 -} -card { - input: [[iron_nugget, "oc:circuitChip1", "oc:materialTransistor"] - [iron_nugget, "oc:materialCircuitBoardPrinted", "oc:materialCircuitBoardPrinted"] - [iron_nugget, nuggetGold, nuggetGold]] -} - -interweb { - input: [[string, materialEnderPearl, string] - [materialEnderPearl, string, materialEnderPearl] - [string, materialEnderPearl, string]] -} - -adapter { - input: [[ingotIron, "oc:cable", ingotIron] - ["oc:cable", "oc:circuitChip1", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -cable { - input: [["", iron_nugget, ""] - [iron_nugget, redstone, iron_nugget] - ["", iron_nugget, ""]] - output: 4 -} -carpetedcapacitor { - type: shapeless - input: [carpet, "oc:capacitor"] -} -capacitor { - input: [[ingotIron, "oc:materialTransistor", ingotIron] - [nuggetGold, paper, nuggetGold] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -charger { - input: [[ingotIron, ingotGold, ingotIron] - ["oc:capacitor", "oc:circuitChip2", "oc:capacitor"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -case1 { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [fenceIron, chest, fenceIron] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -case2 { - input: [[ingotGold, "oc:circuitChip2", ingotGold] - ["oc:circuitChip2", "oc:case1", "oc:circuitChip2"] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -case3 { - input: [[gemDiamond, "oc:circuitChip3", gemDiamond] - ["oc:circuitChip3", "oc:case2", "oc:circuitChip3"] - [gemDiamond, "oc:materialCircuitBoardPrinted", gemDiamond]] -} -diskdrive { - input: [[ingotIron, "oc:circuitChip1", ingotIron] - [craftingPiston, stickWood, ""] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -geolyzer { - input: [[ingotGold, "oc:analyzer", ingotGold] - [eyeOfEnder, "oc:circuitChip2", eyeOfEnder] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -hologram1 { - input: [["oc:circuitChip2", paneGlass, "oc:circuitChip2"] - ["oc:materialCircuitBoardPrinted", gemDiamond, "oc:materialCircuitBoardPrinted"] - [obsidian, yellowDust, obsidian]] -} -hologram2 { - input: [["oc:circuitChip3", blockGlass, "oc:circuitChip3"] - ["oc:materialCircuitBoardPrinted", blockDiamond, "oc:materialCircuitBoardPrinted"] - [obsidian, blazePowder, obsidian]] -} -keyboard { - input: [["oc:materialButtonGroup", "oc:materialButtonGroup", "oc:materialButtonGroup"] - ["oc:materialButtonGroup", "oc:materialArrowKey", "oc:materialNumPad"]] -} -powerconverter { - input: [[ingotIron, "oc:cable", ingotIron] - [ingotGold, "oc:circuitChip1", ingotGold] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -powerdistributor { - input: [[ingotIron, ingotGold, ingotIron] - ["oc:cable", "oc:circuitChip1", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -rack { - input: [["oc:circuitChip3", "oc:wlanCard2", "oc:circuitChip3"] - [fenceIron, chest, fenceIron] - ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] -} -redstone { - input: [[ingotIron, "oc:circuitChip3", ingotIron] - [blockRedstone, "oc:redstoneCard1", blockRedstone] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -relay { - input: [[ingotIron, "oc:cable", ingotIron] - ["oc:cable", "oc:lanCard", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} -screen1 { - input: [[ingotIron, ingotIron, blockGlass] - [redstone, "oc:materialTransistor", blockGlass] - [ingotIron, ingotIron, blockGlass]] -} -screen2 { - input: [[ingotGold, dyeRed, ingotGold] - ["oc:circuitChip2", dyeGreen, "oc:screen1"] - [ingotGold, dyeBlue, ingotGold]] -} -screen3 { - input: [[obsidian, "oc:materialCircuitBoardPrinted", "oc:circuitChip3"] - [blazeRod, netherquartz, "oc:screen2"] - [obsidian, "oc:materialCircuitBoardPrinted", "oc:circuitChip3"]] -} diff --git a/src/main/resources/assets/opencomputers/recipes/peaceful.recipes b/src/main/resources/assets/opencomputers/recipes/peaceful.recipes deleted file mode 100644 index 83b57cd219..0000000000 --- a/src/main/resources/assets/opencomputers/recipes/peaceful.recipes +++ /dev/null @@ -1,79 +0,0 @@ -include file("default.recipes") - -redstonecard2 { - input: [[blockRedstone, "oc:circuitChip2", gemDiamond] - ["", "oc:materialCard", ""]] -} -wlancard1 { - input: [[torchRedstoneActive, "oc:circuitChip1", torchRedstoneActive] - ["", "oc:materialCard", ""]] -} -wlancard2 { - input: [[gemDiamond, "oc:circuitChip2", ""] - ["", "oc:materialCard", ""]] -} -linkedcard { - input: [[gemDiamond, "", gemDiamond] - ["oc:lanCard", "oc:materialInterweb", "oc:lanCard"] - ["oc:circuitChip3", "", "oc:circuitChip3"]] - output: 2 # Note: all resulting cards are linked to each other. -} - -angelupgrade { - input: [[ingotIron, gemDiamond, ingotIron] - ["oc:circuitChip1", pistonStickyBase, "oc:circuitChip1"] - [ingotIron, gemDiamond, ingotIron]] -} -chunkloaderupgrade { - input: [[ingotGold, blockGlass, ingotGold] - ["oc:circuitChip3", gemDiamond, "oc:circuitChip3"] - [obsidian, "oc:materialCircuitBoardPrinted", obsidian]] -} -inventoryupgrade { - input: [[plankWood, hopper, plankWood] - [dropper, chest, craftingPiston] - [plankWood, "oc:circuitChip1", plankWood]] -} -inventorycontrollerupgrade { - input: [[ingotGold, "oc:analyzer", ingotGold] - [dropper, "oc:circuitChip2", craftingPiston] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -signupgrade { - input: [[ingotIron, dyeBlack, ingotIron] - ["oc:circuitChip1", stickWood, "oc:circuitChip1"] - [ingotIron, craftingPiston, ingotIron]] -} -tankupgrade { - input: [[plankWood, fenceIron, plankWood] - [dropper, cauldron, craftingPiston] - [plankWood, "oc:circuitChip1", plankWood]] -} -tankcontrollerupgrade { - input: [[ingotGold, glassBottle, ingotGold] - [dropper, "oc:circuitChip2", craftingPiston] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} - -inkcartridgeempty { - input: [[iron_nugget, dropper, iron_nugget], - ["oc:materialTransistor", bucket, "oc:materialTransistor"], - [iron_nugget, "oc:materialCircuitBoardPrinted", iron_nugget]] -} - -interweb { - input: [[redstone, {block="minecraft:wool", subID=9}, redstone] - [{block="minecraft:wool", subID=9}, gemDiamond, {block="minecraft:wool", subID=9}] - [redstone, {block="minecraft:wool", subID=9}, redstone]] -} - -geolyzer { - input: [[ingotGold, compass, ingotGold] - [gemDiamond, "oc:circuitChip2", gemDiamond] - [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] -} -hologram2 { - input: [["oc:circuitChip3", blockGlass, "oc:circuitChip3"] - ["oc:materialCircuitBoardPrinted", blockDiamond, "oc:materialCircuitBoardPrinted"] - [obsidian, yellowDust, obsidian]] -} diff --git a/src/main/resources/assets/opencomputers/recipes/user.recipes b/src/main/resources/assets/opencomputers/recipes/user.recipes deleted file mode 100644 index 69c4632ba0..0000000000 --- a/src/main/resources/assets/opencomputers/recipes/user.recipes +++ /dev/null @@ -1,20 +0,0 @@ -# To use different sets of recipes, include other recipe files in the order of -# priority to use the recipes defined in them. The last include has the highest -# priority (i.e. included recipes simply replace the current definition for all -# already known recipes). - -# To disable a recipe, assign a boolean value to it. For example, to disable -# the recipe for the transistor: `transistor = false`. This will disable the -# recipe and hide the item in the creative tab and NEI. If you assign `true`, -# the recipe will still be disabled, but not hidden in the creative tab/NEI. - -include file("default.recipes") -#include file("hardmode.recipes") -#include file("gregtech.recipes") -#include file("peaceful.recipes") -#include file("your_custom.recipes") - -# You can also specify custom recipes in this file directly. Have a look at the -# default.recipes file to get an idea how recipes have to be structured. You'll -# want to define your recipes after all includes, to avoid those overwriting -# your recipes. \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json b/src/main/resources/data/forge/tags/blocks/end_stones.json similarity index 100% rename from src/main/resources/data/opencomputers/tags/blocks/stoneendstone.json rename to src/main/resources/data/forge/tags/blocks/end_stones.json diff --git a/src/main/resources/data/forge/tags/items/end_stones.json b/src/main/resources/data/forge/tags/items/end_stones.json new file mode 100644 index 0000000000..55c89af86e --- /dev/null +++ b/src/main/resources/data/forge/tags/items/end_stones.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:endstone" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/acid.json b/src/main/resources/data/opencomputers/recipes/acid.json new file mode 100644 index 0000000000..15b1167adf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/acid.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:water_bucket" + }, + { + "item": "minecraft:sugar" + }, + { + "tag": "forge:slimeballs" + }, + { + "item": "minecraft:fermented_spider_eye" + }, + { + "item": "minecraft:bone" + } + ], + "result": { + "item": "opencomputers:acid" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/adapter.json b/src/main/resources/data/opencomputers/recipes/adapter.json new file mode 100644 index 0000000000..3f6d502d52 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/adapter.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ici", + "cCc", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:cable" + } + }, + "result": { + "item": "opencomputers:adapter" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/alu.json b/src/main/resources/data/opencomputers/recipes/alu.json new file mode 100644 index 0000000000..d288e261e8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/alu.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iri", + "TCT", + "iTi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:alu" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/analyzer.json b/src/main/resources/data/opencomputers/recipes/analyzer.json new file mode 100644 index 0000000000..a904121de9 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/analyzer.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "r ", + "Tg", + "Pg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "item": "minecraft:redstone_torch" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:analyzer" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/angelupgrade.json b/src/main/resources/data/opencomputers/recipes/angelupgrade.json new file mode 100644 index 0000000000..5e04b328ad --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/angelupgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iei", + "CpC", + "iei" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "e": { + "tag": "forge:ender_pearls" + }, + "p": { + "item": "minecraft:sticky_piston" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:angelupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/apu1.json b/src/main/resources/data/opencomputers/recipes/apu1.json new file mode 100644 index 0000000000..9019f939c2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/apu1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gCg", + "PBG", + "gCg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:componentbus1" + }, + "P": { + "tag": "opencomputers:cpu2" + }, + "G": { + "tag": "opencomputers:graphicscard1" + } + }, + "result": { + "item": "opencomputers:apu1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/apu2.json b/src/main/resources/data/opencomputers/recipes/apu2.json new file mode 100644 index 0000000000..6d05fbd626 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/apu2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "dCd", + "PBG", + "dCd" + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:componentbus2" + }, + "P": { + "tag": "opencomputers:cpu3" + }, + "G": { + "tag": "opencomputers:graphicscard2" + } + }, + "result": { + "item": "opencomputers:apu2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/arrowkeys.json b/src/main/resources/data/opencomputers/recipes/arrowkeys.json new file mode 100644 index 0000000000..0001b732a2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/arrowkeys.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " b ", + "bbb" + ], + "key": { + "b": { + "item": "minecraft:stone_button" + } + }, + "result": { + "item": "opencomputers:arrowkeys" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/assembler.json b/src/main/resources/data/opencomputers/recipes/assembler.json new file mode 100644 index 0000000000..05e98a2a5f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/assembler.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iwi", + "pCp", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "w": { + "item": "minecraft:crafting_table" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:assembler" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/batteryupgrade1.json b/src/main/resources/data/opencomputers/recipes/batteryupgrade1.json new file mode 100644 index 0000000000..54a468451e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/batteryupgrade1.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "igi", + "fCf", + "igi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "f": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:capacitor" + } + }, + "result": { + "item": "opencomputers:batteryupgrade1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/batteryupgrade2.json b/src/main/resources/data/opencomputers/recipes/batteryupgrade2.json new file mode 100644 index 0000000000..a3fa203fa6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/batteryupgrade2.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "fgf", + "iCi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "f": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:capacitor" + } + }, + "result": { + "item": "opencomputers:batteryupgrade2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/batteryupgrade3.json b/src/main/resources/data/opencomputers/recipes/batteryupgrade3.json new file mode 100644 index 0000000000..2c8175b7d8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/batteryupgrade3.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "CDC", + "iCi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "D": { + "tag": "opencomputers:chipdiamond" + }, + "C": { + "tag": "opencomputers:capacitor" + } + }, + "result": { + "item": "opencomputers:batteryupgrade3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/buttongroup.json b/src/main/resources/data/opencomputers/recipes/buttongroup.json new file mode 100644 index 0000000000..e362e8e1b7 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/buttongroup.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "bbb", + "bbb" + ], + "key": { + "b": { + "item": "minecraft:stone_button" + } + }, + "result": { + "item": "opencomputers:buttongroup" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cable.json b/src/main/resources/data/opencomputers/recipes/cable.json new file mode 100644 index 0000000000..69ac62b492 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cable.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " i ", + "iri", + " i " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + } + }, + "result": { + "item": "opencomputers:cable", + "count": 4 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/capacitor.json b/src/main/resources/data/opencomputers/recipes/capacitor.json new file mode 100644 index 0000000000..da8b6f41dc --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/capacitor.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iTi", + "gpg", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "p": { + "item": "minecraft:paper" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:capacitor" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/card.json b/src/main/resources/data/opencomputers/recipes/card.json new file mode 100644 index 0000000000..e2c6cbf75b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/card.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "i ", + "iP", + "ig" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:card" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cardcontainer1.json b/src/main/resources/data/opencomputers/recipes/cardcontainer1.json new file mode 100644 index 0000000000..22af6e535d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cardcontainer1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "pc ", + "iBi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:cardcontainer1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cardcontainer2.json b/src/main/resources/data/opencomputers/recipes/cardcontainer2.json new file mode 100644 index 0000000000..d19606d58d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cardcontainer2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "pc ", + "iBi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:cardcontainer2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cardcontainer3.json b/src/main/resources/data/opencomputers/recipes/cardcontainer3.json new file mode 100644 index 0000000000..5b64f2e233 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cardcontainer3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gCg", + "pc ", + "gBg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:cardcontainer3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/carpetedcapacitor.json b/src/main/resources/data/opencomputers/recipes/carpetedcapacitor.json new file mode 100644 index 0000000000..f5558ecca6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/carpetedcapacitor.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:capacitor" + }, + { + "item": "minecraft:white_carpet" + } + ], + "result": { + "item": "opencomputers:carpetedcapacitor" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/case1.json b/src/main/resources/data/opencomputers/recipes/case1.json new file mode 100644 index 0000000000..5a6a1e8403 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/case1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "bcb", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "c": { + "tag": "forge:chests" + }, + "b": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:case1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/case2.json b/src/main/resources/data/opencomputers/recipes/case2.json new file mode 100644 index 0000000000..7b7a5d8375 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/case2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gCg", + "bcb", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "c": { + "tag": "forge:chests" + }, + "b": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:case2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/case3.json b/src/main/resources/data/opencomputers/recipes/case3.json new file mode 100644 index 0000000000..19977cf8c3 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/case3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "dCd", + "bcb", + "dPd" + ], + "key": { + "d": { + "tag": "forge:gems/diamond" + }, + "c": { + "tag": "forge:chests" + }, + "b": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:case3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium.json b/src/main/resources/data/opencomputers/recipes/chamelium.json new file mode 100644 index 0000000000..142061bcca --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "grg", + "rcr", + "gwg" + ], + "key": { + "g": { + "tag": "forge:gravel" + }, + "c": { + "item": "minecraft:coal" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "w": { + "item": "minecraft:water_bucket" + } + }, + "result": { + "item": "opencomputers:chamelium", + "count": 16 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chameliumblock.json b/src/main/resources/data/opencomputers/recipes/chameliumblock.json new file mode 100644 index 0000000000..26e10cdc9b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chameliumblock.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CCC", + "CCC", + "CCC" + ], + "key": { + "C": { + "tag": "opencomputers:chamelium" + } + }, + "result": { + "item": "opencomputers:chameliumblock", + "nbt": { + "Damage": 15 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chameliumsplit.json b/src/main/resources/data/opencomputers/recipes/chameliumsplit.json new file mode 100644 index 0000000000..474752cacf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chameliumsplit.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:chameliumblock" + } + ], + "result": { + "item": "opencomputers:chamelium", + "count": 9 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/charger.json b/src/main/resources/data/opencomputers/recipes/charger.json new file mode 100644 index 0000000000..53e3bf9ff4 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/charger.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "igi", + "cCc", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:ingots/gold" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:capacitor" + } + }, + "result": { + "item": "opencomputers:charger" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chip1.json b/src/main/resources/data/opencomputers/recipes/chip1.json new file mode 100644 index 0000000000..bc19e69fb7 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chip1.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "rTr", + "iii" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "T": { + "tag": "opencomputers:materialtransistor" + } + }, + "result": { + "item": "opencomputers:chip1", + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chip2.json b/src/main/resources/data/opencomputers/recipes/chip2.json new file mode 100644 index 0000000000..46e02f835f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chip2.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ggg", + "rTr", + "ggg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "T": { + "tag": "opencomputers:materialtransistor" + } + }, + "result": { + "item": "opencomputers:chip2", + "count": 4 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chip3.json b/src/main/resources/data/opencomputers/recipes/chip3.json new file mode 100644 index 0000000000..4175689448 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chip3.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ddd", + "rTr", + "ddd" + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "T": { + "tag": "opencomputers:materialtransistor" + } + }, + "result": { + "item": "opencomputers:chip3", + "count": 2 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chipdiamond.json b/src/main/resources/data/opencomputers/recipes/chipdiamond.json new file mode 100644 index 0000000000..1154a176d3 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chipdiamond.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:materialcuttingwire" + }, + { + "tag": "forge:gems/diamond" + } + ], + "result": { + "item": "opencomputers:chipdiamond", + "count": 6 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chunkloaderupgrade.json b/src/main/resources/data/opencomputers/recipes/chunkloaderupgrade.json new file mode 100644 index 0000000000..79f50e7d5b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chunkloaderupgrade.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gsg", + "CeC", + "oPo" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "s": { + "tag": "forge:glass" + }, + "o": { + "tag": "forge:obsidian" + }, + "e": { + "item": "minecraft:ender_eye" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:chunkloaderupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/componentbus1.json b/src/main/resources/data/opencomputers/recipes/componentbus1.json new file mode 100644 index 0000000000..d1dd337f6f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/componentbus1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iri", + "CU ", + "iPi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:componentbus1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/componentbus2.json b/src/main/resources/data/opencomputers/recipes/componentbus2.json new file mode 100644 index 0000000000..9170c64a42 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/componentbus2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "grg", + "CU ", + "gPg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:componentbus2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/componentbus3.json b/src/main/resources/data/opencomputers/recipes/componentbus3.json new file mode 100644 index 0000000000..d66b7019fc --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/componentbus3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "drd", + "CU ", + "dPd" + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:componentbus3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cpu1.json b/src/main/resources/data/opencomputers/recipes/cpu1.json new file mode 100644 index 0000000000..4b7300b1c8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cpu1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iri", + "CUC", + "iAi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "A": { + "tag": "opencomputers:materialalu" + } + }, + "result": { + "item": "opencomputers:cpu1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cpu2.json b/src/main/resources/data/opencomputers/recipes/cpu2.json new file mode 100644 index 0000000000..08b581bdc9 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cpu2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "grg", + "CUC", + "gAg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "A": { + "tag": "opencomputers:materialalu" + } + }, + "result": { + "item": "opencomputers:cpu2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cpu3.json b/src/main/resources/data/opencomputers/recipes/cpu3.json new file mode 100644 index 0000000000..8293bc2a8d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cpu3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "drd", + "CUC", + "dAd" + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "A": { + "tag": "opencomputers:materialalu" + } + }, + "result": { + "item": "opencomputers:cpu3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/craftingupgrade.json b/src/main/resources/data/opencomputers/recipes/craftingupgrade.json new file mode 100644 index 0000000000..cafa5ae156 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/craftingupgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "i i", + "CwC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "w": { + "item": "minecraft:crafting_table" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:craftingupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cu.json b/src/main/resources/data/opencomputers/recipes/cu.json new file mode 100644 index 0000000000..d12240688b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cu.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "grg", + "TcT", + "gTg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "c": { + "item": "minecraft:clock" + }, + "T": { + "tag": "opencomputers:materialtransistor" + } + }, + "result": { + "item": "opencomputers:cu" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/cuttingwire.json b/src/main/resources/data/opencomputers/recipes/cuttingwire.json new file mode 100644 index 0000000000..343093c2e9 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/cuttingwire.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "sis" + ], + "key": { + "s": { + "tag": "forge:rods/wooden" + }, + "i": { + "tag": "forge:nuggets/iron" + } + }, + "result": { + "item": "opencomputers:cuttingwire" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/databaseupgrade1.json b/src/main/resources/data/opencomputers/recipes/databaseupgrade1.json new file mode 100644 index 0000000000..f23b93b915 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/databaseupgrade1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iAi", + "CHC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "A": { + "tag": "opencomputers:analyzer" + }, + "H": { + "tag": "opencomputers:hdd1" + } + }, + "result": { + "item": "opencomputers:databaseupgrade1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/databaseupgrade2.json b/src/main/resources/data/opencomputers/recipes/databaseupgrade2.json new file mode 100644 index 0000000000..c6576ea95d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/databaseupgrade2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iAi", + "CHC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "A": { + "tag": "opencomputers:analyzer" + }, + "H": { + "tag": "opencomputers:hdd2" + } + }, + "result": { + "item": "opencomputers:databaseupgrade2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/databaseupgrade3.json b/src/main/resources/data/opencomputers/recipes/databaseupgrade3.json new file mode 100644 index 0000000000..4a194ba8dc --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/databaseupgrade3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iAi", + "CHC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "A": { + "tag": "opencomputers:analyzer" + }, + "H": { + "tag": "opencomputers:hdd3" + } + }, + "result": { + "item": "opencomputers:databaseupgrade3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/datacard1.json b/src/main/resources/data/opencomputers/recipes/datacard1.json new file mode 100644 index 0000000000..9c3eb403ef --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/datacard1.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iAC", + " B " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "A": { + "tag": "opencomputers:materialalu" + } + }, + "result": { + "item": "opencomputers:datacard1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/datacard2.json b/src/main/resources/data/opencomputers/recipes/datacard2.json new file mode 100644 index 0000000000..f76d6038ff --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/datacard2.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gPC", + " B " + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "P": { + "tag": "opencomputers:cpu1" + } + }, + "result": { + "item": "opencomputers:datacard2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/datacard3.json b/src/main/resources/data/opencomputers/recipes/datacard3.json new file mode 100644 index 0000000000..f6cda2b60f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/datacard3.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "dPM", + " B " + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "M": { + "tag": "opencomputers:ram5" + }, + "P": { + "tag": "opencomputers:cpu2" + } + }, + "result": { + "item": "opencomputers:datacard3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/disassembler.json b/src/main/resources/data/opencomputers/recipes/disassembler.json new file mode 100644 index 0000000000..41d0a8e3cc --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/disassembler.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "UgA", + "p o", + "ili" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:glass_panes" + }, + "l": { + "item": "minecraft:lava_bucket" + }, + "o": { + "tag": "forge:obsidian" + }, + "p": { + "tag": "forge:pistons" + }, + "U": { + "tag": "opencomputers:materialcu" + }, + "A": { + "tag": "opencomputers:analyzer" + } + }, + "result": { + "item": "opencomputers:disassembler" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/disk.json b/src/main/resources/data/opencomputers/recipes/disk.json new file mode 100644 index 0000000000..ae74390769 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/disk.json @@ -0,0 +1,16 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " i ", + "i i", + " i " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + } + }, + "result": { + "item": "opencomputers:disk" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/diskdrive.json b/src/main/resources/data/opencomputers/recipes/diskdrive.json new file mode 100644 index 0000000000..4049801bab --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/diskdrive.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "ps ", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "s": { + "tag": "forge:rods/wooden" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:diskdrive" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/diskdrivemountable.json b/src/main/resources/data/opencomputers/recipes/diskdrivemountable.json new file mode 100644 index 0000000000..58689b43bb --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/diskdrivemountable.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "oCo", + "fDf", + "oPo" + ], + "key": { + "o": { + "tag": "forge:obsidian" + }, + "f": { + "item": "minecraft:iron_bars" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "D": { + "tag": "opencomputers:diskdrive" + } + }, + "result": { + "item": "opencomputers:diskdrivemountable" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/dronecase1.json b/src/main/resources/data/opencomputers/recipes/dronecase1.json new file mode 100644 index 0000000000..bd37834b2f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/dronecase1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "EcE", + "CMC", + "EBE" + ], + "key": { + "c": { + "item": "minecraft:compass" + }, + "E": { + "tag": "forge:end_stones" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:componentbus2" + }, + "M": { + "tag": "opencomputers:microcontrollercase1" + } + }, + "result": { + "item": "opencomputers:dronecase1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/dronecase2.json b/src/main/resources/data/opencomputers/recipes/dronecase2.json new file mode 100644 index 0000000000..47e5ed298e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/dronecase2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "EcE", + "CMC", + "EBE" + ], + "key": { + "c": { + "item": "minecraft:compass" + }, + "E": { + "tag": "forge:end_stones" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:componentbus3" + }, + "M": { + "tag": "opencomputers:microcontrollercase2" + } + }, + "result": { + "item": "opencomputers:dronecase2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/eeprom.json b/src/main/resources/data/opencomputers/recipes/eeprom.json new file mode 100644 index 0000000000..9b8572103a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/eeprom.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gTg", + "pCp", + "grg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "p": { + "item": "minecraft:paper" + }, + "r": { + "item": "minecraft:redstone_torch" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:eeprom" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/endstone.json b/src/main/resources/data/opencomputers/recipes/endstone.json new file mode 100644 index 0000000000..0d872c3c48 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/endstone.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "eCe", + "CeC", + "eCe" + ], + "key": { + "e": { + "tag": "forge:ender_pearls" + }, + "C": { + "tag": "opencomputers:chameliumblock" + } + }, + "result": { + "item": "opencomputers:endstone" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/experienceupgrade.json b/src/main/resources/data/opencomputers/recipes/experienceupgrade.json new file mode 100644 index 0000000000..136f7e3030 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/experienceupgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "g g", + "CeC", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "e": { + "tag": "forge:gems/emerald" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:experienceupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy.json b/src/main/resources/data/opencomputers/recipes/floppy.json new file mode 100644 index 0000000000..72c332f673 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ili", + "pDp", + "ipi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "p": { + "item": "minecraft:paper" + }, + "l": { + "item": "minecraft:lever" + }, + "D": { + "tag": "opencomputers:materialdisk" + } + }, + "result": { + "item": "opencomputers:floppy", + "nbt": { + "oc:color": 8 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/generatorupgrade.json b/src/main/resources/data/opencomputers/recipes/generatorupgrade.json new file mode 100644 index 0000000000..d367d0d843 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/generatorupgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "i i", + "CpC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:generatorupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/geolyzer.json b/src/main/resources/data/opencomputers/recipes/geolyzer.json new file mode 100644 index 0000000000..e7899a2e4f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/geolyzer.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gcg", + "eCe", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "c": { + "item": "minecraft:compass" + }, + "e": { + "item": "minecraft:ender_eye" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:geolyzer" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/graphicscard1.json b/src/main/resources/data/opencomputers/recipes/graphicscard1.json new file mode 100644 index 0000000000..9ca3f647f6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/graphicscard1.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CAM", + " B " + ], + "key": { + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "A": { + "tag": "opencomputers:materialalu" + }, + "M": { + "tag": "opencomputers:ram1" + } + }, + "result": { + "item": "opencomputers:graphicscard1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/graphicscard2.json b/src/main/resources/data/opencomputers/recipes/graphicscard2.json new file mode 100644 index 0000000000..0689f0ee3e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/graphicscard2.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CAM", + " B " + ], + "key": { + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "A": { + "tag": "opencomputers:materialalu" + }, + "M": { + "tag": "opencomputers:ram3" + } + }, + "result": { + "item": "opencomputers:graphicscard2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/graphicscard3.json b/src/main/resources/data/opencomputers/recipes/graphicscard3.json new file mode 100644 index 0000000000..f47935e0db --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/graphicscard3.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CAM", + " B " + ], + "key": { + "C": { + "tag": "opencomputers:circuitchip3" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "A": { + "tag": "opencomputers:materialalu" + }, + "M": { + "tag": "opencomputers:ram5" + } + }, + "result": { + "item": "opencomputers:graphicscard3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd1.json b/src/main/resources/data/opencomputers/recipes/hdd1.json new file mode 100644 index 0000000000..1a4848e4ed --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CDi", + "PDp", + "CDi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "p": { + "tag": "forge:pistons" + }, + "D": { + "tag": "opencomputers:materialdisk" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:hdd1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd2.json b/src/main/resources/data/opencomputers/recipes/hdd2.json new file mode 100644 index 0000000000..bb91754de5 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CDg", + "PDp", + "CDg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "p": { + "tag": "forge:pistons" + }, + "D": { + "tag": "opencomputers:materialdisk" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "C": { + "tag": "opencomputers:circuitchip2" + } + }, + "result": { + "item": "opencomputers:hdd2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd3.json b/src/main/resources/data/opencomputers/recipes/hdd3.json new file mode 100644 index 0000000000..2e552d2487 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CDd", + "PDp", + "CDd" + ], + "key": { + "d": { + "tag": "forge:gems/diamond" + }, + "p": { + "tag": "forge:pistons" + }, + "D": { + "tag": "opencomputers:materialdisk" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "C": { + "tag": "opencomputers:circuitchip3" + } + }, + "result": { + "item": "opencomputers:hdd3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hologram1.json b/src/main/resources/data/opencomputers/recipes/hologram1.json new file mode 100644 index 0000000000..051ad335e9 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hologram1.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CgC", + "PdP", + "oGo" + ], + "key": { + "d": { + "tag": "opencomputers:chipdiamond" + }, + "g": { + "tag": "forge:glass_panes" + }, + "o": { + "tag": "forge:obsidian" + }, + "G": { + "tag": "forge:dusts/glowstone" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:hologram1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hologram2.json b/src/main/resources/data/opencomputers/recipes/hologram2.json new file mode 100644 index 0000000000..9874b8967b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hologram2.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CgC", + "PdP", + "obo" + ], + "key": { + "d": { + "tag": "forge:gems/diamond" + }, + "g": { + "tag": "forge:glass" + }, + "o": { + "tag": "forge:obsidian" + }, + "b": { + "item": "minecraft:blaze_powder" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:hologram2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hoverboots.json b/src/main/resources/data/opencomputers/recipes/hoverboots.json new file mode 100644 index 0000000000..5fe0b7addd --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hoverboots.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iHi", + "LDL", + "iCi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "L": { + "tag": "forge:leather" + }, + "C": { + "tag": "opencomputers:capacitor" + }, + "D": { + "tag": "opencomputers:dronecase1" + }, + "H": { + "tag": "opencomputers:hoverupgrade2" + } + }, + "result": { + "item": "opencomputers:hoverboots" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hoverupgrade1.json b/src/main/resources/data/opencomputers/recipes/hoverupgrade1.json new file mode 100644 index 0000000000..c38ca51e02 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hoverupgrade1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "fCf", + "ili", + "fPf" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "f": { + "tag": "forge:feathers" + }, + "l": { + "tag": "forge:leather" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:hoverupgrade1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hoverupgrade2.json b/src/main/resources/data/opencomputers/recipes/hoverupgrade2.json new file mode 100644 index 0000000000..4546f46371 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hoverupgrade2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "eCe", + "gig", + "ePe" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "e": { + "tag": "forge:end_stones" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:hoverupgrade2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/inkcartridge.json b/src/main/resources/data/opencomputers/recipes/inkcartridge.json new file mode 100644 index 0000000000..655c4a619a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/inkcartridge.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:inkcartridgeempty" + }, + { + "tag": "forge:dyes/cyan" + }, + { + "tag": "forge:dyes/magenta" + }, + { + "tag": "forge:dyes/yellow" + }, + { + "tag": "forge:dyes/black" + } + ], + "result": { + "item": "opencomputers:inkcartridge" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/inkcartridgeempty.json b/src/main/resources/data/opencomputers/recipes/inkcartridgeempty.json new file mode 100644 index 0000000000..f05081ba5c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/inkcartridgeempty.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "idi", + "TbT", + "iPi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "b": { + "item": "minecraft:bucket" + }, + "d": { + "item": "minecraft:dispenser" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:inkcartridgeempty" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/internetcard.json b/src/main/resources/data/opencomputers/recipes/internetcard.json new file mode 100644 index 0000000000..bc91064094 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/internetcard.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ICr", + " Bo" + ], + "key": { + "r": { + "item": "minecraft:redstone_torch" + }, + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "I": { + "tag": "opencomputers:materialinterweb" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:internetcard" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/interweb.json b/src/main/resources/data/opencomputers/recipes/interweb.json new file mode 100644 index 0000000000..75cd6112ff --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/interweb.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "sss", + "ses", + "sss" + ], + "key": { + "s": { + "tag": "forge:string" + }, + "e": { + "tag": "forge:ender_pearls" + } + }, + "result": { + "item": "opencomputers:interweb" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/inventorycontrollerupgrade.json b/src/main/resources/data/opencomputers/recipes/inventorycontrollerupgrade.json new file mode 100644 index 0000000000..abc5d11cea --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/inventorycontrollerupgrade.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gAg", + "dCp", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "d": { + "item": "minecraft:dropper" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "A": { + "tag": "opencomputers:analyzer" + } + }, + "result": { + "item": "opencomputers:inventorycontrollerupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/inventoryupgrade.json b/src/main/resources/data/opencomputers/recipes/inventoryupgrade.json new file mode 100644 index 0000000000..89dac6713b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/inventoryupgrade.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "whw", + "dcp", + "wCw" + ], + "key": { + "w": { + "tag": "minecraft:planks" + }, + "c": { + "tag": "forge:chests" + }, + "h": { + "item": "minecraft:hopper" + }, + "d": { + "item": "minecraft:dropper" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:inventoryupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/keyboard.json b/src/main/resources/data/opencomputers/recipes/keyboard.json new file mode 100644 index 0000000000..d84354246e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/keyboard.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "BBB", + "BAN" + ], + "key": { + "B": { + "tag": "opencomputers:materialbuttongroup" + }, + "A": { + "tag": "opencomputers:materialarrowkey" + }, + "N": { + "tag": "opencomputers:materialnumpad" + } + }, + "result": { + "item": "opencomputers:keyboard" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/lancard.json b/src/main/resources/data/opencomputers/recipes/lancard.json new file mode 100644 index 0000000000..c05ba47d6d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/lancard.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cC", + " B" + ], + "key": { + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + }, + "c": { + "tag": "opencomputers:cable" + } + }, + "result": { + "item": "opencomputers:lancard" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/leashupgrade.json b/src/main/resources/data/opencomputers/recipes/leashupgrade.json new file mode 100644 index 0000000000..f6961c829a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/leashupgrade.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ili", + "lCl", + "ili" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "l": { + "item": "minecraft:lead" + }, + "C": { + "tag": "opencomputers:materialcu" + } + }, + "result": { + "item": "opencomputers:leashupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/linkedcard.json b/src/main/resources/data/opencomputers/recipes/linkedcard.json new file mode 100644 index 0000000000..cc4b151f9d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/linkedcard.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "e e", + "LIL", + "C C" + ], + "key": { + "e": { + "item": "minecraft:ender_eye" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "I": { + "tag": "opencomputers:materialinterweb" + }, + "L": { + "tag": "opencomputers:lancard" + } + }, + "result": { + "item": "opencomputers:linkedcard" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/lootdisks/openos.json b/src/main/resources/data/opencomputers/recipes/lootdisks/openos.json new file mode 100644 index 0000000000..5538f43146 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/lootdisks/openos.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:floppy" + }, + { + "tag": "opencomputers:manual" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": { + "oc:data": { + "oc:fs.label": "openos" + }, + "oc:lootFactory": "opencomputers:openos", + "oc:color": 13, + "display": { + "Name": "OpenOS (Operating System)" + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/lootdisks/oppm.json b/src/main/resources/data/opencomputers/recipes/lootdisks/oppm.json new file mode 100644 index 0000000000..cc90e6462d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/lootdisks/oppm.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:floppy" + }, + { + "tag": "opencomputers:materialinterweb" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": { + "oc:data": { + "oc:fs.label": "oppm" + }, + "oc:lootFactory": "opencomputers:oppm", + "oc:color": 9, + "display": { + "Name": "OPPM (Package Manager)" + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/manual.json b/src/main/resources/data/opencomputers/recipes/manual.json new file mode 100644 index 0000000000..0e9ace7902 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/manual.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:book" + }, + { + "tag": "opencomputers:circuitchip1" + } + ], + "result": { + "item": "opencomputers:manual" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/mfu.json b/src/main/resources/data/opencomputers/recipes/mfu.json new file mode 100644 index 0000000000..38fc731b06 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/mfu.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "clc", + "LAL", + "clc" + ], + "key": { + "l": { + "tag": "forge:gems/lapis" + }, + "c": { + "tag": "opencomputers:chamelium" + }, + "A": { + "tag": "opencomputers:adapter" + }, + "L": { + "tag": "opencomputers:linkedcard" + } + }, + "result": { + "item": "opencomputers:mfu" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/microcontrollercase1.json b/src/main/resources/data/opencomputers/recipes/microcontrollercase1.json new file mode 100644 index 0000000000..8677c74faf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/microcontrollercase1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "rcr", + "iPi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "c": { + "tag": "forge:chests" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:microcontrollercase1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/microcontrollercase2.json b/src/main/resources/data/opencomputers/recipes/microcontrollercase2.json new file mode 100644 index 0000000000..48e014f315 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/microcontrollercase2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gCg", + "rcr", + "gPg" + ], + "key": { + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:storage_blocks/redstone" + }, + "c": { + "tag": "forge:chests" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:microcontrollercase2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/motionsensor.json b/src/main/resources/data/opencomputers/recipes/motionsensor.json new file mode 100644 index 0000000000..213b73a3b1 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/motionsensor.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gdg", + "dCd", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "d": { + "item": "minecraft:daylight_detector" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "C": { + "tag": "opencomputers:cpu2" + } + }, + "result": { + "item": "opencomputers:motionsensor" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/nanomachines.json b/src/main/resources/data/opencomputers/recipes/nanomachines.json new file mode 100644 index 0000000000..ab356d004d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/nanomachines.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SRS", + "PAM", + "SCS" + ], + "key": { + "S": { + "tag": "opencomputers:chamelium" + }, + "A": { + "tag": "opencomputers:materialacid" + }, + "C": { + "tag": "opencomputers:capacitor" + }, + "R": { + "tag": "opencomputers:wlancard2" + }, + "M": { + "tag": "opencomputers:ram1" + }, + "P": { + "tag": "opencomputers:cpu2" + } + }, + "result": { + "item": "opencomputers:nanomachines" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/navigationupgrade.json b/src/main/resources/data/opencomputers/recipes/navigationupgrade.json new file mode 100644 index 0000000000..e9bc95e141 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/navigationupgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gcg", + "CmC", + "gpg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "c": { + "item": "minecraft:compass" + }, + "m": { + "item": "minecraft:filled_map" + }, + "p": { + "item": "minecraft:potion" + }, + "C": { + "tag": "opencomputers:circuitchip2" + } + }, + "result": { + "item": "opencomputers:navigationupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/netsplitter.json b/src/main/resources/data/opencomputers/recipes/netsplitter.json new file mode 100644 index 0000000000..16e9330a7a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/netsplitter.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ici", + "cpc", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "p": { + "tag": "forge:pistons" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:cable" + } + }, + "result": { + "item": "opencomputers:netsplitter" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/numpad.json b/src/main/resources/data/opencomputers/recipes/numpad.json new file mode 100644 index 0000000000..82aef08dcf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/numpad.json @@ -0,0 +1,16 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "bbb", + "bbb", + "bbb" + ], + "key": { + "b": { + "item": "minecraft:stone_button" + } + }, + "result": { + "item": "opencomputers:numpad" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/pistonupgrade.json b/src/main/resources/data/opencomputers/recipes/pistonupgrade.json new file mode 100644 index 0000000000..84ef4b78de --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/pistonupgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ipi", + "sCs", + "iPi" + ], + "key": { + "s": { + "tag": "forge:rods/wooden" + }, + "i": { + "tag": "forge:ingots/iron" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:pistonupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/powerconverter.json b/src/main/resources/data/opencomputers/recipes/powerconverter.json new file mode 100644 index 0000000000..4d8d4ebec2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/powerconverter.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ici", + "gCg", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:ingots/gold" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:cable" + } + }, + "result": { + "item": "opencomputers:powerconverter" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/powerdistributor.json b/src/main/resources/data/opencomputers/recipes/powerdistributor.json new file mode 100644 index 0000000000..eacc393044 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/powerdistributor.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "igi", + "cCc", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:ingots/gold" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:cable" + } + }, + "result": { + "item": "opencomputers:powerdistributor" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/printedcircuitboard.json b/src/main/resources/data/opencomputers/recipes/printedcircuitboard.json new file mode 100644 index 0000000000..907b0d4052 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/printedcircuitboard.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smelting", + "ingredient": { + "tag": "opencomputers:materialcircuitboardraw" + }, + "result": "opencomputers:printedcircuitboard", + "experience": 0.1, + "cookingtime": 200 +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/printer.json b/src/main/resources/data/opencomputers/recipes/printer.json new file mode 100644 index 0000000000..8b689d686a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/printer.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ihi", + "pCp", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "h": { + "item": "minecraft:hopper" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:printer" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/rack.json b/src/main/resources/data/opencomputers/recipes/rack.json new file mode 100644 index 0000000000..76759006ee --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/rack.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "dWd", + "bcb", + "RPD" + ], + "key": { + "d": { + "tag": "forge:gems/diamond" + }, + "c": { + "tag": "forge:chests" + }, + "b": { + "item": "minecraft:iron_bars" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "W": { + "tag": "opencomputers:wlancard2" + }, + "R": { + "tag": "opencomputers:relay" + }, + "D": { + "tag": "opencomputers:powerdistributor" + } + }, + "result": { + "item": "opencomputers:rack" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/raid.json b/src/main/resources/data/opencomputers/recipes/raid.json new file mode 100644 index 0000000000..41cf6e7da7 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/raid.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iPi", + "MDM", + "iCi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "M": { + "tag": "opencomputers:ram1" + }, + "P": { + "tag": "opencomputers:cpu3" + }, + "D": { + "tag": "opencomputers:diskdrive" + } + }, + "result": { + "item": "opencomputers:raid" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram1.json b/src/main/resources/data/opencomputers/recipes/ram1.json new file mode 100644 index 0000000000..86e31a3858 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram1.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cic", + " P " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "c": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram2.json b/src/main/resources/data/opencomputers/recipes/ram2.json new file mode 100644 index 0000000000..28bea65d17 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram2.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cCc", + " P " + ], + "key": { + "c": { + "tag": "opencomputers:circuitchip1" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram3.json b/src/main/resources/data/opencomputers/recipes/ram3.json new file mode 100644 index 0000000000..b13d00989b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram3.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cic", + " P " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "c": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram4.json b/src/main/resources/data/opencomputers/recipes/ram4.json new file mode 100644 index 0000000000..b7a25de2d3 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram4.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cCc", + " P " + ], + "key": { + "c": { + "tag": "opencomputers:circuitchip2" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram4" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram5.json b/src/main/resources/data/opencomputers/recipes/ram5.json new file mode 100644 index 0000000000..11447a4a1e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram5.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cic", + " P " + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "c": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram5" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/ram6.json b/src/main/resources/data/opencomputers/recipes/ram6.json new file mode 100644 index 0000000000..dae767c322 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/ram6.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "CCC", + "cPc" + ], + "key": { + "c": { + "tag": "opencomputers:circuitchip2" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:ram6" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/rawcircuitboard.json b/src/main/resources/data/opencomputers/recipes/rawcircuitboard.json new file mode 100644 index 0000000000..0d4c7e7ac5 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/rawcircuitboard.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "forge:ingots/gold" + }, + { + "item": "minecraft:clay" + }, + { + "tag": "forge:dyes/green" + } + ], + "result": { + "item": "opencomputers:rawcircuitboard", + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/redstone.json b/src/main/resources/data/opencomputers/recipes/redstone.json new file mode 100644 index 0000000000..9ef9f2986d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/redstone.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "rRr", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "r": { + "tag": "forge:storage_blocks/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "R": { + "tag": "opencomputers:redstonecard1" + } + }, + "result": { + "item": "opencomputers:redstone" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/redstonecard1.json b/src/main/resources/data/opencomputers/recipes/redstonecard1.json new file mode 100644 index 0000000000..a22d86acd0 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/redstonecard1.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "rC", + " B" + ], + "key": { + "r": { + "item": "minecraft:redstone_torch" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:redstonecard1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/redstonecard2.json b/src/main/resources/data/opencomputers/recipes/redstonecard2.json new file mode 100644 index 0000000000..ac2e427a62 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/redstonecard2.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "rCe", + " B " + ], + "key": { + "e": { + "tag": "forge:ender_pearls" + }, + "r": { + "tag": "forge:storage_blocks/redstone" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:redstonecard2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/relay.json b/src/main/resources/data/opencomputers/recipes/relay.json new file mode 100644 index 0000000000..566e5e56e3 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/relay.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ici", + "cLc", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "c": { + "tag": "opencomputers:cable" + }, + "L": { + "tag": "opencomputers:lancard" + } + }, + "result": { + "item": "opencomputers:relay" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/screen1.json b/src/main/resources/data/opencomputers/recipes/screen1.json new file mode 100644 index 0000000000..7157d7ce7e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/screen1.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iri", + "rCG", + "iri" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "G": { + "tag": "forge:glass" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:screen1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/screen2.json b/src/main/resources/data/opencomputers/recipes/screen2.json new file mode 100644 index 0000000000..061b2621b2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/screen2.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gcg", + "pCG", + "gyg" + ], + "key": { + "c": { + "tag": "forge:dyes/red" + }, + "p": { + "tag": "forge:dyes/green" + }, + "y": { + "tag": "forge:dyes/blue" + }, + "g": { + "tag": "forge:ingots/gold" + }, + "G": { + "tag": "forge:glass" + }, + "C": { + "tag": "opencomputers:circuitchip2" + } + }, + "result": { + "item": "opencomputers:screen2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/screen3.json b/src/main/resources/data/opencomputers/recipes/screen3.json new file mode 100644 index 0000000000..285bc5058a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/screen3.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ogo", + "gCG", + "ogo" + ], + "key": { + "g": { + "tag": "forge:dusts/glowstone" + }, + "G": { + "tag": "forge:glass" + }, + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip3" + } + }, + "result": { + "item": "opencomputers:screen3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/server1.json b/src/main/resources/data/opencomputers/recipes/server1.json new file mode 100644 index 0000000000..cc5f366766 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/server1.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iRi", + "CBC", + "oPo" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "B": { + "tag": "opencomputers:componentbus1" + }, + "R": { + "tag": "opencomputers:ram2" + } + }, + "result": { + "item": "opencomputers:server1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/server2.json b/src/main/resources/data/opencomputers/recipes/server2.json new file mode 100644 index 0000000000..6e19104647 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/server2.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gRg", + "CBC", + "oPo" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "B": { + "tag": "opencomputers:componentbus2" + }, + "R": { + "tag": "opencomputers:ram4" + } + }, + "result": { + "item": "opencomputers:server2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/server3.json b/src/main/resources/data/opencomputers/recipes/server3.json new file mode 100644 index 0000000000..3c8651e26e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/server3.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "dRd", + "CBC", + "oPo" + ], + "key": { + "d": { + "tag": "forge:gems/diamond" + }, + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "B": { + "tag": "opencomputers:componentbus3" + }, + "R": { + "tag": "opencomputers:ram6" + } + }, + "result": { + "item": "opencomputers:server3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/signupgrade.json b/src/main/resources/data/opencomputers/recipes/signupgrade.json new file mode 100644 index 0000000000..a9ff81200e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/signupgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ibi", + "CsC", + "ipi" + ], + "key": { + "s": { + "tag": "forge:rods/wooden" + }, + "b": { + "tag": "forge:dyes/black" + }, + "i": { + "tag": "forge:ingots/iron" + }, + "p": { + "item": "minecraft:sticky_piston" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:signupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/solargeneratorupgrade.json b/src/main/resources/data/opencomputers/recipes/solargeneratorupgrade.json new file mode 100644 index 0000000000..92f5643f16 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/solargeneratorupgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "ggg", + "ClC", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:glass" + }, + "l": { + "tag": "forge:storage_blocks/lapis" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:solargeneratorupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/stickypistonupgrade.json b/src/main/resources/data/opencomputers/recipes/stickypistonupgrade.json new file mode 100644 index 0000000000..0316de4caa --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/stickypistonupgrade.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "tag": "opencomputers:pistonupgrade" + }, + { + "tag": "forge:slimeballs" + } + ], + "result": { + "item": "opencomputers:stickypistonupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tabletcase1.json b/src/main/resources/data/opencomputers/recipes/tabletcase1.json new file mode 100644 index 0000000000..df3725413c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tabletcase1.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gbg", + "BMC", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "b": { + "item": "minecraft:stone_button" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "B": { + "tag": "opencomputers:componentbus1" + }, + "M": { + "tag": "opencomputers:screen2" + } + }, + "result": { + "item": "opencomputers:tabletcase1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tabletcase2.json b/src/main/resources/data/opencomputers/recipes/tabletcase2.json new file mode 100644 index 0000000000..7c8aec84d6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tabletcase2.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "cbg", + "BMC", + "cPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "b": { + "item": "minecraft:stone_button" + }, + "c": { + "tag": "opencomputers:circuitchip2" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "B": { + "tag": "opencomputers:componentbus3" + }, + "M": { + "tag": "opencomputers:screen2" + } + }, + "result": { + "item": "opencomputers:tabletcase2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tankcontrollerupgrade.json b/src/main/resources/data/opencomputers/recipes/tankcontrollerupgrade.json new file mode 100644 index 0000000000..959ab130e7 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tankcontrollerupgrade.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gbg", + "dCp", + "gPg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "b": { + "item": "minecraft:glass_bottle" + }, + "d": { + "item": "minecraft:dispenser" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:tankcontrollerupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tankupgrade.json b/src/main/resources/data/opencomputers/recipes/tankupgrade.json new file mode 100644 index 0000000000..d3418ba101 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tankupgrade.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "dcp", + "wCw" + ], + "key": { + "w": { + "tag": "minecraft:planks" + }, + "c": { + "item": "minecraft:cauldron" + }, + "b": { + "item": "minecraft:iron_bars" + }, + "d": { + "item": "minecraft:dispenser" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + } + }, + "result": { + "item": "opencomputers:tankupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/terminal.json b/src/main/resources/data/opencomputers/recipes/terminal.json new file mode 100644 index 0000000000..da5b905cbf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/terminal.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iSi", + "CMR", + "iKi" + ], + "key": { + "i": { + "tag": "forge:nuggets/iron" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "M": { + "tag": "opencomputers:screen2" + }, + "K": { + "tag": "opencomputers:keyboard" + }, + "R": { + "tag": "opencomputers:wlancard2" + }, + "S": { + "tag": "opencomputers:solargeneratorupgrade" + } + }, + "result": { + "item": "opencomputers:terminal" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/terminalserver.json b/src/main/resources/data/opencomputers/recipes/terminalserver.json new file mode 100644 index 0000000000..34c4050148 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/terminalserver.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "oRo", + "RCR", + "oPo" + ], + "key": { + "o": { + "tag": "forge:obsidian" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "R": { + "tag": "opencomputers:wlancard2" + } + }, + "result": { + "item": "opencomputers:terminalserver" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/texturepicker.json b/src/main/resources/data/opencomputers/recipes/texturepicker.json new file mode 100644 index 0000000000..c8523a4ba0 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/texturepicker.json @@ -0,0 +1,40 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "DRG", + "BAP", + "YMW" + ], + "key": { + "D": { + "tag": "forge:dyes/black" + }, + "R": { + "tag": "forge:dyes/red" + }, + "G": { + "tag": "forge:dyes/green" + }, + "B": { + "tag": "forge:dyes/blue" + }, + "P": { + "tag": "forge:dyes/purple" + }, + "Y": { + "tag": "forge:dyes/yellow" + }, + "M": { + "tag": "forge:dyes/magenta" + }, + "W": { + "tag": "forge:dyes/white" + }, + "A": { + "tag": "opencomputers:analyzer" + } + }, + "result": { + "item": "opencomputers:texturepicker" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tractorbeamupgrade.json b/src/main/resources/data/opencomputers/recipes/tractorbeamupgrade.json new file mode 100644 index 0000000000..1d541f8e33 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tractorbeamupgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gpg", + "iBi", + "gCg" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:ingots/gold" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip3" + }, + "B": { + "tag": "opencomputers:capacitor" + } + }, + "result": { + "item": "opencomputers:tractorbeamupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/tradingupgrade.json b/src/main/resources/data/opencomputers/recipes/tradingupgrade.json new file mode 100644 index 0000000000..741371de22 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/tradingupgrade.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gcg", + "eCe", + "dPp" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "e": { + "tag": "forge:gems/emerald" + }, + "c": { + "tag": "forge:chests" + }, + "d": { + "item": "minecraft:dropper" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:tradingupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/transistor.json b/src/main/resources/data/opencomputers/recipes/transistor.json new file mode 100644 index 0000000000..92ab21d0c6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/transistor.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "gpg", + " r " + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "g": { + "tag": "forge:nuggets/gold" + }, + "r": { + "tag": "forge:dusts/redstone" + }, + "p": { + "item": "minecraft:paper" + } + }, + "result": { + "item": "opencomputers:transistor", + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/transposer.json b/src/main/resources/data/opencomputers/recipes/transposer.json new file mode 100644 index 0000000000..0bd83c4c43 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/transposer.json @@ -0,0 +1,29 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iIi", + "hbh", + "iTi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "b": { + "item": "minecraft:bucket" + }, + "h": { + "item": "minecraft:hopper" + }, + "I": { + "tag": "opencomputers:inventorycontrollerupgrade" + }, + "T": { + "tag": "opencomputers:tankcontrollerupgrade" + } + }, + "result": { + "item": "opencomputers:transposer", + "count": 4 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/upgradecontainer1.json b/src/main/resources/data/opencomputers/recipes/upgradecontainer1.json new file mode 100644 index 0000000000..e6bc47e0f1 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/upgradecontainer1.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "pc ", + "iBi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:upgradecontainer1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/upgradecontainer2.json b/src/main/resources/data/opencomputers/recipes/upgradecontainer2.json new file mode 100644 index 0000000000..c097e3fcb5 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/upgradecontainer2.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "pc ", + "iBi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:upgradecontainer2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/upgradecontainer3.json b/src/main/resources/data/opencomputers/recipes/upgradecontainer3.json new file mode 100644 index 0000000000..47c980d932 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/upgradecontainer3.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gCg", + "pc ", + "gBg" + ], + "key": { + "g": { + "tag": "forge:ingots/gold" + }, + "c": { + "tag": "forge:chests" + }, + "p": { + "tag": "forge:pistons" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcircuitboardprinted" + } + }, + "result": { + "item": "opencomputers:upgradecontainer3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/waypoint.json b/src/main/resources/data/opencomputers/recipes/waypoint.json new file mode 100644 index 0000000000..a8998b1a69 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/waypoint.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "iCi", + "TIT", + "iPi" + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "T": { + "tag": "opencomputers:materialtransistor" + }, + "P": { + "tag": "opencomputers:materialcircuitboardprinted" + }, + "I": { + "tag": "opencomputers:materialinterweb" + } + }, + "result": { + "item": "opencomputers:waypoint" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/wlancard1.json b/src/main/resources/data/opencomputers/recipes/wlancard1.json new file mode 100644 index 0000000000..0d728e324f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/wlancard1.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "rCr", + " B " + ], + "key": { + "r": { + "item": "minecraft:redstone_torch" + }, + "C": { + "tag": "opencomputers:circuitchip1" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:wlancard1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/wlancard2.json b/src/main/resources/data/opencomputers/recipes/wlancard2.json new file mode 100644 index 0000000000..42e2c20ed6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/wlancard2.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "eC", + " B" + ], + "key": { + "e": { + "tag": "forge:ender_pearls" + }, + "C": { + "tag": "opencomputers:circuitchip2" + }, + "B": { + "tag": "opencomputers:materialcard" + } + }, + "result": { + "item": "opencomputers:wlancard2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/wrench.json b/src/main/resources/data/opencomputers/recipes/wrench.json new file mode 100644 index 0000000000..1d1ee14115 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/wrench.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "i i", + " C ", + " i " + ], + "key": { + "i": { + "tag": "forge:ingots/iron" + }, + "C": { + "tag": "opencomputers:circuitchip2" + } + }, + "result": { + "item": "opencomputers:wrench" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/adapter.json b/src/main/resources/data/opencomputers/tags/items/adapter.json new file mode 100644 index 0000000000..40ea055820 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/adapter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:adapter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/assembler.json b/src/main/resources/data/opencomputers/tags/items/assembler.json new file mode 100644 index 0000000000..ce253c7d63 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/assembler.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:assembler" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/cable.json b/src/main/resources/data/opencomputers/tags/items/cable.json new file mode 100644 index 0000000000..ddf88f324c --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/cable.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:cable" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/capacitor.json b/src/main/resources/data/opencomputers/tags/items/capacitor.json new file mode 100644 index 0000000000..d5c5604646 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/capacitor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:capacitor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/carpetedcapacitor.json b/src/main/resources/data/opencomputers/tags/items/carpetedcapacitor.json new file mode 100644 index 0000000000..3b0f159796 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/carpetedcapacitor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:carpetedcapacitor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/case1.json b/src/main/resources/data/opencomputers/tags/items/case1.json new file mode 100644 index 0000000000..36fe770a86 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/case1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/case2.json b/src/main/resources/data/opencomputers/tags/items/case2.json new file mode 100644 index 0000000000..731dfce4f6 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/case2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/case3.json b/src/main/resources/data/opencomputers/tags/items/case3.json new file mode 100644 index 0000000000..81cf6d2e6f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/case3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:case3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/chameliumblock.json b/src/main/resources/data/opencomputers/tags/items/chameliumblock.json new file mode 100644 index 0000000000..96191a5dfa --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/chameliumblock.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:chameliumblock" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/charger.json b/src/main/resources/data/opencomputers/tags/items/charger.json new file mode 100644 index 0000000000..1d72aca9d4 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/charger.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:charger" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/disassembler.json b/src/main/resources/data/opencomputers/tags/items/disassembler.json new file mode 100644 index 0000000000..b8edc0505a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/disassembler.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:disassembler" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/diskdrive.json b/src/main/resources/data/opencomputers/tags/items/diskdrive.json new file mode 100644 index 0000000000..c672b364d5 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/diskdrive.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:diskdrive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/geolyzer.json b/src/main/resources/data/opencomputers/tags/items/geolyzer.json new file mode 100644 index 0000000000..b5dee04f2f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/geolyzer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:geolyzer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hologram1.json b/src/main/resources/data/opencomputers/tags/items/hologram1.json new file mode 100644 index 0000000000..f86e9ea22f --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hologram1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hologram1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/hologram2.json b/src/main/resources/data/opencomputers/tags/items/hologram2.json new file mode 100644 index 0000000000..8cd7c2c5fa --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/hologram2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:hologram2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/keyboard.json b/src/main/resources/data/opencomputers/tags/items/keyboard.json new file mode 100644 index 0000000000..46c3a95875 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/keyboard.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:keyboard" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/motionsensor.json b/src/main/resources/data/opencomputers/tags/items/motionsensor.json new file mode 100644 index 0000000000..5bedd10526 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/motionsensor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:motionsensor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/netsplitter.json b/src/main/resources/data/opencomputers/tags/items/netsplitter.json new file mode 100644 index 0000000000..1a34747e99 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/netsplitter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:netsplitter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/powerconverter.json b/src/main/resources/data/opencomputers/tags/items/powerconverter.json new file mode 100644 index 0000000000..f1c25c0541 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/powerconverter.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:powerconverter" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/powerdistributor.json b/src/main/resources/data/opencomputers/tags/items/powerdistributor.json new file mode 100644 index 0000000000..327ec64106 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/powerdistributor.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:powerdistributor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/printer.json b/src/main/resources/data/opencomputers/tags/items/printer.json new file mode 100644 index 0000000000..e519cbe8b0 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/printer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:printer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/rack.json b/src/main/resources/data/opencomputers/tags/items/rack.json new file mode 100644 index 0000000000..5c21c51ef2 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/rack.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:rack" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/raid.json b/src/main/resources/data/opencomputers/tags/items/raid.json new file mode 100644 index 0000000000..94d790f968 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/raid.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:raid" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/redstone.json b/src/main/resources/data/opencomputers/tags/items/redstone.json new file mode 100644 index 0000000000..b02efc6970 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:redstone" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/relay.json b/src/main/resources/data/opencomputers/tags/items/relay.json new file mode 100644 index 0000000000..8777de68ce --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/relay.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:relay" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/screen1.json b/src/main/resources/data/opencomputers/tags/items/screen1.json new file mode 100644 index 0000000000..3715f2df5a --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/screen1.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen1" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/screen2.json b/src/main/resources/data/opencomputers/tags/items/screen2.json new file mode 100644 index 0000000000..43611f12cc --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/screen2.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen2" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/screen3.json b/src/main/resources/data/opencomputers/tags/items/screen3.json new file mode 100644 index 0000000000..7747d82efb --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/screen3.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:screen3" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/transposer.json b/src/main/resources/data/opencomputers/tags/items/transposer.json new file mode 100644 index 0000000000..b27df92e33 --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/transposer.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:transposer" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/tags/items/waypoint.json b/src/main/resources/data/opencomputers/tags/items/waypoint.json new file mode 100644 index 0000000000..9096c6243d --- /dev/null +++ b/src/main/resources/data/opencomputers/tags/items/waypoint.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "opencomputers:waypoint" + ] +} \ No newline at end of file From 4aaf219c076a7cd94cffc2822e01533391c2a748 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 10 Oct 2022 00:06:02 +0200 Subject: [PATCH 110/159] Fix missing power distributor item --- src/main/scala/li/cil/oc/common/init/Blocks.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index 647f669e23..c2d0066322 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -30,9 +30,9 @@ object Blocks { Items.registerBlock(new Hologram(defaultProps, Tier.Two), Constants.BlockName.HologramTier2, defaultItemProps.rarity(Rarity.UNCOMMON)) Items.registerBlock(new Keyboard(Properties.of(Material.STONE).strength(2, 5).noOcclusion), Constants.BlockName.Keyboard, defaultItemProps) Items.registerBlock(new MotionSensor(defaultProps), Constants.BlockName.MotionSensor, defaultItemProps) - Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter, defaultItemProps) - Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor, - new Item.Properties().tab(if (Settings.get.ignorePower) CreativeTab else null)) + Items.registerBlock(new PowerConverter(defaultProps), Constants.BlockName.PowerConverter, + new Item.Properties().tab(if (!Settings.get.ignorePower) CreativeTab else null)) + Items.registerBlock(new PowerDistributor(defaultProps), Constants.BlockName.PowerDistributor, defaultItemProps) Items.registerBlock(new Printer(defaultProps), Constants.BlockName.Printer, defaultItemProps) Items.registerBlock(new Raid(defaultProps), Constants.BlockName.Raid, defaultItemProps) Items.registerBlock(new Redstone(defaultProps), Constants.BlockName.Redstone, defaultItemProps) From 52338482ba4599a8a5108c0fdb304fc2448f5745 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 10 Oct 2022 00:55:42 +0200 Subject: [PATCH 111/159] Fix world load never completing with JEI present --- src/main/scala/li/cil/oc/common/init/Items.scala | 7 ++++--- .../cil/oc/integration/jei/ModPluginOpenComputers.scala | 9 +++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 77060caf18..4e469b5732 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -82,7 +82,7 @@ object Items extends ItemAPI { def registerBlock(instance: Block, id: String, itemProps: Properties): Block = { if (!descriptors.contains(id)) { - instance match { + val itemInst = instance match { case simple: SimpleBlock => simple.setUnlocalizedName("oc." + id) simple.setRegistryName(OpenComputers.ID, id) @@ -92,14 +92,15 @@ object Items extends ItemAPI { item.setRegistryName(OpenComputers.ID, id) GameData.register_impl(item) OpenComputers.proxy.registerModel(item, id) - case _ => + item + case _ => null.asInstanceOf[Item] } descriptors += id -> new ItemInfo { override def name: String = id override def block = instance - override def item = item + override def item = itemInst override def createItemStack(size: Int): ItemStack = instance match { case simple: SimpleBlock => simple.createItemStack(size) diff --git a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala index dae2ed75de..7beb45ce78 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala @@ -37,7 +37,6 @@ class ModPluginOpenComputers extends IModPlugin { override def registerCategories(registry: IRecipeCategoryRegistration): Unit = { registry.addRecipeCategories(ManualUsageHandler.ManualUsageRecipeCategory) registry.addRecipeCategories(CallbackDocHandler.CallbackDocRecipeCategory) - } override def registerRecipes(registration: IRecipeRegistration) { @@ -87,13 +86,11 @@ class ModPluginOpenComputers extends IModPlugin { subtypeRegistry.registerSubtypeInterpreter(Items.get(Constants.ItemName.Floppy).item(), new IIngredientSubtypeInterpreter[ItemStack] { override def apply(stack: ItemStack, ctx: UidContext): String = { if (!stack.hasTag) return IIngredientSubtypeInterpreter.NONE - val compound: CompoundNBT = stack.getTag - val data = new CompoundNBT // Separate loot disks from normal floppies - if (compound.contains(Settings.namespace + "lootFactory")) { - data.put(Settings.namespace + "lootFactory", compound.get(Settings.namespace + "lootFactory")) + Option(stack.getTag.get(Settings.namespace + "lootFactory")) match { + case Some(lf) => lf.toString + case None => IIngredientSubtypeInterpreter.NONE } - if (data.isEmpty) IIngredientSubtypeInterpreter.NONE else data.toString } }) } From 37311f27471f43fa6efab0f3dfad7b0b17708e86 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:41:53 +0200 Subject: [PATCH 112/159] Fix hard dependency on CC within Relay --- .../li/cil/oc/common/tileentity/Relay.scala | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index 983d0b9ca6..ac7f67c7c3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -129,14 +129,17 @@ class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) w // ----------------------------------------------------------------------- // - protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { - for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { - val address = s"cc${computer.getID}_${computer.getAttachmentName}" - if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) - computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { - case x: Array[Byte] => new String(x, Charsets.UTF_8) - case x => x - }: _*)) + // Isolated from parent class so automatic callbacks don't depend on optional mods. + protected object RelayCCAdapter { + def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { + for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { + val address = s"cc${computer.getID}_${computer.getAttachmentName}" + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) + computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + case x: Array[Byte] => new String(x, Charsets.UTF_8) + case x => x + }: _*)) + } } } @@ -159,8 +162,8 @@ class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) w override def tryEnqueuePacket(sourceSide: Option[Direction], packet: Packet): Boolean = { if (Mods.ComputerCraft.isModAvailable) { packet.data.headOption match { - case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) - case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) + case Some(answerPort: java.lang.Double) => RelayCCAdapter.queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) + case _ => RelayCCAdapter.queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) } } super.tryEnqueuePacket(sourceSide, packet) From c5f64f51b661cdbe9d2d8963138c1b0ef1b1e24a Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 11 Oct 2022 20:23:02 +0200 Subject: [PATCH 113/159] Fix JEI tab order & rendering --- .../scala/li/cil/oc/integration/jei/CallbackDocHandler.scala | 4 ++-- .../scala/li/cil/oc/integration/jei/ManualUsageHandler.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala index 31fdd35fa7..3a7af620b8 100644 --- a/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/CallbackDocHandler.scala @@ -116,14 +116,14 @@ object CallbackDocHandler { override def draw(recipeWrapper: CallbackDocRecipe, stack: MatrixStack, mouseX: Double, mouseY: Double): Unit = { val minecraft = Minecraft.getInstance for ((text, line) <- recipeWrapper.page.lines.zipWithIndex) { - minecraft.font.drawShadow(stack, text, 4, 4 + line * (minecraft.font.lineHeight + 1), 0x333333, false) + minecraft.font.draw(stack, text, 4, 4 + line * (minecraft.font.lineHeight + 1), 0x333333) } } @Deprecated override def getTitle = "OpenComputers API" - override def getUid = new ResourceLocation(OpenComputers.ID, "api") + override def getUid = new ResourceLocation(OpenComputers.ID, "part_api") } } diff --git a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala index 4088ec50f6..b85b522ac9 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala @@ -62,7 +62,7 @@ object ManualUsageHandler { } override def draw(recipeWrapper: ManualUsageRecipe, stack: MatrixStack, mouseX: Double, mouseY: Double) { - button.renderButton(stack, mouseX.toInt, mouseY.toInt, 1) + button.render(stack, mouseX.toInt, mouseY.toInt, 0) } override def handleClick(recipeWrapper: ManualUsageRecipe, mouseX: Double, mouseY: Double, mouseButton: Int): Boolean = { From 03fdc0f828e69efcf7e8cadb052c73a8e545e1d1 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 14 Oct 2022 21:06:36 +0200 Subject: [PATCH 114/159] Remove ItemBlacklist Only used by JEI, which respects ItemGroups --- .../cil/oc/common/block/Microcontroller.scala | 5 ---- .../cil/oc/common/block/PowerConverter.scala | 15 ----------- .../scala/li/cil/oc/common/block/Print.scala | 7 ----- .../cil/oc/common/block/RobotAfterimage.scala | 8 ------ .../li/cil/oc/common/block/RobotProxy.scala | 9 ------- .../scala/li/cil/oc/common/item/Drone.scala | 4 --- .../jei/ModPluginOpenComputers.scala | 3 --- .../opencomputers/ModOpenComputers.scala | 3 --- .../oc/integration/util/ItemBlacklist.scala | 27 ------------------- 9 files changed, 81 deletions(-) delete mode 100644 src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index 5a54346ce1..465d947cc0 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -10,11 +10,9 @@ import li.cil.oc.common.Tier import li.cil.oc.common.block.property.PropertyRotatable import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.tileentity -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.integration.util.Wrench import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.Rarity import li.cil.oc.util.StackOption._ import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties @@ -23,7 +21,6 @@ import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item // Rarity import net.minecraft.item.ItemStack import net.minecraft.loot.LootContext import net.minecraft.loot.LootParameters @@ -43,8 +40,6 @@ import scala.reflect.ClassTag class Microcontroller(props: Properties) extends RedstoneAware(props) with IForgeBlock with traits.PowerAcceptor with traits.StateAware { - ItemBlacklist.hide(this) - protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = builder.add(PropertyRotatable.Facing) diff --git a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala index 4b50df9253..8ff3f4e6cd 100644 --- a/src/main/scala/li/cil/oc/common/block/PowerConverter.scala +++ b/src/main/scala/li/cil/oc/common/block/PowerConverter.scala @@ -1,26 +1,11 @@ package li.cil.oc.common.block -import java.text.DecimalFormat -import java.util - import li.cil.oc.Settings import li.cil.oc.common.tileentity -import li.cil.oc.integration.Mods -import li.cil.oc.integration.util.ItemBlacklist -import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties -import net.minecraft.client.util.ITooltipFlag -import net.minecraft.item.ItemStack import net.minecraft.world.IBlockReader -import net.minecraft.world.World class PowerConverter(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor { - if (Settings.get.ignorePower) { - ItemBlacklist.hide(this) - } - - // ----------------------------------------------------------------------- // - override def energyThroughput: Double = Settings.get.powerConverterRate override def newBlockEntity(world: IBlockReader) = new tileentity.PowerConverter(tileentity.TileEntityTypes.POWER_CONVERTER) diff --git a/src/main/scala/li/cil/oc/common/block/Print.scala b/src/main/scala/li/cil/oc/common/block/Print.scala index 50c7b26382..95152f23a1 100644 --- a/src/main/scala/li/cil/oc/common/block/Print.scala +++ b/src/main/scala/li/cil/oc/common/block/Print.scala @@ -7,16 +7,11 @@ import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.tileentity -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.loot.LootFunctions -import li.cil.oc.util.InventoryUtils import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState -import net.minecraft.block.material.Material import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.Entity -import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -41,8 +36,6 @@ import scala.collection.convert.ImplicitConversionsToJava._ import scala.reflect.ClassTag class Print(props: Properties) extends RedstoneAware(props) with IForgeBlock { - ItemBlacklist.hide(this) - @Deprecated override def propagatesSkylightDown(state: BlockState, world: IBlockReader, pos: BlockPos) = false diff --git a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala index b9a94e564e..4cf5fbdf8b 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotAfterimage.scala @@ -5,10 +5,7 @@ import java.util.Random import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.api -import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity -import li.cil.oc.integration.util.ItemBlacklist -import li.cil.oc.util.Rarity import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.Blocks import net.minecraft.block.BlockState @@ -23,16 +20,11 @@ import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.shapes.ISelectionContext import net.minecraft.util.math.shapes.VoxelShape -import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.world.IBlockReader import net.minecraft.world.World import net.minecraft.world.server.ServerWorld class RobotAfterimage(props: Properties) extends SimpleBlock(props) { - ItemBlacklist.hide(this) - - // ----------------------------------------------------------------------- // - override def getPickBlock(state: BlockState, target: RayTraceResult, world: IBlockReader, pos: BlockPos, player: PlayerEntity): ItemStack = findMovingRobot(world, pos) match { case Some(robot) => robot.info.createItemStack() diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index 2e2ea73ea4..af4c8bb981 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -3,34 +3,28 @@ package li.cil.oc.common.block import java.util import li.cil.oc.Constants -import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.client.KeyBindings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.tileentity -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.PacketSender import li.cil.oc.server.agent import li.cil.oc.server.loot.LootFunctions import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils -import li.cil.oc.util.Rarity import li.cil.oc.util.Tooltip import net.minecraft.block.AbstractBlock.Properties import net.minecraft.block.BlockState import net.minecraft.client.util.ITooltipFlag -import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.fluid.FluidState -import net.minecraft.item import net.minecraft.item.ItemStack import net.minecraft.loot.LootContext import net.minecraft.loot.LootParameters -import net.minecraft.util.ActionResultType import net.minecraft.util.Direction import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos @@ -38,7 +32,6 @@ import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.shapes.ISelectionContext import net.minecraft.util.math.shapes.VoxelShape import net.minecraft.util.math.shapes.VoxelShapes -import net.minecraft.util.math.vector.Vector3d import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader @@ -47,8 +40,6 @@ import net.minecraft.world.World import scala.collection.convert.ImplicitConversionsToScala._ class RobotProxy(props: Properties) extends RedstoneAware(props) with traits.StateAware { - ItemBlacklist.hide(this) - val shape = VoxelShapes.box(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) override val getDescriptionId = "robot" diff --git a/src/main/scala/li/cil/oc/common/item/Drone.scala b/src/main/scala/li/cil/oc/common/item/Drone.scala index 504726b7c2..13a19402d1 100644 --- a/src/main/scala/li/cil/oc/common/item/Drone.scala +++ b/src/main/scala/li/cil/oc/common/item/Drone.scala @@ -6,10 +6,8 @@ import li.cil.oc.Constants import li.cil.oc.Settings import li.cil.oc.client.KeyBindings import li.cil.oc.client.renderer.block.DroneModel -import li.cil.oc.common.init.Items import li.cil.oc.common.item.data.DroneData import li.cil.oc.common.entity -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.agent import li.cil.oc.util.BlockPosition import li.cil.oc.util.Rarity @@ -30,8 +28,6 @@ import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn class Drone(props: Properties) extends Item(props) with IForgeItem with traits.SimpleItem with CustomModel { - ItemBlacklist.hide(this) - @OnlyIn(Dist.CLIENT) override def getModelLocation(stack: ItemStack) = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Drone, "inventory") diff --git a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala index 7beb45ce78..31904a553c 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala @@ -7,7 +7,6 @@ import li.cil.oc.api.Items import li.cil.oc.client.gui.Relay import li.cil.oc.integration.jei.CallbackDocHandler.CallbackDocRecipe import li.cil.oc.integration.jei.ManualUsageHandler.ManualUsageRecipe -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.integration.util.ItemSearch import li.cil.oc.util.StackOption import mezz.jei.api.IModPlugin @@ -62,8 +61,6 @@ class ModPluginOpenComputers extends IModPlugin { } stackUnderMouse = (container, mouseX, mouseY) => StackOption(jeiRuntime.getIngredientListOverlay.getIngredientUnderMouse(VanillaTypes.ITEM)) - jeiRuntime.getIngredientManager.removeIngredientsAtRuntime(VanillaTypes.ITEM, ItemBlacklist.hiddenItems.map(getter => getter()).asJavaCollection) - ModJEI.runtime = Option(jeiRuntime) ModJEI.ingredientRegistry = Option(jeiRuntime.getIngredientManager) } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index c0c777ae77..1bea7cc407 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -34,7 +34,6 @@ import li.cil.oc.common.template._ import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods import li.cil.oc.integration.util.BundledRedstone -import li.cil.oc.integration.util.ItemBlacklist import li.cil.oc.server.machine.luac.LuaStateFactory import li.cil.oc.server.machine.luac.NativeLua53Architecture import li.cil.oc.server.network.Waypoints @@ -52,8 +51,6 @@ object ModOpenComputers extends ModProxy { override def getMod = Mods.OpenComputers override def initialize() { - ItemBlacklist.apply() - DroneTemplate.register() MicrocontrollerTemplate.register() NavigationUpgradeTemplate.register() diff --git a/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala b/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala deleted file mode 100644 index db1653bc24..0000000000 --- a/src/main/scala/li/cil/oc/integration/util/ItemBlacklist.scala +++ /dev/null @@ -1,27 +0,0 @@ -package li.cil.oc.integration.util - -import li.cil.oc.common.item.traits.SimpleItem -import net.minecraft.block.Block -import net.minecraft.item.ItemStack - -import scala.collection.mutable - -object ItemBlacklist { - // Lazily evaluated stacks to avoid creating stacks with unregistered items/blocks. - val hiddenItems = mutable.Set.empty[() => ItemStack] - - // List of consumers for item stacks (blacklisting for NEI and JEI). - val consumers = mutable.Set.empty[ItemStack => Unit] - - def hide(block: Block): Unit = hiddenItems += (() => new ItemStack(block)) - - def hide(item: SimpleItem): Unit = hiddenItems += (() => item.createItemStack()) - - def apply(): Unit = { - for (consumer <- consumers) { - for (stack <- hiddenItems) { - consumer(stack()) - } - } - } -} From 2f1182b209f88b628882e6117714b27eadfd00e2 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 17 Oct 2022 23:03:15 +0200 Subject: [PATCH 115/159] Reimplement loot disk cycling --- .../recipes/lootdisks/cycling.json | 3 + .../common/recipe/LootDiskCyclingRecipe.scala | 68 +++++++++++++++++++ .../oc/common/recipe/RecipeSerializers.java | 35 ++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/main/resources/data/opencomputers/recipes/lootdisks/cycling.json create mode 100644 src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala create mode 100644 src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java diff --git a/src/main/resources/data/opencomputers/recipes/lootdisks/cycling.json b/src/main/resources/data/opencomputers/recipes/lootdisks/cycling.json new file mode 100644 index 0000000000..61e59a9a0b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/lootdisks/cycling.json @@ -0,0 +1,3 @@ +{ + "type": "opencomputers:crafting_lootdisk_cycling" +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala new file mode 100644 index 0000000000..0116ac06a0 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala @@ -0,0 +1,68 @@ +package li.cil.oc.common.recipe + +import li.cil.oc.Constants +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.common.Loot +import li.cil.oc.integration.util.Wrench +import li.cil.oc.util.StackOption +import net.minecraft.inventory.CraftingInventory +import net.minecraft.item.ItemStack +import net.minecraft.item.crafting.Ingredient +import net.minecraft.item.crafting.ICraftingRecipe +import net.minecraft.util.NonNullList +import net.minecraft.util.ResourceLocation +import net.minecraft.world.World + +import scala.collection.JavaConverters +import scala.collection.immutable + +class LootDiskCyclingRecipe(val getId: ResourceLocation) extends ICraftingRecipe { + val ingredients = NonNullList.create[Ingredient] + ingredients.add(Ingredient.of(Loot.disksForCycling.toArray: _*)) + ingredients.add(Ingredient.of(api.Items.get(Constants.ItemName.Wrench).createItemStack(1))) + + override def matches(crafting: CraftingInventory, world: World): Boolean = { + val stacks = collectStacks(crafting).toArray + stacks.length == 2 && stacks.exists(Loot.isLootDisk) && stacks.exists(Wrench.isWrench) + } + + override def assemble(crafting: CraftingInventory): ItemStack = { + val lootDiskStacks = Loot.disksForCycling + collectStacks(crafting).find(Loot.isLootDisk) match { + case Some(lootDisk) if lootDiskStacks.nonEmpty => + val lootFactoryName = getLootFactoryName(lootDisk) + val oldIndex = lootDiskStacks.indexWhere(s => getLootFactoryName(s) == lootFactoryName) + val newIndex = (oldIndex + 1) % lootDiskStacks.length + lootDiskStacks(newIndex).copy() + case _ => ItemStack.EMPTY + } + } + + def getLootFactoryName(stack: ItemStack): String = stack.getTag.getString(Settings.namespace + "lootFactory") + + def collectStacks(crafting: CraftingInventory): immutable.IndexedSeq[ItemStack] = (0 until crafting.getContainerSize).flatMap(i => StackOption(crafting.getItem(i))) + + override def canCraftInDimensions(width: Int, height: Int): Boolean = width * height >= 2 + + override def getResultItem = Loot.disksForCycling.headOption match { + case Some(lootDisk) => lootDisk + case _ => ItemStack.EMPTY + } + + override def getRemainingItems(crafting: CraftingInventory): NonNullList[ItemStack] = { + val result = NonNullList.withSize[ItemStack](crafting.getContainerSize, ItemStack.EMPTY) + for (slot <- 0 until crafting.getContainerSize) { + val stack = crafting.getItem(slot) + if (Wrench.isWrench(stack)) { + result.set(slot, stack.copy()) + stack.setCount(0) + } + } + result + } + + override def getIngredients = ingredients + + override def getSerializer = RecipeSerializers.CRAFTING_LOOTDISK_CYCLING +} diff --git a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java new file mode 100644 index 0000000000..a8604fb722 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java @@ -0,0 +1,35 @@ +package li.cil.oc.common.recipe; + +import li.cil.oc.OpenComputers; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.SpecialRecipeSerializer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.ObjectHolder; + +@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) +@ObjectHolder("opencomputers") +public class RecipeSerializers { + public static final IRecipeSerializer CRAFTING_LOOTDISK_CYCLING = null; + + @SubscribeEvent + public static void registerSerializers(RegistryEvent.Register> e) { + register(e.getRegistry(), "crafting_lootdisk_cycling", new SpecialRecipeSerializer<>(LootDiskCyclingRecipe::new)); + } + + private static > & IRecipeSerializer> + void register(IForgeRegistry> registry, String name, S serializer) { + + serializer.setRegistryName(new ResourceLocation(OpenComputers.ID(), name)); + registry.register(serializer); + } + + private RecipeSerializers() { + throw new Error(); + } +} From 671461aaf7452c66f588db9a21011a7ce9704295 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 18 Oct 2022 00:18:37 +0200 Subject: [PATCH 116/159] Reimplement (de)colorization recipes --- .../opencomputers/recipes/colorize/cable.json | 4 + .../recipes/colorize/hoverboots.json | 4 + .../recipes/decolorize/cable.json | 4 + .../recipes/decolorize/hoverboots.json | 4 + .../cil/oc/common/recipe/ColorizeRecipe.scala | 85 +++++++++++++++++++ .../oc/common/recipe/DecolorizeRecipe.scala | 49 +++++++++++ .../common/recipe/ItemSpecialSerializer.java | 47 ++++++++++ .../oc/common/recipe/RecipeSerializers.java | 4 + src/main/scala/li/cil/oc/util/Color.scala | 2 +- 9 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/data/opencomputers/recipes/colorize/cable.json create mode 100644 src/main/resources/data/opencomputers/recipes/colorize/hoverboots.json create mode 100644 src/main/resources/data/opencomputers/recipes/decolorize/cable.json create mode 100644 src/main/resources/data/opencomputers/recipes/decolorize/hoverboots.json create mode 100644 src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala create mode 100644 src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala create mode 100644 src/main/scala/li/cil/oc/common/recipe/ItemSpecialSerializer.java diff --git a/src/main/resources/data/opencomputers/recipes/colorize/cable.json b/src/main/resources/data/opencomputers/recipes/colorize/cable.json new file mode 100644 index 0000000000..ea91e59f72 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/colorize/cable.json @@ -0,0 +1,4 @@ +{ + "type": "opencomputers:crafting_colorize", + "item": "opencomputers:cable" +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/colorize/hoverboots.json b/src/main/resources/data/opencomputers/recipes/colorize/hoverboots.json new file mode 100644 index 0000000000..973455979f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/colorize/hoverboots.json @@ -0,0 +1,4 @@ +{ + "type": "opencomputers:crafting_colorize", + "item": "opencomputers:hoverboots" +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/decolorize/cable.json b/src/main/resources/data/opencomputers/recipes/decolorize/cable.json new file mode 100644 index 0000000000..61a33288e6 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/decolorize/cable.json @@ -0,0 +1,4 @@ +{ + "type": "opencomputers:crafting_decolorize", + "item": "opencomputers:cable" +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/decolorize/hoverboots.json b/src/main/resources/data/opencomputers/recipes/decolorize/hoverboots.json new file mode 100644 index 0000000000..ba63396e0b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/decolorize/hoverboots.json @@ -0,0 +1,4 @@ +{ + "type": "opencomputers:crafting_decolorize", + "item": "opencomputers:hoverboots" +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala new file mode 100644 index 0000000000..b5c1fc05df --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala @@ -0,0 +1,85 @@ +package li.cil.oc.common.recipe + +import li.cil.oc.util.Color +import li.cil.oc.util.ItemColorizer +import li.cil.oc.util.StackOption +import net.minecraft.inventory.CraftingInventory +import net.minecraft.item.crafting.SpecialRecipe +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.util.IItemProvider +import net.minecraft.util.ResourceLocation +import net.minecraft.world.World + +/** + * @author asie, Vexatos + */ +class ColorizeRecipe(id: ResourceLocation, target: IItemProvider) extends SpecialRecipe(id) { + val targetItem: Item = target.asItem() + + override def matches(crafting: CraftingInventory, world: World): Boolean = { + val stacks = (0 until crafting.getContainerSize).flatMap(i => StackOption(crafting.getItem(i))) + val targets = stacks.filter(stack => stack.getItem == targetItem) + val other = stacks.filterNot(targets.contains(_)) + targets.size == 1 && other.nonEmpty && other.forall(Color.isDye) + } + + override def assemble(crafting: CraftingInventory): ItemStack = { + var targetStack: ItemStack = ItemStack.EMPTY + val color = Array[Int](0, 0, 0) + var colorCount = 0 + var maximum = 0 + + (0 until crafting.getContainerSize).flatMap(i => StackOption(crafting.getItem(i))).foreach { stack => + if (stack.getItem == targetItem) { + targetStack = stack.copy() + targetStack.setCount(1) + } else { + val dye = Color.findDye(stack) + if (dye.isEmpty) + return ItemStack.EMPTY + + val itemColor = Color.byTag(dye.get).getTextureDiffuseColors + val red = (itemColor(0) * 255.0F).toInt + val green = (itemColor(1) * 255.0F).toInt + val blue = (itemColor(2) * 255.0F).toInt + maximum += Math.max(red, Math.max(green, blue)) + color(0) += red + color(1) += green + color(2) += blue + colorCount = colorCount + 1 + } + } + + if (targetStack.isEmpty) return ItemStack.EMPTY + + if (targetItem == targetStack.getItem) { + if (ItemColorizer.hasColor(targetStack)) { + val itemColor = ItemColorizer.getColor(targetStack) + val red = (itemColor >> 16 & 255).toFloat / 255.0F + val green = (itemColor >> 8 & 255).toFloat / 255.0F + val blue = (itemColor & 255).toFloat / 255.0F + maximum = (maximum.toFloat + Math.max(red, Math.max(green, blue)) * 255.0F).toInt + color(0) = (color(0).toFloat + red * 255.0F).toInt + color(1) = (color(1).toFloat + green * 255.0F).toInt + color(2) = (color(2).toFloat + blue * 255.0F).toInt + colorCount = colorCount + 1 + } + } + + var red = color(0) / colorCount + var green = color(1) / colorCount + var blue = color(2) / colorCount + val max = maximum.toFloat / colorCount.toFloat + val div = Math.max(red, Math.max(green, blue)).toFloat + red = (red.toFloat * max / div).toInt + green = (green.toFloat * max / div).toInt + blue = (blue.toFloat * max / div).toInt + ItemColorizer.setColor(targetStack, (red << 16) | (green << 8) | blue) + targetStack + } + + override def canCraftInDimensions(width: Int, height: Int): Boolean = width * height >= 2 + + override def getSerializer = RecipeSerializers.CRAFTING_COLORIZE +} diff --git a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala new file mode 100644 index 0000000000..0486b98b49 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala @@ -0,0 +1,49 @@ +package li.cil.oc.common.recipe + +import li.cil.oc.util.ItemColorizer +import li.cil.oc.util.StackOption +import net.minecraft.block.Block +import net.minecraft.inventory.CraftingInventory +import net.minecraft.item.crafting.SpecialRecipe +import net.minecraft.item.Item +import net.minecraft.item.Items +import net.minecraft.item.ItemStack +import net.minecraft.util.IItemProvider +import net.minecraft.util.ResourceLocation +import net.minecraft.world.World + +/** + * @author Vexatos + */ +class DecolorizeRecipe(id: ResourceLocation, target: IItemProvider) extends SpecialRecipe(id) { + val targetItem: Item = target.asItem() + + override def matches(crafting: CraftingInventory, world: World): Boolean = { + val stacks = (0 until crafting.getContainerSize).flatMap(i => StackOption(crafting.getItem(i))) + val targets = stacks.filter(stack => stack.getItem == targetItem) + val other = stacks.filterNot(targets.contains) + targets.size == 1 && other.size == 1 && other.forall(_.getItem == Items.WATER_BUCKET) + } + + override def assemble(crafting: CraftingInventory): ItemStack = { + var targetStack: ItemStack = ItemStack.EMPTY + + (0 until crafting.getContainerSize).flatMap(i => StackOption(crafting.getItem(i))).foreach { stack => + if (stack.getItem == targetItem) { + targetStack = stack.copy() + targetStack.setCount(1) + } else if (stack.getItem != Items.WATER_BUCKET) { + return ItemStack.EMPTY + } + } + + if (targetStack.isEmpty) return ItemStack.EMPTY + + ItemColorizer.removeColor(targetStack) + targetStack + } + + override def canCraftInDimensions(width: Int, height: Int): Boolean = width * height >= 2 + + override def getSerializer = RecipeSerializers.CRAFTING_DECOLORIZE +} diff --git a/src/main/scala/li/cil/oc/common/recipe/ItemSpecialSerializer.java b/src/main/scala/li/cil/oc/common/recipe/ItemSpecialSerializer.java new file mode 100644 index 0000000000..ec4e1335b6 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ItemSpecialSerializer.java @@ -0,0 +1,47 @@ +package li.cil.oc.common.recipe; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; +import net.minecraft.item.Item; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.IItemProvider; +import net.minecraft.util.JSONUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistryEntry; + +public class ItemSpecialSerializer> extends ForgeRegistryEntry> + implements IRecipeSerializer { + + private BiFunction ctor; + private Function getter; + + public ItemSpecialSerializer(BiFunction ctor, Function getter) { + this.ctor = ctor; + this.getter = getter; + } + + @Override + public T fromJson(ResourceLocation recipeId, JsonObject json) { + ResourceLocation loc = new ResourceLocation(JSONUtils.getAsString(json, "item")); + if (!ForgeRegistries.ITEMS.containsKey(loc)) { + throw new JsonSyntaxException("Unknown item '" + loc + "'"); + } + return ctor.apply(recipeId, ForgeRegistries.ITEMS.getValue(loc)); + } + + @Override + public T fromNetwork(ResourceLocation recipeId, PacketBuffer buff) { + return ctor.apply(recipeId, buff.readRegistryIdUnsafe(ForgeRegistries.ITEMS)); + } + + @Override + public void toNetwork(PacketBuffer buff, T recipe) { + buff.writeRegistryIdUnsafe(ForgeRegistries.ITEMS, getter.apply(recipe)); + } +} diff --git a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java index a8604fb722..ae2e0d7cb5 100644 --- a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java +++ b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java @@ -16,10 +16,14 @@ @ObjectHolder("opencomputers") public class RecipeSerializers { public static final IRecipeSerializer CRAFTING_LOOTDISK_CYCLING = null; + public static final IRecipeSerializer CRAFTING_COLORIZE = null; + public static final IRecipeSerializer CRAFTING_DECOLORIZE = null; @SubscribeEvent public static void registerSerializers(RegistryEvent.Register> e) { register(e.getRegistry(), "crafting_lootdisk_cycling", new SpecialRecipeSerializer<>(LootDiskCyclingRecipe::new)); + register(e.getRegistry(), "crafting_colorize", new ItemSpecialSerializer<>(ColorizeRecipe::new, ColorizeRecipe::targetItem)); + register(e.getRegistry(), "crafting_decolorize", new ItemSpecialSerializer<>(DecolorizeRecipe::new, DecolorizeRecipe::targetItem)); } private static > & IRecipeSerializer> diff --git a/src/main/scala/li/cil/oc/util/Color.scala b/src/main/scala/li/cil/oc/util/Color.scala index 6cc9a47d19..848807617a 100644 --- a/src/main/scala/li/cil/oc/util/Color.scala +++ b/src/main/scala/li/cil/oc/util/Color.scala @@ -66,7 +66,7 @@ object Color { @Deprecated val byId = dyes.map(name => (byOreName(name).getId, name)).toMap - private val byTag = DyeColor.values.map(col => (col.getTag.getName, col)).toMap + val byTag = DyeColor.values.map(col => (col.getTag.getName, col)).toMap val byTier = Array(DyeColor.LIGHT_GRAY, DyeColor.YELLOW, DyeColor.CYAN, DyeColor.MAGENTA) From 7d8e3e4e0b3f53b6de442f98dafa332ab245b1ec Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:25:55 +0200 Subject: [PATCH 117/159] Fix decolorized items not stacking anymore --- src/main/scala/li/cil/oc/util/ItemColorizer.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/util/ItemColorizer.scala b/src/main/scala/li/cil/oc/util/ItemColorizer.scala index 6fae0051d4..40525013c9 100644 --- a/src/main/scala/li/cil/oc/util/ItemColorizer.scala +++ b/src/main/scala/li/cil/oc/util/ItemColorizer.scala @@ -18,8 +18,11 @@ object ItemColorizer { def getColor(stack: ItemStack): Int = { val tag = stack.getTag if (tag != null) { - val displayTag = tag.getCompound("display") - if (displayTag == null) -1 else if (displayTag.contains("color")) displayTag.getInt("color") else -1 + if (tag.contains("display")) { + val displayTag = tag.getCompound("display") + if (displayTag.contains("color")) displayTag.getInt("color") else -1 + } + else -1 } else -1 } @@ -29,6 +32,8 @@ object ItemColorizer { if (tag != null) { val displayTag = tag.getCompound("display") if (displayTag.contains("color")) displayTag.remove("color") + if (displayTag.isEmpty) tag.remove("display") + if (tag.isEmpty) stack.setTag(null) } } From 4e31248dc98c33d09961e1d73c30bc91a1aa5c72 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 18 Oct 2022 00:52:06 +0200 Subject: [PATCH 118/159] Fix hover boot rendering offset --- .../renderer/item/HoverBootRenderer.scala | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala index b52b40e7a1..7ee1b117f6 100644 --- a/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/item/HoverBootRenderer.scala @@ -1,18 +1,15 @@ package li.cil.oc.client.renderer.item import com.mojang.blaze3d.matrix.MatrixStack -import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.IVertexBuilder import li.cil.oc.Settings -import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.LightTexture import net.minecraft.client.renderer.model.Model import net.minecraft.client.renderer.model.ModelRenderer import net.minecraft.client.renderer.entity.model.BipedModel -import net.minecraft.client.renderer.RenderHelper -import net.minecraft.entity.Entity +import net.minecraft.client.renderer.texture.OverlayTexture import net.minecraft.entity.LivingEntity import net.minecraft.util.ResourceLocation -import org.lwjgl.opengl.GL11 object HoverBootRenderer extends BipedModel[LivingEntity](0.5f) { val texture = new ResourceLocation(Settings.resourceDomain, "textures/model/drone.png") @@ -45,8 +42,8 @@ object HoverBootRenderer extends BipedModel[LivingEntity](0.5f) { texWidth = 64 texHeight = 32 - bootRight.y = 10.1f / 16 - bootLeft.y = 10.11f / 16f + bootRight.y = 10.1f + bootLeft.y = 10.11f droneBody.texOffs(0, 23).addBox(-3, 1, -3, 6, 1, 6).yRot = math.toRadians(45).toFloat // top droneBody.texOffs(0, 1).addBox(-1, 0, -1, 2, 1, 2).yRot = math.toRadians(45).toFloat // middle @@ -90,22 +87,11 @@ object HoverBootRenderer extends BipedModel[LivingEntity](0.5f) { class LightModelRenderer(modelBase: Model) extends ModelRenderer(modelBase) { override def render(stack: MatrixStack, builder: IVertexBuilder, light: Int, overlay: Int, r: Float, g: Float, b: Float, a: Float): Unit = { - RenderState.pushAttrib() - RenderSystem.disableLighting() - RenderState.disableEntityLighting() - RenderSystem.depthFunc(GL11.GL_LEQUAL) - RenderState.makeItBlend() - RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) val rm = ((lightColor >>> 16) & 0xFF) / 255f val gm = ((lightColor >>> 8) & 0xFF) / 255f val bm = ((lightColor >>> 0) & 0xFF) / 255f - super.render(stack, builder, light, overlay, r * rm, g * gm, b * bm, a) - - RenderState.disableBlend() - RenderSystem.enableLighting() - RenderState.enableEntityLighting() - RenderState.popAttrib() + super.render(stack, builder, LightTexture.pack(15, 15), OverlayTexture.NO_OVERLAY, r * rm, g * gm, b * bm, a) } } From a10d8c731d62d03b2f08f285c18b6cc03a12c641 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 19 Oct 2022 00:35:10 +0200 Subject: [PATCH 119/159] Reimplement generic extended crafting recipes --- .../forge/tags/items/beacon_base_blocks.json | 10 + .../data/opencomputers/recipes/apu1.json | 2 +- .../data/opencomputers/recipes/apu2.json | 2 +- .../recipes/chamelium/coloring/black.json | 15 ++ .../recipes/chamelium/coloring/blue.json | 15 ++ .../recipes/chamelium/coloring/brown.json | 15 ++ .../recipes/chamelium/coloring/cyan.json | 15 ++ .../recipes/chamelium/coloring/gray.json | 15 ++ .../recipes/chamelium/coloring/green.json | 15 ++ .../chamelium/coloring/light_blue.json | 15 ++ .../chamelium/coloring/light_gray.json | 15 ++ .../recipes/chamelium/coloring/lime.json | 15 ++ .../recipes/chamelium/coloring/magenta.json | 15 ++ .../recipes/chamelium/coloring/orange.json | 15 ++ .../recipes/chamelium/coloring/pink.json | 15 ++ .../recipes/chamelium/coloring/purple.json | 15 ++ .../recipes/chamelium/coloring/red.json | 15 ++ .../recipes/chamelium/coloring/white.json | 15 ++ .../recipes/chamelium/coloring/yellow.json | 15 ++ .../splitting.json} | 4 +- .../data/opencomputers/recipes/cpu1.json | 2 +- .../data/opencomputers/recipes/cpu2.json | 2 +- .../data/opencomputers/recipes/cpu3.json | 2 +- .../opencomputers/recipes/eeprom/copying.json | 15 ++ .../recipes/eeprom/formatting.json | 11 + .../recipes/floppy/coloring/black.json | 15 ++ .../recipes/floppy/coloring/blue.json | 15 ++ .../recipes/floppy/coloring/brown.json | 15 ++ .../recipes/floppy/coloring/cyan.json | 15 ++ .../recipes/floppy/coloring/gray.json | 15 ++ .../recipes/floppy/coloring/green.json | 15 ++ .../recipes/floppy/coloring/light_blue.json | 15 ++ .../recipes/floppy/coloring/light_gray.json | 15 ++ .../recipes/floppy/coloring/lime.json | 15 ++ .../recipes/floppy/coloring/magenta.json | 15 ++ .../recipes/floppy/coloring/orange.json | 15 ++ .../recipes/floppy/coloring/pink.json | 15 ++ .../recipes/floppy/coloring/purple.json | 15 ++ .../recipes/floppy/coloring/red.json | 15 ++ .../recipes/floppy/coloring/white.json | 15 ++ .../recipes/floppy/coloring/yellow.json | 15 ++ .../recipes/floppy/formatting.json | 11 + .../recipes/hdd/formatting1.json | 11 + .../recipes/hdd/formatting2.json | 11 + .../recipes/hdd/formatting3.json | 11 + .../opencomputers/recipes/linkedcard.json | 5 +- .../recipes/navigationupgrade.json | 2 +- .../opencomputers/recipes/print/beacon.json | 14 + .../recipes/print/glowstone.json | 14 + .../recipes/print/glowstone_dust.json | 14 + .../recipes/recrafting/drone.json | 14 + .../recipes/recrafting/linkedcard.json | 15 ++ .../recipes/recrafting/microcontroller.json | 14 + .../recipes/recrafting/navigationupgrade.json | 14 + .../recipes/recrafting/robot.json | 14 + .../recipes/recrafting/tablet.json | 14 + .../cil/oc/common/recipe/ExtendedRecipe.scala | 252 ++++++++++++++++++ .../common/recipe/ExtendedShapedRecipe.java | 101 +++++++ .../recipe/ExtendedShapelessRecipe.java | 90 +++++++ .../oc/common/recipe/RecipeSerializers.java | 4 + 60 files changed, 1145 insertions(+), 10 deletions(-) create mode 100644 src/main/resources/data/forge/tags/items/beacon_base_blocks.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/black.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/blue.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/brown.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/cyan.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/gray.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/green.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_blue.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_gray.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/lime.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/magenta.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/orange.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/pink.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/purple.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/red.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/white.json create mode 100644 src/main/resources/data/opencomputers/recipes/chamelium/coloring/yellow.json rename src/main/resources/data/opencomputers/recipes/{chameliumsplit.json => chamelium/splitting.json} (52%) create mode 100644 src/main/resources/data/opencomputers/recipes/eeprom/copying.json create mode 100644 src/main/resources/data/opencomputers/recipes/eeprom/formatting.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/black.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/blue.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/brown.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/cyan.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/gray.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/green.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/light_blue.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/light_gray.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/lime.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/magenta.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/orange.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/pink.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/purple.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/red.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/white.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/coloring/yellow.json create mode 100644 src/main/resources/data/opencomputers/recipes/floppy/formatting.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd/formatting1.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd/formatting2.json create mode 100644 src/main/resources/data/opencomputers/recipes/hdd/formatting3.json create mode 100644 src/main/resources/data/opencomputers/recipes/print/beacon.json create mode 100644 src/main/resources/data/opencomputers/recipes/print/glowstone.json create mode 100644 src/main/resources/data/opencomputers/recipes/print/glowstone_dust.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/drone.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/linkedcard.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/microcontroller.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/navigationupgrade.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/robot.json create mode 100644 src/main/resources/data/opencomputers/recipes/recrafting/tablet.json create mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala create mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedShapedRecipe.java create mode 100644 src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessRecipe.java diff --git a/src/main/resources/data/forge/tags/items/beacon_base_blocks.json b/src/main/resources/data/forge/tags/items/beacon_base_blocks.json new file mode 100644 index 0000000000..a368aa50b6 --- /dev/null +++ b/src/main/resources/data/forge/tags/items/beacon_base_blocks.json @@ -0,0 +1,10 @@ +{ + "replace": false, + "values": [ + "minecraft:netherite_block", + "minecraft:emerald_block", + "minecraft:diamond_block", + "minecraft:gold_block", + "minecraft:iron_block" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/apu1.json b/src/main/resources/data/opencomputers/recipes/apu1.json index 9019f939c2..46a281d246 100644 --- a/src/main/resources/data/opencomputers/recipes/apu1.json +++ b/src/main/resources/data/opencomputers/recipes/apu1.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "gCg", "PBG", diff --git a/src/main/resources/data/opencomputers/recipes/apu2.json b/src/main/resources/data/opencomputers/recipes/apu2.json index 6d05fbd626..0c8aca229f 100644 --- a/src/main/resources/data/opencomputers/recipes/apu2.json +++ b/src/main/resources/data/opencomputers/recipes/apu2.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "dCd", "PBG", diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/black.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/black.json new file mode 100644 index 0000000000..97407ee4d2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/black.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/black" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 15}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/blue.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/blue.json new file mode 100644 index 0000000000..80bd6b7d2f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/blue.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/blue" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 11}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/brown.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/brown.json new file mode 100644 index 0000000000..ab3af44cc7 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/brown.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/brown" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 12}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/cyan.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/cyan.json new file mode 100644 index 0000000000..20743e1196 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/cyan.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/cyan" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 9}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/gray.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/gray.json new file mode 100644 index 0000000000..b21b3381f4 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/gray.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/gray" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 7}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/green.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/green.json new file mode 100644 index 0000000000..346eb6f2e9 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/green.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/green" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 13}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_blue.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_blue.json new file mode 100644 index 0000000000..ab76c12891 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_blue.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/light_blue" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 3}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_gray.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_gray.json new file mode 100644 index 0000000000..34773f95e8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/light_gray.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/light_gray" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 8}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/lime.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/lime.json new file mode 100644 index 0000000000..fa53032ac2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/lime.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/lime" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 5}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/magenta.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/magenta.json new file mode 100644 index 0000000000..511d06dd7c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/magenta.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/magenta" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 2}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/orange.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/orange.json new file mode 100644 index 0000000000..ef2f9f1acf --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/orange.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/orange" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 1}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/pink.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/pink.json new file mode 100644 index 0000000000..bc08ba163d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/pink.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/pink" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 6}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/purple.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/purple.json new file mode 100644 index 0000000000..c1e7dc7a1e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/purple.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/purple" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 10}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/red.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/red.json new file mode 100644 index 0000000000..531e3fea2f --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/red.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/red" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 14}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/white.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/white.json new file mode 100644 index 0000000000..c3a17fad8a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/white.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/white" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 0}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chamelium/coloring/yellow.json b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/yellow.json new file mode 100644 index 0000000000..db4d68f66c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/chamelium/coloring/yellow.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:chameliumblock" + }, + { + "tag": "forge:dyes/yellow" + } + ], + "result": { + "item": "opencomputers:chameliumblock", + "nbt": "{\"Damage\": 4}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/chameliumsplit.json b/src/main/resources/data/opencomputers/recipes/chamelium/splitting.json similarity index 52% rename from src/main/resources/data/opencomputers/recipes/chameliumsplit.json rename to src/main/resources/data/opencomputers/recipes/chamelium/splitting.json index 474752cacf..f482d07bf0 100644 --- a/src/main/resources/data/opencomputers/recipes/chameliumsplit.json +++ b/src/main/resources/data/opencomputers/recipes/chamelium/splitting.json @@ -1,8 +1,8 @@ { - "type": "minecraft:crafting_shapeless", + "type": "opencomputers:crafting_shapeless_extended", "ingredients": [ { - "tag": "opencomputers:chameliumblock" + "item": "opencomputers:chameliumblock" } ], "result": { diff --git a/src/main/resources/data/opencomputers/recipes/cpu1.json b/src/main/resources/data/opencomputers/recipes/cpu1.json index 4b7300b1c8..ff8fce0e20 100644 --- a/src/main/resources/data/opencomputers/recipes/cpu1.json +++ b/src/main/resources/data/opencomputers/recipes/cpu1.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "iri", "CUC", diff --git a/src/main/resources/data/opencomputers/recipes/cpu2.json b/src/main/resources/data/opencomputers/recipes/cpu2.json index 08b581bdc9..b829c63c3a 100644 --- a/src/main/resources/data/opencomputers/recipes/cpu2.json +++ b/src/main/resources/data/opencomputers/recipes/cpu2.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "grg", "CUC", diff --git a/src/main/resources/data/opencomputers/recipes/cpu3.json b/src/main/resources/data/opencomputers/recipes/cpu3.json index 8293bc2a8d..20c11e9956 100644 --- a/src/main/resources/data/opencomputers/recipes/cpu3.json +++ b/src/main/resources/data/opencomputers/recipes/cpu3.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "drd", "CUC", diff --git a/src/main/resources/data/opencomputers/recipes/eeprom/copying.json b/src/main/resources/data/opencomputers/recipes/eeprom/copying.json new file mode 100644 index 0000000000..18df60cb9c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/eeprom/copying.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:eeprom" + }, + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:eeprom", + "count": 2 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/eeprom/formatting.json b/src/main/resources/data/opencomputers/recipes/eeprom/formatting.json new file mode 100644 index 0000000000..9753bd3e30 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/eeprom/formatting.json @@ -0,0 +1,11 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:eeprom" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/black.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/black.json new file mode 100644 index 0000000000..53ef4be93d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/black.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/black" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 15}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/blue.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/blue.json new file mode 100644 index 0000000000..bc56769cc8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/blue.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/blue" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 11}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/brown.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/brown.json new file mode 100644 index 0000000000..dca6215ef2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/brown.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/brown" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 12}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/cyan.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/cyan.json new file mode 100644 index 0000000000..03fe66029a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/cyan.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/cyan" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 9}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/gray.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/gray.json new file mode 100644 index 0000000000..80749c1616 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/gray.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/gray" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 7}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/green.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/green.json new file mode 100644 index 0000000000..c1923cde86 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/green.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/green" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 13}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_blue.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_blue.json new file mode 100644 index 0000000000..d70ede156b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_blue.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/light_blue" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 3}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_gray.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_gray.json new file mode 100644 index 0000000000..c754fbc926 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/light_gray.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/light_gray" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 8}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/lime.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/lime.json new file mode 100644 index 0000000000..cc6234e310 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/lime.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/lime" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 5}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/magenta.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/magenta.json new file mode 100644 index 0000000000..594f55833a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/magenta.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/magenta" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 2}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/orange.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/orange.json new file mode 100644 index 0000000000..1f14c00dd8 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/orange.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/orange" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 1}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/pink.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/pink.json new file mode 100644 index 0000000000..b019825ebc --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/pink.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/pink" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 6}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/purple.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/purple.json new file mode 100644 index 0000000000..a6940ee66d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/purple.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/purple" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 10}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/red.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/red.json new file mode 100644 index 0000000000..23680d120b --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/red.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/red" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 14}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/white.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/white.json new file mode 100644 index 0000000000..4040d59402 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/white.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/white" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 0}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/coloring/yellow.json b/src/main/resources/data/opencomputers/recipes/floppy/coloring/yellow.json new file mode 100644 index 0000000000..5472ebd757 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/coloring/yellow.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + }, + { + "tag": "forge:dyes/yellow" + } + ], + "result": { + "item": "opencomputers:floppy", + "nbt": "{\"oc:color\": 4}" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/floppy/formatting.json b/src/main/resources/data/opencomputers/recipes/floppy/formatting.json new file mode 100644 index 0000000000..0358f3a9d2 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/floppy/formatting.json @@ -0,0 +1,11 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:floppy" + } + ], + "result": { + "item": "opencomputers:floppy" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd/formatting1.json b/src/main/resources/data/opencomputers/recipes/hdd/formatting1.json new file mode 100644 index 0000000000..42a71dbf08 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd/formatting1.json @@ -0,0 +1,11 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:hdd1" + } + ], + "result": { + "item": "opencomputers:hdd1" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd/formatting2.json b/src/main/resources/data/opencomputers/recipes/hdd/formatting2.json new file mode 100644 index 0000000000..647faaecfb --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd/formatting2.json @@ -0,0 +1,11 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:hdd2" + } + ], + "result": { + "item": "opencomputers:hdd2" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/hdd/formatting3.json b/src/main/resources/data/opencomputers/recipes/hdd/formatting3.json new file mode 100644 index 0000000000..6907530501 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/hdd/formatting3.json @@ -0,0 +1,11 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:hdd3" + } + ], + "result": { + "item": "opencomputers:hdd3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/linkedcard.json b/src/main/resources/data/opencomputers/recipes/linkedcard.json index cc4b151f9d..4726421fb8 100644 --- a/src/main/resources/data/opencomputers/recipes/linkedcard.json +++ b/src/main/resources/data/opencomputers/recipes/linkedcard.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "e e", "LIL", @@ -20,6 +20,7 @@ } }, "result": { - "item": "opencomputers:linkedcard" + "item": "opencomputers:linkedcard", + "count": 2 } } \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/navigationupgrade.json b/src/main/resources/data/opencomputers/recipes/navigationupgrade.json index e9bc95e141..4281e79c26 100644 --- a/src/main/resources/data/opencomputers/recipes/navigationupgrade.json +++ b/src/main/resources/data/opencomputers/recipes/navigationupgrade.json @@ -1,5 +1,5 @@ { - "type": "minecraft:crafting_shaped", + "type": "opencomputers:crafting_shaped_extended", "pattern": [ "gcg", "CmC", diff --git a/src/main/resources/data/opencomputers/recipes/print/beacon.json b/src/main/resources/data/opencomputers/recipes/print/beacon.json new file mode 100644 index 0000000000..e9ea88d3da --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/print/beacon.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:print" + }, + { + "tag": "forge:beacon_base_blocks" + } + ], + "result": { + "item": "opencomputers:print" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/print/glowstone.json b/src/main/resources/data/opencomputers/recipes/print/glowstone.json new file mode 100644 index 0000000000..0e3d021bf0 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/print/glowstone.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:print" + }, + { + "item": "minecraft:glowstone" + } + ], + "result": { + "item": "opencomputers:print" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/print/glowstone_dust.json b/src/main/resources/data/opencomputers/recipes/print/glowstone_dust.json new file mode 100644 index 0000000000..70668225ed --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/print/glowstone_dust.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:print" + }, + { + "tag": "forge:dusts/glowstone" + } + ], + "result": { + "item": "opencomputers:print" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/drone.json b/src/main/resources/data/opencomputers/recipes/recrafting/drone.json new file mode 100644 index 0000000000..a604c4e431 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/drone.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:drone" + }, + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:drone" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/linkedcard.json b/src/main/resources/data/opencomputers/recipes/recrafting/linkedcard.json new file mode 100644 index 0000000000..b30816050d --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/linkedcard.json @@ -0,0 +1,15 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:linkedcard" + }, + { + "item": "opencomputers:linkedcard" + } + ], + "result": { + "item": "opencomputers:linkedcard", + "count": 2 + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/microcontroller.json b/src/main/resources/data/opencomputers/recipes/recrafting/microcontroller.json new file mode 100644 index 0000000000..bd0127450a --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/microcontroller.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:microcontroller" + }, + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:microcontroller" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/navigationupgrade.json b/src/main/resources/data/opencomputers/recipes/recrafting/navigationupgrade.json new file mode 100644 index 0000000000..d7cedbd10e --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/navigationupgrade.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:navigationupgrade" + }, + { + "item": "minecraft:filled_map" + } + ], + "result": { + "item": "opencomputers:navigationupgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/robot.json b/src/main/resources/data/opencomputers/recipes/recrafting/robot.json new file mode 100644 index 0000000000..57d4928905 --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/robot.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:robot" + }, + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:robot" + } +} \ No newline at end of file diff --git a/src/main/resources/data/opencomputers/recipes/recrafting/tablet.json b/src/main/resources/data/opencomputers/recipes/recrafting/tablet.json new file mode 100644 index 0000000000..e871988d2c --- /dev/null +++ b/src/main/resources/data/opencomputers/recipes/recrafting/tablet.json @@ -0,0 +1,14 @@ +{ + "type": "opencomputers:crafting_shapeless_extended", + "ingredients": [ + { + "item": "opencomputers:tablet" + }, + { + "item": "opencomputers:eeprom" + } + ], + "result": { + "item": "opencomputers:tablet" + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala new file mode 100644 index 0000000000..24c3a4d52c --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala @@ -0,0 +1,252 @@ +package li.cil.oc.common.recipe + +import java.util.UUID + +import li.cil.oc.Constants +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.detail.ItemInfo +import li.cil.oc.common.item.data.DroneData +import li.cil.oc.common.item.data.MicrocontrollerData +import li.cil.oc.common.item.data.PrintData +import li.cil.oc.common.item.data.RobotData +import li.cil.oc.common.item.data.TabletData +import li.cil.oc.server.machine.luac.LuaStateFactory +import li.cil.oc.util.Color +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.SideTracker +import net.minecraft.block.Blocks +import net.minecraft.inventory.CraftingInventory +import net.minecraft.item.Items +import net.minecraft.item.ItemStack +import net.minecraft.item.crafting.IRecipe +import net.minecraft.nbt.CompoundNBT +import net.minecraft.tags.ItemTags + +import scala.collection.convert.ImplicitConversionsToScala._ +import scala.util.control.Breaks._ + +object ExtendedRecipe { + private lazy val drone = api.Items.get(Constants.ItemName.Drone) + private lazy val eeprom = api.Items.get(Constants.ItemName.EEPROM) + private lazy val luaBios = api.Items.get(Constants.ItemName.LuaBios) + private lazy val mcu = api.Items.get(Constants.BlockName.Microcontroller) + private lazy val navigationUpgrade = api.Items.get(Constants.ItemName.NavigationUpgrade) + private lazy val linkedCard = api.Items.get(Constants.ItemName.LinkedCard) + private lazy val floppy = api.Items.get(Constants.ItemName.Floppy) + private lazy val hdds = Array( + api.Items.get(Constants.ItemName.HDDTier1), + api.Items.get(Constants.ItemName.HDDTier2), + api.Items.get(Constants.ItemName.HDDTier3) + ) + private lazy val cpus = Array( + api.Items.get(Constants.ItemName.CPUTier1), + api.Items.get(Constants.ItemName.CPUTier2), + api.Items.get(Constants.ItemName.CPUTier3), + api.Items.get(Constants.ItemName.APUTier1), + api.Items.get(Constants.ItemName.APUTier2) + ) + private lazy val robot = api.Items.get(Constants.BlockName.Robot) + private lazy val tablet = api.Items.get(Constants.ItemName.Tablet) + private lazy val print = api.Items.get(Constants.BlockName.Print) + private val beaconBlocks = ItemTags.bind("forge:beacon_base_blocks") + + def addNBTToResult(recipe: IRecipe[_], craftedStack: ItemStack, inventory: CraftingInventory): ItemStack = { + val craftedItemName = api.Items.get(craftedStack) + + if (craftedItemName == navigationUpgrade) { + Option(api.Driver.driverFor(craftedStack)).foreach(driver => + for (stack <- getItems(inventory)) { + if (stack.getItem == Items.FILLED_MAP) { + // Store information of the map used for crafting in the result. + val nbt = driver.dataTag(craftedStack) + nbt.setNewCompoundTag(Settings.namespace + "map", stack.save) + } + }) + } + + if (craftedItemName == linkedCard) { + if (SideTracker.isServer) { + Option(api.Driver.driverFor(craftedStack)).foreach(driver => { + val nbt = driver.dataTag(craftedStack) + nbt.putString(Settings.namespace + "tunnel", UUID.randomUUID().toString) + }) + } + } + + if (cpus.contains(craftedItemName)) { + LuaStateFactory.setDefaultArch(craftedStack) + } + + if (craftedItemName == floppy || hdds.contains(craftedItemName)) { + val nbt = craftedStack.getOrCreateTag + if (recipe.canCraftInDimensions(1, 1)) { + // Formatting / loot to normal disk conversion, only keep coloring. + val colorKey = Settings.namespace + "color" + for (stack <- getItems(inventory)) { + if (api.Items.get(stack) != null && (api.Items.get(stack) == floppy || api.Items.get(stack).name == "lootDisk") && stack.hasTag) { + val oldData = stack.getTag + if (oldData.contains(colorKey) && oldData.getInt(colorKey) != Color.dyes.indexOf("lightGray")) { + nbt.put(colorKey, oldData.get(colorKey).copy()) + } + } + } + if (nbt.isEmpty) { + craftedStack.setTag(null) + } + } + else if (getItems(inventory).forall(api.Items.get(_) == floppy)) { + // Copy operation. + for (stack <- getItems(inventory)) { + if (api.Items.get(stack) == floppy && stack.hasTag) { + val oldData = stack.getTag + for (oldTagName <- oldData.getAllKeys.map(_.asInstanceOf[String]) if !nbt.contains(oldTagName)) { + nbt.put(oldTagName, oldData.get(oldTagName).copy()) + } + } + } + } + } + + if (craftedItemName == print && + recipe.getIngredients.size == 2) { + // First, copy old data. + val data = new PrintData(craftedStack) + val inputs = getItems(inventory) + for (stack <- inputs) { + if (api.Items.get(stack) == print) { + data.loadData(stack) + } + } + + // Then apply new data. + val glowstoneDust = new ItemStack(Items.GLOWSTONE_DUST) + val glowstone = new ItemStack(Blocks.GLOWSTONE) + for (stack <- inputs) { + if (stack.getItem.is(beaconBlocks)) { + if (data.isBeaconBase) { + // Crafting wouldn't change anything, prevent accidental resource loss. + return ItemStack.EMPTY + } + data.isBeaconBase = true + } + if (glowstoneDust.sameItem(stack)) { + if (data.lightLevel == 15) { + // Crafting wouldn't change anything, prevent accidental resource loss. + return ItemStack.EMPTY + } + data.lightLevel = math.min(15, data.lightLevel + 1) + } + if (glowstone.sameItem(stack)) { + if (data.lightLevel == 15) { + // Crafting wouldn't change anything, prevent accidental resource loss. + return ItemStack.EMPTY + } + data.lightLevel = math.min(15, data.lightLevel + 4) + } + } + + // Finally apply modified data. + data.saveData(craftedStack) + } + + // EEPROM copying. + if (craftedItemName == eeprom && + craftedStack.getCount == 2 && + recipe.getIngredients.size == 2) breakable { + for (stack <- getItems(inventory)) { + if (api.Items.get(stack) == eeprom && stack.hasTag) { + val copy = stack.getTag.copy.asInstanceOf[CompoundNBT] + // Erase node address, just in case. + copy.getCompound(Settings.namespace + "data").getCompound("node").remove("address") + craftedStack.setTag(copy) + break() + } + } + } + + // Swapping EEPROM in devices. + recraft(craftedStack, inventory, mcu, stack => new MCUDataWrapper(stack)) + recraft(craftedStack, inventory, drone, stack => new DroneDataWrapper(stack)) + recraft(craftedStack, inventory, robot, stack => new RobotDataWrapper(stack)) + recraft(craftedStack, inventory, tablet, stack => new TabletDataWrapper(stack)) + + craftedStack + } + + private def getItems(inventory: CraftingInventory) = (0 until inventory.getContainerSize).map(inventory.getItem).filter(!_.isEmpty) + + private def recraft(craftedStack: ItemStack, inventory: CraftingInventory, descriptor: ItemInfo, dataFactory: (ItemStack) => ItemDataWrapper) { + if (api.Items.get(craftedStack) == descriptor) { + // Find old Microcontroller. + getItems(inventory).find(api.Items.get(_) == descriptor) match { + case Some(oldMcu) => + val data = dataFactory(oldMcu) + + // Remove old EEPROM. + val oldRom = data.components.filter(api.Items.get(_) == eeprom) + data.components = data.components.diff(oldRom) + + // Insert new EEPROM. + for (stack <- getItems(inventory)) { + if (api.Items.get(stack) == eeprom) { + data.components :+= stack.copy.split(1) + } + } + + data.save(craftedStack) + case _ => + } + } + } + + private trait ItemDataWrapper { + def components: Array[ItemStack] + + def components_=(value: Array[ItemStack]): Unit + + def save(stack: ItemStack): Unit + } + + private class MCUDataWrapper(val stack: ItemStack) extends ItemDataWrapper { + val data = new MicrocontrollerData(stack) + + override def components: Array[ItemStack] = data.components + + override def components_=(value: Array[ItemStack]): Unit = data.components = value + + override def save(stack: ItemStack): Unit = data.saveData(stack) + } + + private class DroneDataWrapper(val stack: ItemStack) extends ItemDataWrapper { + val data = new DroneData(stack) + + override def components: Array[ItemStack] = data.components + + override def components_=(value: Array[ItemStack]): Unit = data.components = value + + override def save(stack: ItemStack): Unit = data.saveData(stack) + } + + private class RobotDataWrapper(val stack: ItemStack) extends ItemDataWrapper { + val data = new RobotData(stack) + + override def components: Array[ItemStack] = data.components + + override def components_=(value: Array[ItemStack]): Unit = data.components = value + + override def save(stack: ItemStack): Unit = data.saveData(stack) + } + + private class TabletDataWrapper(val stack: ItemStack) extends ItemDataWrapper { + val data = new TabletData(stack) + + var components: Array[ItemStack] = data.items.filter(!_.isEmpty) + + override def save(stack: ItemStack): Unit = { + data.items = components.clone() + data.saveData(stack) + } + } + +} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedRecipe.java b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedRecipe.java new file mode 100644 index 0000000000..cd0af03135 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapedRecipe.java @@ -0,0 +1,101 @@ +package li.cil.oc.common.recipe; + +import com.google.gson.JsonObject; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ICraftingRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.ShapedRecipe; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.common.crafting.IShapedRecipe; +import net.minecraftforge.registries.ForgeRegistryEntry; + +public class ExtendedShapedRecipe implements ICraftingRecipe, IShapedRecipe { + private ShapedRecipe wrapped; + + public ExtendedShapedRecipe(ShapedRecipe wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean matches(CraftingInventory inv, World world) { + return wrapped.matches(inv, world); + } + + @Override + public ItemStack assemble(CraftingInventory inv) { + return ExtendedRecipe.addNBTToResult(this, wrapped.assemble(inv), inv); + } + + @Override + public boolean canCraftInDimensions(int w, int h) { + return wrapped.canCraftInDimensions(w, h); + } + + @Override + public ItemStack getResultItem() { + return wrapped.getResultItem(); + } + + @Override + public NonNullList getRemainingItems(CraftingInventory inv) { + return wrapped.getRemainingItems(inv); + } + + @Override + public NonNullList getIngredients() { + return wrapped.getIngredients(); + } + + @Override + public ResourceLocation getId() { + return wrapped.getId(); + } + + @Override + public IRecipeSerializer getSerializer() { + return RecipeSerializers.CRAFTING_SHAPED_EXTENDED; + } + + @Override + public String getGroup() { + return wrapped.getGroup(); + } + + @Override + public int getRecipeWidth() { + return wrapped.getRecipeWidth(); + } + + @Override + public int getRecipeHeight() { + return wrapped.getRecipeHeight(); + } + + public static final class Serializer extends ForgeRegistryEntry> + implements IRecipeSerializer { + + @Override + public ExtendedShapedRecipe fromJson(ResourceLocation recipeId, JsonObject json) { + ShapedRecipe wrapped = IRecipeSerializer.SHAPED_RECIPE.fromJson(recipeId, json); + return new ExtendedShapedRecipe(wrapped); + } + + @Override + public ExtendedShapedRecipe fromNetwork(ResourceLocation recipeId, PacketBuffer buff) { + ShapedRecipe wrapped = IRecipeSerializer.SHAPED_RECIPE.fromNetwork(recipeId, buff); + return new ExtendedShapedRecipe(wrapped); + } + + @Override + public void toNetwork(PacketBuffer buff, ExtendedShapedRecipe recipe) { + IRecipeSerializer serializer = + (IRecipeSerializer) recipe.wrapped.getSerializer(); + serializer.toNetwork(buff, recipe.wrapped); + } + } +} diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessRecipe.java b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessRecipe.java new file mode 100644 index 0000000000..be68905ded --- /dev/null +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedShapelessRecipe.java @@ -0,0 +1,90 @@ +package li.cil.oc.common.recipe; + +import com.google.gson.JsonObject; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ICraftingRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.ShapelessRecipe; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.registries.ForgeRegistryEntry; + +public class ExtendedShapelessRecipe implements ICraftingRecipe { + private ShapelessRecipe wrapped; + + public ExtendedShapelessRecipe(ShapelessRecipe wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean matches(CraftingInventory inv, World world) { + return wrapped.matches(inv, world); + } + + @Override + public ItemStack assemble(CraftingInventory inv) { + return ExtendedRecipe.addNBTToResult(this, wrapped.assemble(inv), inv); + } + + @Override + public boolean canCraftInDimensions(int w, int h) { + return wrapped.canCraftInDimensions(w, h); + } + + @Override + public ItemStack getResultItem() { + return wrapped.getResultItem(); + } + + @Override + public NonNullList getRemainingItems(CraftingInventory inv) { + return wrapped.getRemainingItems(inv); + } + + @Override + public NonNullList getIngredients() { + return wrapped.getIngredients(); + } + + @Override + public ResourceLocation getId() { + return wrapped.getId(); + } + + @Override + public IRecipeSerializer getSerializer() { + return RecipeSerializers.CRAFTING_SHAPELESS_EXTENDED; + } + + @Override + public String getGroup() { + return wrapped.getGroup(); + } + + public static final class Serializer extends ForgeRegistryEntry> + implements IRecipeSerializer { + + @Override + public ExtendedShapelessRecipe fromJson(ResourceLocation recipeId, JsonObject json) { + ShapelessRecipe wrapped = IRecipeSerializer.SHAPELESS_RECIPE.fromJson(recipeId, json); + return new ExtendedShapelessRecipe(wrapped); + } + + @Override + public ExtendedShapelessRecipe fromNetwork(ResourceLocation recipeId, PacketBuffer buff) { + ShapelessRecipe wrapped = IRecipeSerializer.SHAPELESS_RECIPE.fromNetwork(recipeId, buff); + return new ExtendedShapelessRecipe(wrapped); + } + + @Override + public void toNetwork(PacketBuffer buff, ExtendedShapelessRecipe recipe) { + IRecipeSerializer serializer = + (IRecipeSerializer) recipe.wrapped.getSerializer(); + serializer.toNetwork(buff, recipe.wrapped); + } + } +} diff --git a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java index ae2e0d7cb5..addd545603 100644 --- a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java +++ b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java @@ -18,12 +18,16 @@ public class RecipeSerializers { public static final IRecipeSerializer CRAFTING_LOOTDISK_CYCLING = null; public static final IRecipeSerializer CRAFTING_COLORIZE = null; public static final IRecipeSerializer CRAFTING_DECOLORIZE = null; + public static final IRecipeSerializer CRAFTING_SHAPED_EXTENDED = null; + public static final IRecipeSerializer CRAFTING_SHAPELESS_EXTENDED = null; @SubscribeEvent public static void registerSerializers(RegistryEvent.Register> e) { register(e.getRegistry(), "crafting_lootdisk_cycling", new SpecialRecipeSerializer<>(LootDiskCyclingRecipe::new)); register(e.getRegistry(), "crafting_colorize", new ItemSpecialSerializer<>(ColorizeRecipe::new, ColorizeRecipe::targetItem)); register(e.getRegistry(), "crafting_decolorize", new ItemSpecialSerializer<>(DecolorizeRecipe::new, DecolorizeRecipe::targetItem)); + register(e.getRegistry(), "crafting_shaped_extended", new ExtendedShapedRecipe.Serializer()); + register(e.getRegistry(), "crafting_shapeless_extended", new ExtendedShapelessRecipe.Serializer()); } private static > & IRecipeSerializer> From 77c0583670e7df500763789c3935e706288cfcef Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 19 Oct 2022 01:11:28 +0200 Subject: [PATCH 120/159] Remove old dye color tag name system --- .../assets/opencomputers/loot/loot.properties | 22 +++++----- .../{floppy_dyered.json => floppy_black.json} | 2 +- .../{floppy_dyeblue.json => floppy_blue.json} | 2 +- ...{floppy_dyelime.json => floppy_brown.json} | 2 +- .../{floppy_dyecyan.json => floppy_cyan.json} | 2 +- .../models/item/floppy_dyegreen.json | 6 --- .../models/item/floppy_dyelightblue.json | 6 --- .../models/item/floppy_dyelightgray.json | 6 --- .../models/item/floppy_dyemagenta.json | 6 --- .../models/item/floppy_dyeorange.json | 6 --- .../models/item/floppy_dyepink.json | 6 --- .../models/item/floppy_dyepurple.json | 6 --- .../models/item/floppy_dyewhite.json | 6 --- .../models/item/floppy_dyeyellow.json | 6 --- .../models/item/floppy_gray.json | 6 +++ .../models/item/floppy_green.json | 6 +++ ...y_dyeblack.json => floppy_light_blue.json} | 2 +- ...y_dyebrown.json => floppy_light_gray.json} | 2 +- .../models/item/floppy_lime.json | 6 +++ ...loppy_dyegray.json => floppy_magenta.json} | 2 +- .../models/item/floppy_orange.json | 6 +++ .../models/item/floppy_pink.json | 6 +++ .../models/item/floppy_purple.json | 6 +++ .../opencomputers/models/item/floppy_red.json | 6 +++ .../models/item/floppy_white.json | 6 +++ .../models/item/floppy_yellow.json | 6 +++ .../{floppy_dyeblack.png => floppy_black.png} | Bin .../{floppy_dyeblue.png => floppy_blue.png} | Bin .../{floppy_dyebrown.png => floppy_brown.png} | Bin .../{floppy_dyecyan.png => floppy_cyan.png} | Bin .../{floppy_dyegray.png => floppy_gray.png} | Bin .../{floppy_dyegreen.png => floppy_green.png} | Bin ...dyelightblue.png => floppy_light_blue.png} | Bin ...dyelightgray.png => floppy_light_gray.png} | Bin .../{floppy_dyelime.png => floppy_lime.png} | Bin ...oppy_dyemagenta.png => floppy_magenta.png} | Bin ...floppy_dyeorange.png => floppy_orange.png} | Bin .../{floppy_dyepink.png => floppy_pink.png} | Bin ...floppy_dyepurple.png => floppy_purple.png} | Bin .../{floppy_dyered.png => floppy_red.png} | Bin .../{floppy_dyewhite.png => floppy_white.png} | Bin ...floppy_dyeyellow.png => floppy_yellow.png} | Bin src/main/scala/li/cil/oc/common/Loot.scala | 2 +- .../li/cil/oc/common/item/FloppyDisk.scala | 14 +++--- .../cil/oc/common/recipe/ExtendedRecipe.scala | 4 +- src/main/scala/li/cil/oc/util/Color.scala | 41 +----------------- 46 files changed, 83 insertions(+), 122 deletions(-) rename src/main/resources/assets/opencomputers/models/item/{floppy_dyered.json => floppy_black.json} (56%) rename src/main/resources/assets/opencomputers/models/item/{floppy_dyeblue.json => floppy_blue.json} (55%) rename src/main/resources/assets/opencomputers/models/item/{floppy_dyelime.json => floppy_brown.json} (55%) rename src/main/resources/assets/opencomputers/models/item/{floppy_dyecyan.json => floppy_cyan.json} (55%) delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyegreen.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyelightblue.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyelightgray.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyemagenta.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyeorange.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyepink.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyepurple.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyewhite.json delete mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_dyeyellow.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_gray.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_green.json rename src/main/resources/assets/opencomputers/models/item/{floppy_dyeblack.json => floppy_light_blue.json} (54%) rename src/main/resources/assets/opencomputers/models/item/{floppy_dyebrown.json => floppy_light_gray.json} (54%) create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_lime.json rename src/main/resources/assets/opencomputers/models/item/{floppy_dyegray.json => floppy_magenta.json} (55%) create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_orange.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_pink.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_purple.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_red.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_white.json create mode 100644 src/main/resources/assets/opencomputers/models/item/floppy_yellow.json rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyeblack.png => floppy_black.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyeblue.png => floppy_blue.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyebrown.png => floppy_brown.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyecyan.png => floppy_cyan.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyegray.png => floppy_gray.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyegreen.png => floppy_green.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyelightblue.png => floppy_light_blue.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyelightgray.png => floppy_light_gray.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyelime.png => floppy_lime.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyemagenta.png => floppy_magenta.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyeorange.png => floppy_orange.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyepink.png => floppy_pink.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyepurple.png => floppy_purple.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyered.png => floppy_red.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyewhite.png => floppy_white.png} (100%) rename src/main/resources/assets/opencomputers/textures/items/{floppy_dyeyellow.png => floppy_yellow.png} (100%) diff --git a/src/main/resources/assets/opencomputers/loot/loot.properties b/src/main/resources/assets/opencomputers/loot/loot.properties index c1886e02c3..c007863721 100644 --- a/src/main/resources/assets/opencomputers/loot/loot.properties +++ b/src/main/resources/assets/opencomputers/loot/loot.properties @@ -7,18 +7,18 @@ #The color defaults to gray. It must be a dye's ore-dict name. # General purpose. -network=Network (Network Stack):1:dyeLime -plan9k=Plan9k (Operating System):1:dyeRed -irc=OpenIRC (IRC Client):1:dyeLightBlue -openloader=OpenLoader (Boot Loader):1:dyeMagenta -openos=OpenOS (Operating System):0:dyeGreen -oppm=OPPM (Package Manager):0:dyeCyan +network=Network (Network Stack):1:lime +plan9k=Plan9k (Operating System):1:red +irc=OpenIRC (IRC Client):1:light_blue +openloader=OpenLoader (Boot Loader):1:magenta +openos=OpenOS (Operating System):0:green +oppm=OPPM (Package Manager):0:cyan # Robot utilities. -builder=Builder:1:dyeYellow -dig=Digger:2:dyeBrown -maze=Mazer:1:dyeOrange +builder=Builder:1:yellow +dig=Digger:2:brown +maze=Mazer:1:orange # Drivers for components. -data=Data Card Software:0:dyePink -generator=Generator Upgrade Software:0:dyePurple +data=Data Card Software:0:pink +generator=Generator Upgrade Software:0:purple diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyered.json b/src/main/resources/assets/opencomputers/models/item/floppy_black.json similarity index 56% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyered.json rename to src/main/resources/assets/opencomputers/models/item/floppy_black.json index eda33de570..e0820566c1 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyered.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_black.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyered" + "layer0": "opencomputers:items/floppy_black" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyeblue.json b/src/main/resources/assets/opencomputers/models/item/floppy_blue.json similarity index 55% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyeblue.json rename to src/main/resources/assets/opencomputers/models/item/floppy_blue.json index 5ab2f34ba8..4da3bf3a04 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyeblue.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_blue.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyeblue" + "layer0": "opencomputers:items/floppy_blue" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyelime.json b/src/main/resources/assets/opencomputers/models/item/floppy_brown.json similarity index 55% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyelime.json rename to src/main/resources/assets/opencomputers/models/item/floppy_brown.json index 244c7750a3..ba52c60827 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyelime.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_brown.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyelime" + "layer0": "opencomputers:items/floppy_brown" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyecyan.json b/src/main/resources/assets/opencomputers/models/item/floppy_cyan.json similarity index 55% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyecyan.json rename to src/main/resources/assets/opencomputers/models/item/floppy_cyan.json index ef844c9a22..9726f0d09d 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyecyan.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_cyan.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyecyan" + "layer0": "opencomputers:items/floppy_cyan" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyegreen.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyegreen.json deleted file mode 100644 index 915a2910e7..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyegreen.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyegreen" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyelightblue.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyelightblue.json deleted file mode 100644 index 635991a8ca..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyelightblue.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyelightblue" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyelightgray.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyelightgray.json deleted file mode 100644 index 3eadf282bb..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyelightgray.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyelightgray" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyemagenta.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyemagenta.json deleted file mode 100644 index be3ff99ca4..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyemagenta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyemagenta" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyeorange.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyeorange.json deleted file mode 100644 index c2860686cd..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyeorange.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyeorange" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyepink.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyepink.json deleted file mode 100644 index 3ac45505d5..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyepink.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyepink" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyepurple.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyepurple.json deleted file mode 100644 index 4c13535ad3..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyepurple.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyepurple" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyewhite.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyewhite.json deleted file mode 100644 index a8f9b07b06..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyewhite.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyewhite" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyeyellow.json b/src/main/resources/assets/opencomputers/models/item/floppy_dyeyellow.json deleted file mode 100644 index 911ca8afba..0000000000 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyeyellow.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "opencomputers:item/flat", - "textures": { - "layer0": "opencomputers:items/floppy_dyeyellow" - } -} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_gray.json b/src/main/resources/assets/opencomputers/models/item/floppy_gray.json new file mode 100644 index 0000000000..4dd9bd3ce3 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_gray.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_gray" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_green.json b/src/main/resources/assets/opencomputers/models/item/floppy_green.json new file mode 100644 index 0000000000..d7c33188fc --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_green.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_green" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyeblack.json b/src/main/resources/assets/opencomputers/models/item/floppy_light_blue.json similarity index 54% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyeblack.json rename to src/main/resources/assets/opencomputers/models/item/floppy_light_blue.json index 263e9e5997..54f58d4304 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyeblack.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_light_blue.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyeblack" + "layer0": "opencomputers:items/floppy_light_blue" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyebrown.json b/src/main/resources/assets/opencomputers/models/item/floppy_light_gray.json similarity index 54% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyebrown.json rename to src/main/resources/assets/opencomputers/models/item/floppy_light_gray.json index c3209a408f..d2c929426f 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyebrown.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_light_gray.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyebrown" + "layer0": "opencomputers:items/floppy_light_gray" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_lime.json b/src/main/resources/assets/opencomputers/models/item/floppy_lime.json new file mode 100644 index 0000000000..fe4dac3318 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_lime.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_lime" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_dyegray.json b/src/main/resources/assets/opencomputers/models/item/floppy_magenta.json similarity index 55% rename from src/main/resources/assets/opencomputers/models/item/floppy_dyegray.json rename to src/main/resources/assets/opencomputers/models/item/floppy_magenta.json index f66055135d..1f73c298d8 100644 --- a/src/main/resources/assets/opencomputers/models/item/floppy_dyegray.json +++ b/src/main/resources/assets/opencomputers/models/item/floppy_magenta.json @@ -1,6 +1,6 @@ { "parent": "opencomputers:item/flat", "textures": { - "layer0": "opencomputers:items/floppy_dyegray" + "layer0": "opencomputers:items/floppy_magenta" } } diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_orange.json b/src/main/resources/assets/opencomputers/models/item/floppy_orange.json new file mode 100644 index 0000000000..aa239efa60 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_orange.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_orange" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_pink.json b/src/main/resources/assets/opencomputers/models/item/floppy_pink.json new file mode 100644 index 0000000000..2c9d99b64c --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_pink.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_pink" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_purple.json b/src/main/resources/assets/opencomputers/models/item/floppy_purple.json new file mode 100644 index 0000000000..b036df4d7a --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_purple.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_purple" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_red.json b/src/main/resources/assets/opencomputers/models/item/floppy_red.json new file mode 100644 index 0000000000..b1f4c81056 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_red.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_red" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_white.json b/src/main/resources/assets/opencomputers/models/item/floppy_white.json new file mode 100644 index 0000000000..4fff4486df --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_white.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_white" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/floppy_yellow.json b/src/main/resources/assets/opencomputers/models/item/floppy_yellow.json new file mode 100644 index 0000000000..8013fb314a --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/floppy_yellow.json @@ -0,0 +1,6 @@ +{ + "parent": "opencomputers:item/flat", + "textures": { + "layer0": "opencomputers:items/floppy_yellow" + } +} diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyeblack.png b/src/main/resources/assets/opencomputers/textures/items/floppy_black.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyeblack.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_black.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyeblue.png b/src/main/resources/assets/opencomputers/textures/items/floppy_blue.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyeblue.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_blue.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyebrown.png b/src/main/resources/assets/opencomputers/textures/items/floppy_brown.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyebrown.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_brown.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyecyan.png b/src/main/resources/assets/opencomputers/textures/items/floppy_cyan.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyecyan.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_cyan.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyegray.png b/src/main/resources/assets/opencomputers/textures/items/floppy_gray.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyegray.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_gray.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyegreen.png b/src/main/resources/assets/opencomputers/textures/items/floppy_green.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyegreen.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_green.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyelightblue.png b/src/main/resources/assets/opencomputers/textures/items/floppy_light_blue.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyelightblue.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_light_blue.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyelightgray.png b/src/main/resources/assets/opencomputers/textures/items/floppy_light_gray.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyelightgray.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_light_gray.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyelime.png b/src/main/resources/assets/opencomputers/textures/items/floppy_lime.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyelime.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_lime.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyemagenta.png b/src/main/resources/assets/opencomputers/textures/items/floppy_magenta.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyemagenta.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_magenta.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyeorange.png b/src/main/resources/assets/opencomputers/textures/items/floppy_orange.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyeorange.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_orange.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyepink.png b/src/main/resources/assets/opencomputers/textures/items/floppy_pink.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyepink.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_pink.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyepurple.png b/src/main/resources/assets/opencomputers/textures/items/floppy_purple.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyepurple.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_purple.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyered.png b/src/main/resources/assets/opencomputers/textures/items/floppy_red.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyered.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_red.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyewhite.png b/src/main/resources/assets/opencomputers/textures/items/floppy_white.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyewhite.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_white.png diff --git a/src/main/resources/assets/opencomputers/textures/items/floppy_dyeyellow.png b/src/main/resources/assets/opencomputers/textures/items/floppy_yellow.png similarity index 100% rename from src/main/resources/assets/opencomputers/textures/items/floppy_dyeyellow.png rename to src/main/resources/assets/opencomputers/textures/items/floppy_yellow.png diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index d41fcfd0e9..eaee85e5db 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -142,7 +142,7 @@ object Loot { val value = list.getProperty(key) try value.split(":") match { case Array(name, count, color) => - acc += ((createLootDisk(name, key, external, Color.byOreName.get(color)), count.toInt)) + acc += ((createLootDisk(name, key, external, Some(Color.byName(color))), count.toInt)) case Array(name, count) => acc += ((createLootDisk(name, key, external), count.toInt)) case _ => diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index 32ee450ae9..31732aa3d0 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -2,10 +2,10 @@ package li.cil.oc.common.item import li.cil.oc.Constants import li.cil.oc.Settings -import li.cil.oc.util.Color import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.DyeColor import net.minecraft.item.Item import net.minecraft.item.Item.Properties import net.minecraft.item.ItemStack @@ -24,8 +24,8 @@ class FloppyDisk(props: Properties) extends Item(props) with IForgeItem with tra val kiloBytes = Settings.get.floppySize @OnlyIn(Dist.CLIENT) - private def modelLocationFromDyeName(name: String) = { - new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Floppy + "_" + name.toLowerCase, "inventory") + private def modelLocationFromDyeName(dye: DyeColor) = { + new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.ItemName.Floppy + "_" + dye.getName, "inventory") } @OnlyIn(Dist.CLIENT) @@ -34,14 +34,14 @@ class FloppyDisk(props: Properties) extends Item(props) with IForgeItem with tra if (stack.hasTag && stack.getTag.contains(Settings.namespace + "color")) stack.getTag.getInt(Settings.namespace + "color") else - 8 - modelLocationFromDyeName(Color.byId(dyeIndex max 0 min 15)) + DyeColor.LIGHT_GRAY.getId + modelLocationFromDyeName(DyeColor.byId(dyeIndex max 0 min 15)) } @OnlyIn(Dist.CLIENT) override def registerModelLocations(): Unit = { - for (dyeName <- Color.dyes) { - val location = modelLocationFromDyeName(dyeName) + for (dye <- DyeColor.values) { + val location = modelLocationFromDyeName(dye) ModelLoader.addSpecialModel(location) } } diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala index 24c3a4d52c..328d760fb4 100644 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala @@ -12,11 +12,11 @@ import li.cil.oc.common.item.data.PrintData import li.cil.oc.common.item.data.RobotData import li.cil.oc.common.item.data.TabletData import li.cil.oc.server.machine.luac.LuaStateFactory -import li.cil.oc.util.Color import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.SideTracker import net.minecraft.block.Blocks import net.minecraft.inventory.CraftingInventory +import net.minecraft.item.DyeColor import net.minecraft.item.Items import net.minecraft.item.ItemStack import net.minecraft.item.crafting.IRecipe @@ -86,7 +86,7 @@ object ExtendedRecipe { for (stack <- getItems(inventory)) { if (api.Items.get(stack) != null && (api.Items.get(stack) == floppy || api.Items.get(stack).name == "lootDisk") && stack.hasTag) { val oldData = stack.getTag - if (oldData.contains(colorKey) && oldData.getInt(colorKey) != Color.dyes.indexOf("lightGray")) { + if (oldData.contains(colorKey) && oldData.getInt(colorKey) != DyeColor.LIGHT_GRAY.getId) { nbt.put(colorKey, oldData.get(colorKey).copy()) } } diff --git a/src/main/scala/li/cil/oc/util/Color.scala b/src/main/scala/li/cil/oc/util/Color.scala index 848807617a..a9d66cc346 100644 --- a/src/main/scala/li/cil/oc/util/Color.scala +++ b/src/main/scala/li/cil/oc/util/Color.scala @@ -25,46 +25,7 @@ object Color { DyeColor.WHITE -> 0xF0F0F0 ) - @Deprecated - val dyes = Array( - "dyeBlack", - "dyeRed", - "dyeGreen", - "dyeBrown", - "dyeBlue", - "dyePurple", - "dyeCyan", - "dyeLightGray", - "dyeGray", - "dyePink", - "dyeLime", - "dyeYellow", - "dyeLightBlue", - "dyeMagenta", - "dyeOrange", - "dyeWhite") - - @Deprecated - val byOreName = Map( - "dyeBlack" -> DyeColor.BLACK, - "dyeRed" -> DyeColor.RED, - "dyeGreen" -> DyeColor.GREEN, - "dyeBrown" -> DyeColor.BROWN, - "dyeBlue" -> DyeColor.BLUE, - "dyePurple" -> DyeColor.PURPLE, - "dyeCyan" -> DyeColor.CYAN, - "dyeLightGray" -> DyeColor.LIGHT_GRAY, - "dyeGray" -> DyeColor.GRAY, - "dyePink" -> DyeColor.PINK, - "dyeLime" -> DyeColor.LIME, - "dyeYellow" -> DyeColor.YELLOW, - "dyeLightBlue" -> DyeColor.LIGHT_BLUE, - "dyeMagenta" -> DyeColor.MAGENTA, - "dyeOrange" -> DyeColor.ORANGE, - "dyeWhite" -> DyeColor.WHITE) - - @Deprecated - val byId = dyes.map(name => (byOreName(name).getId, name)).toMap + val byName = DyeColor.values.map(col => (col.getName, col)).toMap val byTag = DyeColor.values.map(col => (col.getTag.getName, col)).toMap From 1ade67f3042805d44668e85293a06971c6c7a206 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 20 Oct 2022 01:08:28 +0200 Subject: [PATCH 121/159] Fix hologram rendering Can't render behind translucency, would need sorting and thus break caching --- .../tileentity/HologramRenderer.scala | 76 ++++++++++++------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala index 952ee94fa0..9834f42212 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala @@ -1,6 +1,7 @@ package li.cil.oc.client.renderer.tileentity import java.nio.IntBuffer +import java.util.ArrayDeque import java.util.function.Function import java.util.concurrent.Callable import java.util.concurrent.TimeUnit @@ -21,6 +22,7 @@ import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher import net.minecraft.tileentity.TileEntity import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3f +import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.event.TickEvent.ClientTickEvent import net.minecraftforge.eventbus.api.SubscribeEvent import org.lwjgl.BufferUtils @@ -29,13 +31,11 @@ import org.lwjgl.opengl.GL15 import scala.util.Random -object HologramRenderer extends Function[TileEntityRendererDispatcher, HologramRenderer] { - override def apply(dispatch: TileEntityRendererDispatcher) = new HologramRenderer(dispatch) -} - -class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Hologram](dispatch) +object HologramRenderer extends Function[TileEntityRendererDispatcher, HologramRenderer] with Callable[Int] with RemovalListener[TileEntity, Int] { + override def apply(dispatch: TileEntityRendererDispatcher) = new HologramRenderer(dispatch) + private val random = new Random() /** We cache the VBOs for the projectors we render for performance. */ @@ -76,33 +76,43 @@ class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit */ private var failed = false - override def render(hologram: Hologram, f: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { - if (failed) { - HologramRendererFallback.render(hologram, f, stack, buffer, light, overlay) - return - } + private val renderQueue = new ArrayDeque[Hologram] - this.hologram = hologram - RenderState.checkError(getClass.getName + ".render: entering (aka: wasntme)") + // Defer actual rendering until now so transparent things render correctly. + @SubscribeEvent + def onRenderWorldLast(e: RenderWorldLastEvent): Unit = { + RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: entering (aka: wasntme)") + + val stack = e.getMatrixStack + val camPos = Minecraft.getInstance.gameRenderer.getMainCamera.getPosition + val buffer = Minecraft.getInstance.renderBuffers.bufferSource + + while (!renderQueue.isEmpty) { + val holo = renderQueue.removeFirst() + val pos = holo.getBlockPos + stack.pushPose() + stack.translate(pos.getX + 0.5 - camPos.x, pos.getY + 0.5 - camPos.y, pos.getZ + 0.5 - camPos.z) + doRender(holo, e.getPartialTicks, stack) + stack.popPose() + } - if (!hologram.hasPower) return + RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: leaving") + } + private def doRender(hologram: Hologram, f: Float, stack: MatrixStack) { + HologramRenderer.hologram = hologram GL11.glPushClientAttrib(GL11.GL_CLIENT_ALL_ATTRIB_BITS) - RenderState.pushAttrib() RenderState.makeItBlend() RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE) val pos = hologram.getBlockPos - val playerDistSq = Minecraft.getInstance.player.getEyePosition(f) - .distanceToSqr(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) + val relPos = Minecraft.getInstance.player.getEyePosition(f). + subtract(pos.getX + 0.5, pos.getY + 0.5, pos.getZ + 0.5) + val playerDistSq = relPos.dot(relPos) val maxDistSq = hologram.getViewDistance * hologram.getViewDistance val fadeDistSq = hologram.getFadeStartDistanceSquared RenderState.setBlendAlpha(0.75f * (if (playerDistSq > fadeDistSq) math.max(0, 1 - ((playerDistSq - fadeDistSq) / (maxDistSq - fadeDistSq)).toFloat) else 1)) - stack.pushPose() - - stack.translate(0.5, 0.5, 0.5) - hologram.yaw match { case Direction.WEST => stack.mulPose(Vector3f.YP.rotationDegrees(-90)) case Direction.NORTH => stack.mulPose(Vector3f.YP.rotationDegrees(180)) @@ -136,9 +146,9 @@ class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit Textures.bind(Textures.Model.HologramEffect) - val sx = (pos.getX + 0.5) * hologram.scale - val sy = -(pos.getY + 0.5) * hologram.scale - val sz = (pos.getZ + 0.5) * hologram.scale + val sx = relPos.x * hologram.scale + val sy = relPos.y * hologram.scale + val sz = relPos.z * hologram.scale if (sx >= -1.5 && sx <= 1.5 && sz >= -1.5 && sz <= 1.5 && sy >= 0 && sy <= 2) { // Camera is inside the hologram. RenderSystem.disableCull() @@ -153,7 +163,10 @@ class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit // When we don't do this the hologram will look different from different // angles (because some faces will shine through sometimes and sometimes // they won't), so a more... consistent look is desirable. + RenderSystem.pushMatrix() + RenderSystem.multMatrix(stack.last.pose) val glBuffer = cache.get(hologram, this) + GL11.glEnable(GL11.GL_DEPTH_TEST) RenderSystem.colorMask(false, false, false, false) RenderSystem.depthMask(true) draw(glBuffer) @@ -161,14 +174,10 @@ class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit RenderSystem.depthFunc(GL11.GL_EQUAL) draw(glBuffer) RenderSystem.depthFunc(GL11.GL_LEQUAL) - - stack.popPose() + RenderSystem.popMatrix() RenderState.disableBlend() - RenderState.popAttrib() GL11.glPopClientAttrib() - - RenderState.checkError(getClass.getName + ".render: leaving") } def draw(glBuffer: Int) { @@ -393,3 +402,14 @@ class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntit @SubscribeEvent def onTick(e: ClientTickEvent) = cache.cleanUp() } + +class HologramRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRenderer[Hologram](dispatch) { + override def render(hologram: Hologram, f: Float, stack: MatrixStack, buffer: IRenderTypeBuffer, light: Int, overlay: Int) { + if (HologramRenderer.failed) { + HologramRendererFallback.render(hologram, f, stack, buffer, light, overlay) + return + } + + if (hologram.hasPower) HologramRenderer.renderQueue.addLast(hologram) + } +} From 6e6da2eb597e63618d4e6e6e5d350a73e7ea2104 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Thu, 20 Oct 2022 19:11:09 +0200 Subject: [PATCH 122/159] Fix missing block shapes breaking occlusion --- .../li/cil/oc/common/block/Assembler.scala | 14 +++++++++++++- .../scala/li/cil/oc/common/block/Printer.scala | 17 ++++++++++++++++- .../li/cil/oc/common/block/SimpleBlock.scala | 4 ---- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Assembler.scala b/src/main/scala/li/cil/oc/common/block/Assembler.scala index 3b5722afa7..052b8b2da0 100644 --- a/src/main/scala/li/cil/oc/common/block/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/block/Assembler.scala @@ -4,16 +4,28 @@ import li.cil.oc.Settings import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.ServerPlayerEntity -import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.world.IBlockReader import net.minecraft.world.World class Assembler(props: Properties) extends SimpleBlock(props) with traits.PowerAcceptor with traits.StateAware with traits.GUI { override def energyThroughput = Settings.get.assemblerRate + val blockShape = { + val bottom = Block.box(0, 0, 0, 16, 7, 16) + val mid = Block.box(2, 7, 2, 14, 9, 14) + val top = Block.box(0, 9, 0, 16, 16, 16) + VoxelShapes.or(top, bottom, mid) + } + + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = blockShape + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { case te: tileentity.Assembler => ContainerTypes.openAssemblerGui(player, te) case _ => diff --git a/src/main/scala/li/cil/oc/common/block/Printer.scala b/src/main/scala/li/cil/oc/common/block/Printer.scala index 94a022bffe..90cf805894 100644 --- a/src/main/scala/li/cil/oc/common/block/Printer.scala +++ b/src/main/scala/li/cil/oc/common/block/Printer.scala @@ -3,14 +3,29 @@ package li.cil.oc.common.block import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.tileentity import net.minecraft.block.AbstractBlock.Properties +import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.ServerPlayerEntity -import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.shapes.IBooleanFunction +import net.minecraft.util.math.shapes.ISelectionContext +import net.minecraft.util.math.shapes.VoxelShape +import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.world.IBlockReader import net.minecraft.world.World class Printer(props: Properties) extends SimpleBlock(props) with traits.StateAware with traits.GUI { + val blockShape = { + val base = Block.box(0, 0, 0, 16, 8, 16) + val pillars = VoxelShapes.or(Block.box(0, 8, 0, 3, 13, 3), Block.box(13, 8, 0, 16, 13, 3), + Block.box(13, 8, 13, 16, 13, 16), Block.box(0, 8, 13, 3, 13, 16)) + val ring = VoxelShapes.join(Block.box(0, 13, 0, 16, 16, 16), + Block.box(3, 13, 3, 13, 16, 13), IBooleanFunction.ONLY_FIRST) + VoxelShapes.or(base, pillars, ring) + } + + override def getShape(state: BlockState, world: IBlockReader, pos: BlockPos, ctx: ISelectionContext): VoxelShape = blockShape + override def openGui(player: ServerPlayerEntity, world: World, pos: BlockPos): Unit = world.getBlockEntity(pos) match { case te: tileentity.Printer => ContainerTypes.openPrinterGui(player, te) case _ => diff --git a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala index 5249dc1216..21578aa098 100644 --- a/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala +++ b/src/main/scala/li/cil/oc/common/block/SimpleBlock.scala @@ -31,15 +31,11 @@ import net.minecraft.util.Direction import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult -import net.minecraft.util.math.shapes.ISelectionContext -import net.minecraft.util.math.shapes.VoxelShape -import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.text.ITextComponent import net.minecraft.util.text.StringTextComponent import net.minecraft.world.IBlockReader import net.minecraft.world.IWorldReader import net.minecraft.world.World -import net.minecraft.world.server.ServerWorld import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.ToolType From d39388b747e03d44d4d74161cc65c1a5a5b6cb5c Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 21 Oct 2022 22:42:29 +0200 Subject: [PATCH 123/159] Fix screen background rendering --- .../cil/oc/client/renderer/RenderCache.java | 42 +++++++++---------- .../cil/oc/client/renderer/RenderTypes.java | 1 + 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderCache.java b/src/main/scala/li/cil/oc/client/renderer/RenderCache.java index c3460cb6f9..435384e36f 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderCache.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderCache.java @@ -3,8 +3,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import java.util.HashMap; -import java.util.Map; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; @@ -20,10 +18,12 @@ public class RenderCache implements IRenderTypeBuffer { public static class DrawEntry { + private final RenderType type; private final DrawState state; private final ByteBuffer data; - public DrawEntry(DrawState state, ByteBuffer data, boolean copy) { + public DrawEntry(RenderType type, DrawState state, ByteBuffer data, boolean copy) { + this.type = type; this.state = state; if (copy) { @@ -35,6 +35,10 @@ public DrawEntry(DrawState state, ByteBuffer data, boolean copy) { this.data = data; } + public RenderType type() { + return type; + } + public DrawState state() { return state; } @@ -44,32 +48,28 @@ public ByteBuffer data() { } } - private final Map> cached; + private final List cached; private RenderType activeType; private BufferBuilder activeBuilder; public RenderCache() { - cached = new HashMap<>(); + cached = new ArrayList<>(); } public boolean isEmpty() { - for (List frames : cached.values()) { - if (!frames.isEmpty()) return false; - } - return true; + return cached.isEmpty(); } public void clear() { - cached.forEach((k, v) -> v.clear()); + cached.clear(); } private void flush(RenderType type) { if (type == activeType) { activeBuilder.end(); - List frames = cached.computeIfAbsent(type, k -> new ArrayList<>()); Pair rendered = activeBuilder.popNextBuffer(); if (rendered.getSecond().hasRemaining()) { - frames.add(new DrawEntry(rendered.getFirst(), rendered.getSecond(), true)); + cached.add(new DrawEntry(type, rendered.getFirst(), rendered.getSecond(), true)); } activeType = null; } @@ -99,17 +99,13 @@ public void render(MatrixStack stack) { RenderSystem.pushMatrix(); RenderSystem.multMatrix(stack.last().pose()); - cached.forEach((type, frames) -> { - if (!frames.isEmpty()) { - type.setupRenderState(); - frames.forEach(frame -> { - DrawState state = frame.state(); - state.format().setupBufferState(MemoryUtil.memAddress(frame.data())); - RenderSystem.drawArrays(state.mode(), 0, state.vertexCount()); - state.format().clearBufferState(); - }); - type.clearRenderState(); - } + cached.forEach(frame -> { + frame.type().setupRenderState(); + DrawState state = frame.state(); + state.format().setupBufferState(MemoryUtil.memAddress(frame.data())); + RenderSystem.drawArrays(state.mode(), 0, state.vertexCount()); + state.format().clearBufferState(); + frame.type().clearRenderState(); }); RenderSystem.popMatrix(); diff --git a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java index 1e3fc4384d..2817d7ff77 100644 --- a/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java +++ b/src/main/scala/li/cil/oc/client/renderer/RenderTypes.java @@ -87,6 +87,7 @@ private static final RenderType createUpgrade(String name, ResourceLocation text public static final RenderType FONT_QUAD = create(OpenComputers.ID() + ":font_quad", DefaultVertexFormats.POSITION_COLOR, GL11.GL_QUADS, 1024, State.builder() + .setWriteMaskState(COLOR_WRITE) .createCompositeState(false)); private static class CustomTextureState extends TexturingState { From 8a62c801ee0d7b4a8d4006e078d7d71052657386 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 24 Oct 2022 00:39:04 +0200 Subject: [PATCH 124/159] Fix loot disks not working on a real client --- src/main/java/li/cil/oc/api/FileSystem.java | 28 +++++------ src/main/java/li/cil/oc/api/Items.java | 14 ++++-- .../li/cil/oc/api/detail/FileSystemAPI.java | 26 ++++------ .../java/li/cil/oc/api/detail/ItemAPI.java | 11 ++-- src/main/scala/li/cil/oc/common/Loot.scala | 21 +++----- .../scala/li/cil/oc/common/init/Items.scala | 4 +- .../opencomputers/DriverFileSystem.scala | 4 +- .../opencomputers/DriverLootDisk.scala | 3 +- .../li/cil/oc/server/component/Robot.scala | 3 +- .../li/cil/oc/server/fs/FileSystem.scala | 50 ++++--------------- 10 files changed, 66 insertions(+), 98 deletions(-) diff --git a/src/main/java/li/cil/oc/api/FileSystem.java b/src/main/java/li/cil/oc/api/FileSystem.java index 49557d0cf5..2eca6b1d9d 100644 --- a/src/main/java/li/cil/oc/api/FileSystem.java +++ b/src/main/java/li/cil/oc/api/FileSystem.java @@ -3,6 +3,7 @@ import li.cil.oc.api.network.EnvironmentHost; import li.cil.oc.api.fs.Label; import li.cil.oc.api.network.ManagedEnvironment; +import net.minecraft.util.ResourceLocation; /** * This class provides factory methods for creating file systems that are @@ -22,29 +23,24 @@ */ public final class FileSystem { /** - * Creates a new file system based on the location of a class. + * Creates a new file system based on a mod-specific resource location where + * the namespace refers to the mod and the resource path denotes a + * (mandatory) subpath relative to that mod's assets directory. *

    - * This can be used to wrap a folder in the assets folder of your mod's JAR. - * The actual path is built like this: - *

    "/assets/" + domain + "/" + root
    - *

    - * If the class is located in a JAR file, this will create a read-only file - * system based on that JAR file. If the class file is located in the native - * file system, this will create a read-only file system first trying from - * the actual location of the class file, and failing that by searching the - * class path (i.e. it'll look for a path constructed as described above). + * If {@code loc} is stored in a JAR file, this will create a read-only file + * system based on that JAR file. If {@code loc} is stored in the native + * file system, this will create a read-only file system from the the location + * constructed as described above (relative to the root of the namespace). *

    * If the specified path cannot be located, the creation fails and this * returns null. * - * @param clazz the class whose containing JAR to wrap. - * @param domain the domain, usually your mod's ID. - * @param root an optional subdirectory. - * @return a file system wrapping the specified folder. + * @param loc the location where the file system's contents are stored. + * @return a file system wrapping the specified resource. */ - public static li.cil.oc.api.fs.FileSystem fromClass(final Class clazz, final String domain, final String root) { + public static li.cil.oc.api.fs.FileSystem fromResource(final ResourceLocation loc) { if (API.fileSystem != null) - return API.fileSystem.fromClass(clazz, domain, root); + return API.fileSystem.fromResource(loc); return null; } diff --git a/src/main/java/li/cil/oc/api/Items.java b/src/main/java/li/cil/oc/api/Items.java index 673969aef6..b9e7d86903 100644 --- a/src/main/java/li/cil/oc/api/Items.java +++ b/src/main/java/li/cil/oc/api/Items.java @@ -3,6 +3,7 @@ import li.cil.oc.api.detail.ItemInfo; import net.minecraft.item.DyeColor; import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; import java.util.concurrent.Callable; @@ -54,11 +55,11 @@ public static ItemInfo get(ItemStack stack) { *

    * The specified factory callable will be used to generate a new file * system when the loot disk is used as a component. The specified name - * will be used as the label for the loot disk, as well as the identifier - * to select the corresponding factory method, so choose wisely. + * will be used as the label for the loot disk, while the location serves + * as the identifier to select the corresponding factory method. *

    * To use some directory in your mod JAR as the directory provided by the - * loot disk, use {@link FileSystem#fromClass} in your callable. + * loot disk, use {@link FileSystem#fromResource} in your callable. *

    * If doRecipeCycling is true, the floppy disk will be * included in the floppy disk recipe cycle if that is enabled. @@ -66,15 +67,18 @@ public static ItemInfo get(ItemStack stack) { * Call this in the init phase or later, not in pre-init. * * @param name the label and identifier to use for the loot disk. + * @param loc the location where the disk's contents are stored. * @param color the color of the disk, as a Minecraft color. * @param factory the callable to call for creating file system instances. * @param doRecipeCycling whether to include this floppy disk in floppy disk cycling. * @return an item stack representing the registered loot disk, to allow * adding a recipe for your loot disk, for example. */ - public static ItemStack registerFloppy(String name, DyeColor color, Callable factory, boolean doRecipeCycling) { + public static ItemStack registerFloppy(String name, ResourceLocation loc, DyeColor color, + Callable factory, boolean doRecipeCycling) { + if (API.items != null) - return API.items.registerFloppy(name, color, factory, doRecipeCycling); + return API.items.registerFloppy(name, loc, color, factory, doRecipeCycling); return ItemStack.EMPTY; } diff --git a/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java b/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java index fa58c3b1a6..92de9b76be 100644 --- a/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java +++ b/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java @@ -4,30 +4,26 @@ import li.cil.oc.api.fs.FileSystem; import li.cil.oc.api.fs.Label; import li.cil.oc.api.network.ManagedEnvironment; +import net.minecraft.util.ResourceLocation; public interface FileSystemAPI { /** - * Creates a new file system based on the location of a class. + * Creates a new file system based on a mod-specific resource location where + * the namespace refers to the mod and the resource path denotes a + * (mandatory) subpath relative to that mod's assets directory. *

    - * This can be used to wrap a folder in the assets folder of your mod's JAR. - * The actual path is built like this: - *

    "/assets/" + domain + "/" + root
    - *

    - * If the class is located in a JAR file, this will create a read-only file - * system based on that JAR file. If the class file is located in the native - * file system, this will create a read-only file system first trying from - * the actual location of the class file, and failing that by searching the - * class path (i.e. it'll look for a path constructed as described above). + * If {@code loc} is stored in a JAR file, this will create a read-only file + * system based on that JAR file. If {@code loc} is stored in the native + * file system, this will create a read-only file system from the the location + * constructed as described above (relative to the root of the namespace). *

    * If the specified path cannot be located, the creation fails and this * returns null. * - * @param clazz the class whose containing JAR to wrap. - * @param domain the domain, usually your mod's ID. - * @param root an optional subdirectory. - * @return a file system wrapping the specified folder. + * @param loc the location where the file system's contents are stored. + * @return a file system wrapping the specified resource. */ - FileSystem fromClass(Class clazz, String domain, String root); + FileSystem fromResource(ResourceLocation loc); /** * Creates a new writable file system in the save folder. diff --git a/src/main/java/li/cil/oc/api/detail/ItemAPI.java b/src/main/java/li/cil/oc/api/detail/ItemAPI.java index 4efea8904d..f29c54230c 100644 --- a/src/main/java/li/cil/oc/api/detail/ItemAPI.java +++ b/src/main/java/li/cil/oc/api/detail/ItemAPI.java @@ -3,6 +3,7 @@ import li.cil.oc.api.FileSystem; import net.minecraft.item.DyeColor; import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; import javax.annotation.Nullable; import java.util.concurrent.Callable; @@ -41,11 +42,11 @@ public interface ItemAPI { *

    * The specified factory callable will be used to generate a new file * system when the loot disk is used as a component. The specified name - * will be used as the label for the loot disk, as well as the identifier - * to select the corresponding factory method, so choose wisely. + * will be used as the label for the loot disk, while the location serves + * as the identifier to select the corresponding factory method. *

    * To use some directory in your mod JAR as the directory provided by the - * loot disk, use {@link FileSystem#fromClass} in your callable. + * loot disk, use {@link FileSystem#fromResource} in your callable. *

    * If doRecipeCycling is true, the floppy disk will be * included in the floppy disk recipe cycle if that is enabled. @@ -53,13 +54,15 @@ public interface ItemAPI { * Call this in the init phase or later, not in pre-init. * * @param name the label and identifier to use for the loot disk. + * @param loc the location where the disk's contents are stored. * @param color the color of the disk, as a Minecraft color. * @param factory the callable to call for creating file system instances. * @param doRecipeCycling whether to include this floppy disk in floppy disk cycling. * @return an item stack representing the registered loot disk, to allow * adding a recipe for your loot disk, for example. */ - ItemStack registerFloppy(String name, DyeColor color, Callable factory, boolean doRecipeCycling); + ItemStack registerFloppy(String name, ResourceLocation loc, DyeColor color, + Callable factory, boolean doRecipeCycling); /** * Register a single custom EEPROM. diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index eaee85e5db..f97c9edd9c 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -14,6 +14,7 @@ import li.cil.oc.util.Color import net.minecraft.item.DyeColor import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ResourceLocation import net.minecraft.util.text.StringTextComponent import net.minecraft.world.World import net.minecraft.world.server.ServerWorld @@ -21,8 +22,6 @@ import net.minecraft.world.storage.FolderName import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.ModLoadingContext -import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable @@ -43,7 +42,7 @@ object Loot { // ChestGenHooks.PYRAMID_JUNGLE_CHEST, // ChestGenHooks.STRONGHOLD_LIBRARY) - val factories = mutable.Map.empty[String, Callable[FileSystem]] + val factories = mutable.Map.empty[ResourceLocation, Callable[FileSystem]] val globalDisks = mutable.ArrayBuffer.empty[(ItemStack, Int)] @@ -65,12 +64,8 @@ object Loot { if (disksForSampling.nonEmpty) Some(disksForSampling(rng.nextInt(disksForSampling.length))) else None - def registerLootDisk(name: String, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { - val mod = ModLoadingContext.get.getActiveContainer.getModId - - OpenComputers.log.debug(s"Registering loot disk '$name' from mod $mod.") - - val modSpecificName = mod + ":" + name + def registerLootDisk(name: String, loc: ResourceLocation, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { + OpenComputers.log.debug(s"Registering loot disk '$name' from mod ${loc.getNamespace}.") val data = new CompoundNBT() data.putString(Settings.namespace + "fs.label", name) @@ -80,10 +75,10 @@ object Loot { nbt.put(Settings.namespace + "data", data) // Store this top level, so it won't get wiped on save. - nbt.putString(Settings.namespace + "lootFactory", modSpecificName) + nbt.putString(Settings.namespace + "lootFactory", loc.toString) nbt.putInt(Settings.namespace + "color", color.getId) - Loot.factories += modSpecificName -> factory + Loot.factories += loc -> factory if(doRecipeCycling) { Loot.disksForCyclingServer += stack @@ -158,9 +153,9 @@ object Loot { val callable = if (external) new Callable[FileSystem] { override def call(): FileSystem = api.FileSystem.asReadOnly(api.FileSystem.fromSaveDirectory("loot/" + path, 0, false)) } else new Callable[FileSystem] { - override def call(): FileSystem = api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, "loot/" + path) + override def call(): FileSystem = api.FileSystem.fromResource(new ResourceLocation(Settings.resourceDomain, "loot/" + path)) } - val stack = registerLootDisk(path, color.getOrElse(DyeColor.LIGHT_GRAY), callable, doRecipeCycling = true) + val stack = registerLootDisk(path, new ResourceLocation(Settings.resourceDomain, path), color.getOrElse(DyeColor.LIGHT_GRAY), callable, doRecipeCycling = true) stack.setHoverName(new StringTextComponent(name)) if (!external) { Items.registerStack(stack, path) diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 4e469b5732..ee8c78703d 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -166,8 +166,8 @@ object Items extends ItemAPI { val registeredItems: ArrayBuffer[ItemStack] = mutable.ArrayBuffer.empty[ItemStack] - override def registerFloppy(name: String, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { - val stack = Loot.registerLootDisk(name, color, factory, doRecipeCycling) + override def registerFloppy(name: String, loc: ResourceLocation, color: DyeColor, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = { + val stack = Loot.registerLootDisk(name, loc, color, factory, doRecipeCycling) registeredItems += stack diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala index 866ded6c4d..b40f570039 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -15,6 +15,7 @@ import li.cil.oc.server.component.Drive import li.cil.oc.server.fs.FileSystem.{ItemLabel, ReadOnlyLabel} import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ResourceLocation import net.minecraftforge.fml.server.ServerLifecycleHooks object DriverFileSystem extends Item { @@ -51,7 +52,8 @@ object DriverFileSystem extends Item { private def createEnvironment(stack: ItemStack, capacity: Int, platterCount: Int, host: EnvironmentHost, speed: Int) = if (ServerLifecycleHooks.getCurrentServer != null) { if (stack.hasTag && stack.getTag.contains(Settings.namespace + "lootFactory")) { // Loot disk, create file system using factory callback. - Loot.factories.get(stack.getTag.getString(Settings.namespace + "lootFactory")) match { + val lootFactory = new ResourceLocation(stack.getTag.getString(Settings.namespace + "lootFactory")) + Loot.factories.get(lootFactory) match { case Some(factory) => val label = if (dataTag(stack).contains(Settings.namespace + "fs.label")) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala index b0f98c534e..ab8c63ea13 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverLootDisk.scala @@ -9,6 +9,7 @@ import li.cil.oc.api import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common.Slot import net.minecraft.item.ItemStack +import net.minecraft.util.ResourceLocation import net.minecraft.world.storage.FolderName import net.minecraftforge.fml.server.ServerLifecycleHooks @@ -29,7 +30,7 @@ object DriverLootDisk extends Item { api.FileSystem.fromSaveDirectory(lootPath, 0, false) } else { - api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, lootPath) + api.FileSystem.fromResource(new ResourceLocation(Settings.resourceDomain, lootPath)) } val label = if (dataTag(stack).contains(Settings.namespace + "fs.label")) { diff --git a/src/main/scala/li/cil/oc/server/component/Robot.scala b/src/main/scala/li/cil/oc/server/component/Robot.scala index 1008b077a1..ad68547b40 100644 --- a/src/main/scala/li/cil/oc/server/component/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/Robot.scala @@ -26,6 +26,7 @@ import li.cil.oc.util.StackOption._ import net.minecraft.nbt.CompoundNBT import net.minecraft.particles.ParticleTypes import net.minecraft.util.Direction +import net.minecraft.util.ResourceLocation import scala.collection.convert.ImplicitConversionsToJava._ @@ -36,7 +37,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with create() val romRobot = Option(api.FileSystem.asManagedEnvironment(api.FileSystem. - fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/component/robot"), "robot")) + fromResource(new ResourceLocation(Settings.resourceDomain, "lua/component/robot")), "robot")) private final lazy val deviceInfo = Map( DeviceAttribute.Class -> DeviceClass.System, diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index 6222c65f25..53874c8b20 100755 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -15,7 +15,9 @@ import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.server.component import net.minecraft.item.ItemStack import net.minecraft.nbt.CompoundNBT +import net.minecraft.util.ResourceLocation import net.minecraft.world.storage.FolderName +import net.minecraftforge.fml.loading.FMLLoader import net.minecraftforge.fml.server.ServerLifecycleHooks import scala.util.Try @@ -58,53 +60,21 @@ object FileSystem extends api.detail.FileSystemAPI { path } - override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = { - val innerPath = ("/assets/" + domain + "/" + (root.trim + "/")).replace("//", "/") + override def fromResource(loc: ResourceLocation): api.fs.FileSystem = { + val innerPath = "/assets/" + loc.getNamespace + "/" + (loc.getPath.trim + "/") - val codeSource = clazz.getProtectionDomain.getCodeSource - val codePath = if (codeSource != null) codeSource.getLocation.getPath else { - val name = clazz.getName.replace('.', '/').concat(".class") - val resource = clazz.getClassLoader.getResource(name) - if (resource == null) throw new IllegalArgumentException(s"Could not locate ${clazz}") - resource.getPath - } - val (codeUrl, isArchive) = - if (codePath.contains(".zip!") || codePath.contains(".jar!")) - (codePath.substring(0, codePath.lastIndexOf('!')), true) - else { - val name = clazz.getName.replace('.', '/').concat(".class") - if (!codePath.endsWith(name)) throw new IllegalArgumentException(s"Mismatched ${clazz} to '${codePath}'") - (codePath, false) - } - - val url = Try { - new URL(codeUrl) - }.recoverWith { - case _: MalformedURLException => Try { - new URL("file://" + codeUrl) - } - } - val file = url.map(url => new io.File(url.toURI)).recoverWith { - case _: URISyntaxException => url.map(url => new io.File(url.getPath)) - }.getOrElse(new io.File(codePath)) + val modInfo = FMLLoader.getLoadingModList().getModFileById(loc.getNamespace) + val file = modInfo.getFile().getFilePath().toFile() - if (isArchive) { + if (!file.exists) return null + if (!file.isDirectory) { ZipFileInputStreamFileSystem.fromFile(file, innerPath.substring(1)) } else { - if (!file.exists || file.isDirectory) return null - new io.File(new io.File(file.getParent), innerPath) match { + new io.File(file, innerPath) match { case fsp if fsp.exists() && fsp.isDirectory => new ReadOnlyFileSystem(fsp) - case _ => - System.getProperty("java.class.path").split(System.getProperty("path.separator")). - find(cp => { - val fsp = new io.File(new io.File(cp), innerPath) - fsp.exists() && fsp.isDirectory - }) match { - case None => null - case Some(dir) => new ReadOnlyFileSystem(new io.File(new io.File(dir), innerPath)) - } + case _ => null } } } From ecbfeea96aa72082e4965facf561a80805d85dc1 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Mon, 24 Oct 2022 22:43:25 +0200 Subject: [PATCH 125/159] Fix loading of client-only code on servers --- .../scala/li/cil/oc/common/EventHandler.scala | 3 +++ .../scala/li/cil/oc/common/block/Screen.scala | 9 ++++++--- .../li/cil/oc/common/block/Waypoint.scala | 8 +++++++- .../cil/oc/common/container/Assembler.scala | 3 ++- .../li/cil/oc/common/container/Charger.scala | 1 - .../oc/common/container/ComponentSlot.scala | 3 ++- .../oc/common/container/Disassembler.scala | 1 - .../li/cil/oc/common/container/Drone.scala | 1 + .../container/DynamicComponentSlot.scala | 5 +++++ .../li/cil/oc/common/container/Printer.scala | 2 -- .../li/cil/oc/common/container/Robot.scala | 3 ++- .../container/StaticComponentSlot.scala | 3 ++- .../scala/li/cil/oc/common/item/Tablet.scala | 7 ++++++- .../li/cil/oc/common/item/Terminal.scala | 19 +++++++++++------- .../common/item/traits/FileSystemLike.scala | 11 +++++----- .../opencomputers/ModOpenComputers.scala | 20 +++++++++++++------ 16 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 6d53c855ce..59ffc590e7 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -47,6 +47,8 @@ import net.minecraft.world.chunk.Chunk import net.minecraft.world.server.ChunkHolder import net.minecraft.world.server.ChunkManager import net.minecraft.world.server.ServerWorld +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.client.event.ClientPlayerNetworkEvent import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer @@ -283,6 +285,7 @@ object EventHandler { } @SubscribeEvent + @OnlyIn(Dist.CLIENT) def clientLoggedIn(e: ClientPlayerNetworkEvent.LoggedInEvent) { PetRenderer.isInitialized = false PetRenderer.hidden.clear() diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala index 65a443bb75..b2fde6fb1d 100644 --- a/src/main/scala/li/cil/oc/common/block/Screen.scala +++ b/src/main/scala/li/cil/oc/common/block/Screen.scala @@ -73,9 +73,7 @@ class Screen(props: Properties, val tier: Int) extends RedstoneAware(props) { // Yep, this GUI is actually purely client side (to trigger it from // the server we would have to give screens a "container", which we // do not want). - if (world.isClientSide) { - Minecraft.getInstance.pushGuiLayer(new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.hasKeyboard, () => screen.origin.buffer.isRenderingEnabled)) - } + if (world.isClientSide) showGui(screen) true case screen: tileentity.Screen if screen.tier > 0 && side == screen.facing => if (world.isClientSide && player == Minecraft.getInstance.player) { @@ -86,6 +84,11 @@ class Screen(props: Properties, val tier: Int) extends RedstoneAware(props) { } } + @OnlyIn(Dist.CLIENT) + private def showGui(screen: tileentity.Screen) { + Minecraft.getInstance.pushGuiLayer(new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.hasKeyboard, () => screen.origin.buffer.isRenderingEnabled)) + } + override def stepOn(world: World, pos: BlockPos, entity: Entity): Unit = if (!world.isClientSide) world.getBlockEntity(pos) match { case screen: tileentity.Screen if screen.tier > 0 && screen.facing == Direction.UP => screen.walk(entity) diff --git a/src/main/scala/li/cil/oc/common/block/Waypoint.scala b/src/main/scala/li/cil/oc/common/block/Waypoint.scala index e615ab026a..294d2b0232 100644 --- a/src/main/scala/li/cil/oc/common/block/Waypoint.scala +++ b/src/main/scala/li/cil/oc/common/block/Waypoint.scala @@ -17,6 +17,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockRayTraceResult import net.minecraft.world.{IBlockReader, World} +import net.minecraftforge.api.distmarker.{Dist, OnlyIn} class Waypoint(props: Properties) extends RedstoneAware(props) { protected override def createBlockStateDefinition(builder: StateContainer.Builder[Block, BlockState]) = @@ -31,7 +32,7 @@ class Waypoint(props: Properties) extends RedstoneAware(props) { override def use(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, trace: BlockRayTraceResult): ActionResultType = { if (!player.isCrouching) { if (world.isClientSide) world.getBlockEntity(pos) match { - case t: tileentity.Waypoint => Minecraft.getInstance.pushGuiLayer(new gui.Waypoint(t)) + case t: tileentity.Waypoint => showGui(t) case _ => } ActionResultType.sidedSuccess(world.isClientSide) @@ -39,6 +40,11 @@ class Waypoint(props: Properties) extends RedstoneAware(props) { else super.use(state, world, pos, player, hand, trace) } + @OnlyIn(Dist.CLIENT) + private def showGui(t: tileentity.Waypoint) { + Minecraft.getInstance.pushGuiLayer(new gui.Waypoint(t)) + } + override def getValidRotations(world: World, pos: BlockPos): Array[Direction] = world.getBlockEntity(pos) match { case waypoint: tileentity.Waypoint => diff --git a/src/main/scala/li/cil/oc/common/container/Assembler.scala b/src/main/scala/li/cil/oc/common/container/Assembler.scala index 3a44df63ed..236cd33284 100644 --- a/src/main/scala/li/cil/oc/common/container/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Assembler.scala @@ -27,10 +27,11 @@ class Assembler(selfType: ContainerType[_ <: Assembler], id: Int, playerInventor override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false + if (isAssembling) return false AssemblerTemplates.select(stack).isDefined } + @OnlyIn(Dist.CLIENT) override def getBackgroundLocation = if (isAssembling) Textures.Icons.get(common.Tier.None) else super.getBackgroundLocation }) } diff --git a/src/main/scala/li/cil/oc/common/container/Charger.scala b/src/main/scala/li/cil/oc/common/container/Charger.scala index ee4f997dff..f4830be052 100644 --- a/src/main/scala/li/cil/oc/common/container/Charger.scala +++ b/src/main/scala/li/cil/oc/common/container/Charger.scala @@ -16,7 +16,6 @@ class Charger(selfType: ContainerType[_ <: Charger], id: Int, playerInventory: P addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, getHostClass, "tablet", Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false ItemCharge.canCharge(stack) } }) diff --git a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala index c3b03d5d77..80350cecc2 100644 --- a/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/ComponentSlot.scala @@ -20,6 +20,7 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int, def tier: Int + @OnlyIn(Dist.CLIENT) def tierIcon: ResourceLocation var changeListener: Option[Slot => Unit] = None @@ -37,7 +38,7 @@ abstract class ComponentSlot(inventory: IInventory, index: Int, x: Int, y: Int, override def mayPlace(stack: ItemStack): Boolean = { if (!inventory.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false + if (slot == common.Slot.None || tier == common.Tier.None) return false if (slot == common.Slot.Any && tier == common.Tier.Any) return true // Special case: tool slots fit everything. if (slot == common.Slot.Tool) return true diff --git a/src/main/scala/li/cil/oc/common/container/Disassembler.scala b/src/main/scala/li/cil/oc/common/container/Disassembler.scala index e4f64d8ca7..3aa743908a 100644 --- a/src/main/scala/li/cil/oc/common/container/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/container/Disassembler.scala @@ -23,7 +23,6 @@ class Disassembler(selfType: ContainerType[_ <: Disassembler], id: Int, playerIn addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 80, 35, getHostClass, "ocitem", Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false allowDisassembling(stack) && (((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && ItemUtils.getIngredients(playerInventory.player.level.getRecipeManager, stack).nonEmpty) || diff --git a/src/main/scala/li/cil/oc/common/container/Drone.scala b/src/main/scala/li/cil/oc/common/container/Drone.scala index 04aaf8a86a..34326a219d 100644 --- a/src/main/scala/li/cil/oc/common/container/Drone.scala +++ b/src/main/scala/li/cil/oc/common/container/Drone.scala @@ -100,6 +100,7 @@ class Drone(selfType: ContainerType[_ <: Drone], id: Int, playerInventory: Playe @OnlyIn(Dist.CLIENT) override def isActive = isValid && super.isActive + @OnlyIn(Dist.CLIENT) override def getBackgroundLocation = if (isValid) super.getBackgroundLocation else Textures.Icons.get(common.Tier.None) diff --git a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala index 2f80b6f4e0..9738fda3ad 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -10,6 +10,8 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, host: Class[_ <: EnvironmentHost], val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) @@ -21,6 +23,7 @@ class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, in else mainTier } + @OnlyIn(Dist.CLIENT) def tierIcon: ResourceLocation = Textures.Icons.get(tier) def slot: String = { @@ -29,8 +32,10 @@ class DynamicComponentSlot(val agentContainer: Player, inventory: IInventory, in else common.Slot.None } + @OnlyIn(Dist.CLIENT) override def hasBackground: Boolean = Textures.Icons.get(slot) != null + @OnlyIn(Dist.CLIENT) override def getBackgroundLocation: ResourceLocation = Option(Textures.Icons.get(slot)).getOrElse(super.getBackgroundLocation) override def getMaxStackSize: Int = diff --git a/src/main/scala/li/cil/oc/common/container/Printer.scala b/src/main/scala/li/cil/oc/common/container/Printer.scala index 57617c976c..3be5e425aa 100644 --- a/src/main/scala/li/cil/oc/common/container/Printer.scala +++ b/src/main/scala/li/cil/oc/common/container/Printer.scala @@ -18,14 +18,12 @@ class Printer(selfType: ContainerType[_ <: Printer], id: Int, playerInventory: P addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 19, getHostClass, Slot.Filtered, Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false PrintData.materialValue(stack) > 0 } }) addSlot(new StaticComponentSlot(this, otherInventory, slots.size, 18, 51, getHostClass, Slot.Filtered, Tier.Any) { override def mayPlace(stack: ItemStack): Boolean = { if (!container.canPlaceItem(getSlotIndex, stack)) return false - if (!isActive) return false PrintData.inkValue(stack) > 0 } }) diff --git a/src/main/scala/li/cil/oc/common/container/Robot.scala b/src/main/scala/li/cil/oc/common/container/Robot.scala index 10df54508f..7bab9f8c50 100644 --- a/src/main/scala/li/cil/oc/common/container/Robot.scala +++ b/src/main/scala/li/cil/oc/common/container/Robot.scala @@ -168,7 +168,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe } def selectedSlot = selectedSlotData.get - class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, var enabled: Boolean) + class InventorySlot(container: Player, inventory: IInventory, index: Int, x: Int, y: Int, enabled: Boolean) extends StaticComponentSlot(container, inventory, index, x, y, getHostClass, common.Slot.Any, common.Tier.Any) { def isValid: Boolean = getSlotIndex >= 4 && getSlotIndex < 4 + info.mainInvSize @@ -176,6 +176,7 @@ class Robot(selfType: ContainerType[_ <: Robot], id: Int, playerInventory: Playe @OnlyIn(Dist.CLIENT) override def isActive: Boolean = enabled && isValid && super.isActive + @OnlyIn(Dist.CLIENT) override def getBackgroundLocation: ResourceLocation = if (isValid) super.getBackgroundLocation else Textures.Icons.get(common.Tier.None) diff --git a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala index 46a64e14ca..d9180fe6cb 100644 --- a/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/StaticComponentSlot.scala @@ -11,7 +11,8 @@ import net.minecraftforge.api.distmarker.OnlyIn class StaticComponentSlot(val agentContainer: Player, inventory: IInventory, index: Int, x: Int, y: Int, host: Class[_ <: EnvironmentHost], val slot: String, val tier: Int) extends ComponentSlot(inventory, index, x, y, host) { - val tierIcon = Textures.Icons.get(tier) + @OnlyIn(Dist.CLIENT) + def tierIcon = Textures.Icons.get(tier) @OnlyIn(Dist.CLIENT) override def getBackgroundLocation: ResourceLocation = Textures.Icons.get(slot) diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index 7e993009cd..4e4a463588 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -234,7 +234,7 @@ class Tablet(props: Properties) extends Item(props) with IForgeItem with traits. Tablet.get(stack, player).components.collect { case Some(buffer: api.internal.TextBuffer) => buffer }.headOption match { - case Some(buffer: api.internal.TextBuffer) => Minecraft.getInstance.pushGuiLayer(new gui.Screen(buffer, true, () => true, () => buffer.isRenderingEnabled)) + case Some(buffer: api.internal.TextBuffer) => showGui(buffer) case _ => } } @@ -244,6 +244,11 @@ class Tablet(props: Properties) extends Item(props) with IForgeItem with traits. } } + @OnlyIn(Dist.CLIENT) + private def showGui(buffer: api.internal.TextBuffer) { + Minecraft.getInstance.pushGuiLayer(new gui.Screen(buffer, true, () => true, () => buffer.isRenderingEnabled)) + } + override def maxCharge(stack: ItemStack): Double = new TabletData(stack).maxEnergy override def getCharge(stack: ItemStack): Double = new TabletData(stack).energy diff --git a/src/main/scala/li/cil/oc/common/item/Terminal.scala b/src/main/scala/li/cil/oc/common/item/Terminal.scala index 7b582ca53f..07444bf7b7 100644 --- a/src/main/scala/li/cil/oc/common/item/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/item/Terminal.scala @@ -75,13 +75,7 @@ class Terminal(props: Properties) extends Item(props) with IForgeItem with trait case rack: TileEntity with api.internal.Rack => { def inRange = player.isAlive && !rack.isRemoved && player.distanceToSqr(rack.x + 0.5, rack.y + 0.5, rack.z + 0.5) < term.range * term.range if (inRange) { - if (term.sidedKeys.contains(key)) Minecraft.getInstance.pushGuiLayer(new gui.Screen(term.buffer, true, () => true, () => { - // Check if someone else bound a term to our server. - if (stack.getTag.getString(Settings.namespace + "key") != key) Minecraft.getInstance.popGuiLayer - // Check whether we're still in range. - if (!inRange) Minecraft.getInstance.popGuiLayer - true - })) + if (term.sidedKeys.contains(key)) showGui(stack, key, term, () => inRange) else player.displayClientMessage(Localization.Terminal.InvalidKey, true) } else player.displayClientMessage(Localization.Terminal.OutOfRange, true) @@ -98,4 +92,15 @@ class Terminal(props: Properties) extends Item(props) with IForgeItem with trait } super.use(stack, world, player) } + + @OnlyIn(Dist.CLIENT) + private def showGui(stack: ItemStack, key: String, term: component.TerminalServer, inRange: () => Boolean) { + Minecraft.getInstance.pushGuiLayer(new gui.Screen(term.buffer, true, () => true, () => { + // Check if someone else bound a term to our server. + if (stack.getTag.getString(Settings.namespace + "key") != key) Minecraft.getInstance.popGuiLayer + // Check whether we're still in range. + if (!inRange()) Minecraft.getInstance.popGuiLayer + true + })) + } } diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala index 20a654f6e1..b07e363ac9 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -11,7 +11,6 @@ import li.cil.oc.util.Tooltip import net.minecraft.client.Minecraft import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.player.PlayerEntity -import net.minecraft.entity.player.ServerPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.ActionResultType @@ -53,12 +52,14 @@ trait FileSystemLike extends SimpleItem { override def use(stack: ItemStack, world: World, player: PlayerEntity): ActionResult[ItemStack] = { if (!player.isCrouching && (!stack.hasTag || !stack.getTag.contains(Settings.namespace + "lootFactory"))) { - if (!world.isClientSide) player match { - case srvPlr: ServerPlayerEntity => Minecraft.getInstance.pushGuiLayer(new gui.Drive(player.inventory, () => stack)) - case _ => - } + if (world.isClientSide) showGui(stack, player) player.swing(Hand.MAIN_HAND) } new ActionResult(ActionResultType.sidedSuccess(world.isClientSide), stack) } + + @OnlyIn(Dist.CLIENT) + private def showGui(stack: ItemStack, player: PlayerEntity) { + Minecraft.getInstance.pushGuiLayer(new gui.Drive(player.inventory, () => stack)) + } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index 1bea7cc407..6804de14c2 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -44,8 +44,11 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.world.World +import net.minecraftforge.api.distmarker.Dist +import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.world.ForgeChunkManager +import net.minecraftforge.fml.DistExecutor object ModOpenComputers extends ModProxy { override def getMod = Mods.OpenComputers @@ -306,6 +309,17 @@ object ModOpenComputers extends ModProxy { ModOpenComputers.hasRedstoneCardT2 = true } + api.Nanomachines.addProvider(DisintegrationProvider) + api.Nanomachines.addProvider(HungryProvider) + api.Nanomachines.addProvider(ParticleProvider) + api.Nanomachines.addProvider(PotionProvider) + api.Nanomachines.addProvider(MagnetProvider) + + DistExecutor.runWhenOn(Dist.CLIENT, () => () => initializeClient()) + } + + @OnlyIn(Dist.CLIENT) + private def initializeClient() { api.Manual.addProvider(DefinitionPathProvider) api.Manual.addProvider(new ResourceContentProvider(Settings.resourceDomain, "doc/")) api.Manual.addProvider("", TextureImageProvider) @@ -316,12 +330,6 @@ object ModOpenComputers extends ModProxy { api.Manual.addTab(new TextureTabIconRenderer(Textures.GUI.ManualHome), "oc:gui.Manual.Home", "%LANGUAGE%/index.md") api.Manual.addTab(new ItemStackTabIconRenderer(api.Items.get("case1").createItemStack(1)), "oc:gui.Manual.Blocks", "%LANGUAGE%/block/index.md") api.Manual.addTab(new ItemStackTabIconRenderer(api.Items.get("cpu1").createItemStack(1)), "oc:gui.Manual.Items", "%LANGUAGE%/item/index.md") - - api.Nanomachines.addProvider(DisintegrationProvider) - api.Nanomachines.addProvider(HungryProvider) - api.Nanomachines.addProvider(ParticleProvider) - api.Nanomachines.addProvider(PotionProvider) - api.Nanomachines.addProvider(MagnetProvider) } protected[oc] var hasRedstoneCardT2 = false From 2c1cc30fa84f283405c3cee04ee36c1e7727584d Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:20:43 +0200 Subject: [PATCH 126/159] Fix access transformer not running outside of dev --- build.gradle | 2 +- .../resources/META-INF/{oc_at.cfg => accesstransformer.cfg} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/main/resources/META-INF/{oc_at.cfg => accesstransformer.cfg} (100%) diff --git a/build.gradle b/build.gradle index f9c40a0a4e..981c063717 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ version = "MC${config.minecraft.version}-${project.version}" minecraft { mappings channel: "official", version: config.minecraft.version - accessTransformer = file("src/main/resources/META-INF/oc_at.cfg") + accessTransformer = file("src/main/resources/META-INF/accesstransformer.cfg") runs { client { diff --git a/src/main/resources/META-INF/oc_at.cfg b/src/main/resources/META-INF/accesstransformer.cfg similarity index 100% rename from src/main/resources/META-INF/oc_at.cfg rename to src/main/resources/META-INF/accesstransformer.cfg From 49a2bd13ff387aeec4ee44c92bb15ff3dd5e56bc Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:09:37 +0200 Subject: [PATCH 127/159] Fix AE2 crash on load because of scala object --- src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala index 5eb260a9a3..9e4bea2e64 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/AEUtil.scala @@ -18,12 +18,16 @@ import net.minecraftforge.forgespi.language.MavenVersionAdapter import org.apache.maven.artifact.versioning.VersionRange @AEAddon -object AEUtil extends IAEAddon { +class AEUtil extends IAEAddon { + override def onAPIAvailable(aeApi: IAppEngApi) = AEUtil.onAPIAvailable(aeApi) +} + +object AEUtil { var aeApi: Option[IAppEngApi] = None private def optionToScala[T](opt: Optional[T]): Option[T] = if (opt.isPresent) Some(opt.get) else None - def onAPIAvailable(aeApi: IAppEngApi) { + private def onAPIAvailable(aeApi: IAppEngApi) { AEUtil.aeApi = Some(aeApi) itemStorageChannel = aeApi.storage.getStorageChannel[IAEItemStack, IItemStorageChannel](classOf[IItemStorageChannel]) fluidStorageChannel = aeApi.storage.getStorageChannel[IAEFluidStack, IFluidStorageChannel](classOf[IFluidStorageChannel]) From 6fab6951fa4b37df527622c41d22e1881b2a621e Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:20:00 +0200 Subject: [PATCH 128/159] Fix WAILA crash due to bad ResourceLocation --- .../scala/li/cil/oc/integration/waila/BlockDataProvider.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala index 9934cd430a..5c0dd6fbe3 100644 --- a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala +++ b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala @@ -30,7 +30,7 @@ import net.minecraftforge.common.util.Constants.NBT object BlockDataProvider extends IWailaPlugin with IServerDataProvider[TileEntity] with IComponentProvider { val ConfigAddress = new ResourceLocation(OpenComputers.ID, "oc.address") val ConfigEnergy = new ResourceLocation(OpenComputers.ID, "oc.energy") - val ConfigComponentName = new ResourceLocation(OpenComputers.ID, "oc.componentName") + val ConfigComponentName = new ResourceLocation(OpenComputers.ID, "oc.componentname") def register(registrar: IRegistrar) { registrar.registerComponentProvider(this, TooltipPosition.BODY, classOf[SimpleBlock]) From e45a5f423188a89c0678b86b2e3ba76eb8871985 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:43:18 +0200 Subject: [PATCH 129/159] Fix WAILA plugin not getting loaded --- .../li/cil/oc/integration/waila/BlockDataProvider.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala index 5c0dd6fbe3..cc8b8f3493 100644 --- a/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala +++ b/src/main/scala/li/cil/oc/integration/waila/BlockDataProvider.scala @@ -27,12 +27,16 @@ import net.minecraft.world.World import net.minecraftforge.common.util.Constants.NBT @WailaPlugin -object BlockDataProvider extends IWailaPlugin with IServerDataProvider[TileEntity] with IComponentProvider { +class BlockDataProvider extends IWailaPlugin { + def register(registrar: IRegistrar) = BlockDataProvider.register(registrar) +} + +object BlockDataProvider extends IServerDataProvider[TileEntity] with IComponentProvider { val ConfigAddress = new ResourceLocation(OpenComputers.ID, "oc.address") val ConfigEnergy = new ResourceLocation(OpenComputers.ID, "oc.energy") val ConfigComponentName = new ResourceLocation(OpenComputers.ID, "oc.componentname") - def register(registrar: IRegistrar) { + private def register(registrar: IRegistrar) { registrar.registerComponentProvider(this, TooltipPosition.BODY, classOf[SimpleBlock]) registrar.registerBlockDataProvider(this, classOf[li.cil.oc.api.network.Environment]) From 9b805964f8f76c13eeeea7c06714ea0d1c88dd04 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:34:53 +0200 Subject: [PATCH 130/159] Fix getOwner call in EnderStorage driver --- .../oc/integration/enderstorage/DriverFrequencyOwner.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java b/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java index fa0c63bc1f..fb0979fd7f 100644 --- a/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java +++ b/src/main/scala/li/cil/oc/integration/enderstorage/DriverFrequencyOwner.java @@ -84,9 +84,10 @@ public Object[] setFrequency(final Context context, final Arguments args) { return null; } - @Callback(doc = "function():string -- Get the name of the owner, which is usually a player's name or 'global'.") + @Callback(doc = "function():string or nil -- Get the name of the owner, which is usually a player's name or nil.") public Object[] getOwner(final Context context, final Arguments args) { - return new Object[]{tileEntity.getFrequency().ownerName.getString()}; + Frequency freq = tileEntity.getFrequency(); + return new Object[]{freq.hasOwner() ? freq.ownerName.getString() : null}; } @Callback(doc = "function():table -- Get the currently set frequency as a table of color names.") From b2d32e2802289aea9926b233bc6f051b941731ac Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Fri, 28 Oct 2022 23:29:16 +0200 Subject: [PATCH 131/159] Fix stack overflow when connecting to CC devices --- .../li/cil/oc/integration/computercraft/DriverPeripheral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java b/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java index 7f69d2ec1c..9eb4402f5e 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java +++ b/src/main/scala/li/cil/oc/integration/computercraft/DriverPeripheral.java @@ -132,7 +132,7 @@ public Object[] invoke(final String name, final Context context, final Arguments @Override public void onConnect(final Node node) { super.onConnect(node); - if (node.host() instanceof Context) { + if (node.host() instanceof Context && !accesses.containsKey(node.address())) { final FakeComputerAccess access = new FakeComputerAccess(this, (Context) node.host()); accesses.put(node.address(), access); peripheral.attach(access); From 47422ad8889337c71fe9dae1f9ceb048f8b52d9b Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 29 Oct 2022 00:15:01 +0200 Subject: [PATCH 132/159] Fix RelayPeripheral wrapping results in a table --- .../li/cil/oc/integration/computercraft/RelayPeripheral.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala index 042ae36829..abdf304b49 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/RelayPeripheral.scala @@ -116,7 +116,7 @@ class RelayPeripheral(val relay: Relay) extends IDynamicPeripheral { override def getMethodNames = methodNames override def callMethod(computer: IComputerAccess, context: ILuaContext, method: Int, arguments: IArguments) = - try MethodResult.of(methods(methodNames(method))(computer, context, arguments.getAll)) catch { + try MethodResult.of(methods(methodNames(method))(computer, context, arguments.getAll): _*) catch { case e: LuaException => throw e case t: Throwable => t.printStackTrace() From 980d0f45464fded2bbc8606b6092116eeccdf1a4 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sat, 29 Oct 2022 00:47:33 +0200 Subject: [PATCH 133/159] Fix layout of forwarded modem_message event --- src/main/scala/li/cil/oc/common/tileentity/Relay.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala index ac7f67c7c3..2c67d78bdd 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -134,11 +134,14 @@ class Relay(selfType: TileEntityType[_ <: Relay]) extends TileEntity(selfType) w def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { val address = s"cc${computer.getID}_${computer.getAttachmentName}" - if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) - computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) { + val header = Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) + val payload = args.map { case x: Array[Byte] => new String(x, Charsets.UTF_8) case x => x - }: _*)) + } + computer.queueEvent("modem_message", Array((header :+ (if (payload.length > 1) payload else payload(0))): _*): _*) + } } } } From 6b10e8d6d277a3c9949c1cc58da63811e8dbe492 Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 30 Oct 2022 20:40:32 +0100 Subject: [PATCH 134/159] Switch OC to Scorge (Scala 2.13.4) --- build.gradle | 13 ++++- build.properties | 1 + src/main/resources/META-INF/mods.toml | 5 +- src/main/scala/li/cil/oc/OpenComputers.scala | 16 +++-- src/main/scala/li/cil/oc/Settings.scala | 36 ++++++------ src/main/scala/li/cil/oc/client/Proxy.scala | 14 +++-- src/main/scala/li/cil/oc/client/Sound.scala | 4 +- .../scala/li/cil/oc/client/Textures.scala | 4 -- .../scala/li/cil/oc/client/gui/GuiTypes.java | 4 -- .../scala/li/cil/oc/client/gui/Manual.scala | 2 +- .../scala/li/cil/oc/client/gui/Robot.scala | 2 +- .../oc/client/renderer/block/DroneModel.scala | 6 +- .../renderer/block/ModelInitialization.scala | 4 -- .../renderer/block/NetSplitterModel.scala | 4 -- .../renderer/tileentity/RobotRenderer.scala | 4 +- src/main/scala/li/cil/oc/common/Proxy.scala | 58 +++++++++---------- .../cil/oc/common/component/TextBuffer.scala | 6 +- .../component/traits/VideoRamRasterizer.scala | 2 +- .../oc/common/container/ContainerTypes.java | 3 - .../li/cil/oc/common/container/Player.scala | 16 ++++- .../li/cil/oc/common/entity/EntityTypes.java | 3 - .../common/nanomachines/ControllerImpl.scala | 4 +- .../oc/common/recipe/RecipeSerializers.java | 3 - .../common/template/AssemblerTemplates.scala | 2 +- .../cil/oc/common/tileentity/DiskDrive.scala | 2 +- .../cil/oc/common/tileentity/Hologram.scala | 4 +- .../common/tileentity/Microcontroller.scala | 4 +- .../oc/common/tileentity/NetSplitter.scala | 2 +- .../li/cil/oc/common/tileentity/Printer.scala | 4 +- .../li/cil/oc/common/tileentity/Raid.scala | 4 +- .../cil/oc/common/tileentity/RobotProxy.scala | 2 +- .../oc/common/tileentity/TileEntityTypes.java | 3 - .../traits/BundledRedstoneAware.scala | 2 +- .../integration/appeng/DriverExportBus.scala | 6 +- .../integration/appeng/NetworkControl.scala | 10 ++-- .../appeng/PartEnvironmentBase.scala | 4 +- .../integration/jei/ManualUsageHandler.scala | 2 +- .../oc/integration/util/BundledRedstone.scala | 2 +- .../cil/oc/integration/waila/ModWaila.scala | 2 +- .../scala/li/cil/oc/server/agent/Player.scala | 22 +++---- .../li/cil/oc/server/component/Agent.scala | 2 +- .../cil/oc/server/component/DebugCard.scala | 16 ++--- .../li/cil/oc/server/component/EEPROM.scala | 10 ++-- .../cil/oc/server/component/FileSystem.scala | 4 +- .../li/cil/oc/server/component/Geolyzer.scala | 14 ++--- .../oc/server/component/GraphicsCard.scala | 34 +++++------ .../oc/server/component/InternetCard.scala | 16 ++--- .../cil/oc/server/component/LinkedCard.scala | 2 +- .../server/component/RedstoneSignaller.scala | 2 +- .../li/cil/oc/server/component/Robot.scala | 14 ++--- .../server/component/UpgradeExperience.scala | 8 +-- .../server/component/UpgradeGenerator.scala | 8 +-- .../oc/server/component/UpgradeLeash.scala | 4 +- .../server/component/UpgradeNavigation.scala | 4 +- .../cil/oc/server/component/UpgradeSign.scala | 6 +- .../component/traits/InventoryAnalytics.scala | 2 +- .../component/traits/InventoryTransfer.scala | 6 +- .../traits/InventoryWorldControlMk2.scala | 2 +- .../server/component/traits/TankControl.scala | 4 +- .../traits/TankInventoryControl.scala | 22 +++---- .../component/traits/TankWorldControl.scala | 18 +++--- .../traits/WorldInventoryAnalytics.scala | 10 ++-- .../component/traits/WorldTankAnalytics.scala | 8 +-- .../li/cil/oc/server/driver/Registry.scala | 8 +-- .../scala/li/cil/oc/server/fs/Buffered.scala | 2 +- .../li/cil/oc/server/loot/LootFunctions.java | 8 +-- .../cil/oc/server/machine/ArgumentsImpl.scala | 5 +- .../li/cil/oc/server/machine/Machine.scala | 4 +- .../li/cil/oc/server/network/Component.scala | 1 + .../li/cil/oc/server/network/Connector.scala | 2 +- .../cil/oc/server/network/DebugNetwork.scala | 2 +- .../li/cil/oc/server/network/Network.scala | 10 +++- .../oc/server/network/QuantumNetwork.scala | 2 +- .../li/cil/oc/util/ExtendedLuaState.scala | 6 +- .../scala/li/cil/oc/util/ExtendedNBT.scala | 2 +- .../scala/li/cil/oc/util/ResultWrapper.scala | 2 + .../scala/li/cil/oc/util/RotationHelper.scala | 2 +- .../scala/li/cil/oc/util/ScalaClosure.scala | 4 +- .../li/cil/oc/util/ThreadPoolFactory.scala | 3 - 79 files changed, 282 insertions(+), 288 deletions(-) diff --git a/build.gradle b/build.gradle index 981c063717..b3029396d3 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,12 @@ repositories { includeGroup "mekanism" } } + maven { + url "https://proxy-maven.covers1624.net/" + content { + includeModule "net.minecraftforge", "Scorge" + } + } mavenCentral() } @@ -121,8 +127,9 @@ dependencies { var jeiSlug = "jei-${config.minecraft.version}" minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" + compileOnly "org.scala-lang:scala-library:2.13.4" + implementation "net.minecraftforge:Scorge:${config.scorge.version}" embedded "com.typesafe:config:1.2.1" - embedded "org.scala-lang:scala-library:2.12.+" compileOnly fg.deobf("li.cil.tis3d:tis3d-1.16.5-forge:${config.tis3d.version}") compileOnly fg.deobf("curse.maven:hwyla-${config.hwyla.projectId}:${config.hwyla.fileId}") @@ -158,11 +165,11 @@ dependencies { } processResources { - def reducedForgeVer = config.forge.version.replaceAll(/(\d+\.\d+)(\.\d+)/, "\$1") + def reducedScorgeVer = config.scorge.version.replaceAll(/(\d+\.\d+)(\.\d+)/, "\$1") from(sourceSets.main.resources.srcDirs) { duplicatesStrategy = "include" include 'META-INF/mods.toml' - expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'fversion':reducedForgeVer + expand 'version':project.simpleVersion, 'mcversion':config.minecraft.version, 'sversion':reducedScorgeVer } from(sourceSets.main.resources.srcDirs) { duplicatesStrategy = "include" diff --git a/build.properties b/build.properties index e3ebdfe583..b70cfec987 100644 --- a/build.properties +++ b/build.properties @@ -1,5 +1,6 @@ minecraft.version=1.16.5 forge.version=36.2.34 +scorge.version=3.1.3 mod.name=OpenComputers mod.group=li.cil.oc diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 6a394bc339..b043f05888 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,10 +1,11 @@ -modLoader="javafml" -loaderVersion="[${fversion},)" +modLoader="scorge" +loaderVersion="[${sversion},)" license="MIT, CC0 (see LICENSE)" issueTrackerURL="https://github.com/MightyPirates/OpenComputers/issues" [[mods]] modId="opencomputers" +entryClass="li.cil.oc.OpenComputers" version="${version}" displayName="OpenComputers" displayURL="http://oc.cil.li/" diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 0f57662f38..1fc79c3e81 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -6,6 +6,7 @@ import li.cil.oc.common.IMC import li.cil.oc.common.Proxy import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items +import li.cil.oc.util.ThreadPoolFactory import net.minecraft.block.Block import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item @@ -18,11 +19,10 @@ import net.minecraftforge.forgespi.Environment import net.minecraftforge.fml.InterModComms import net.minecraftforge.fml.ModContainer import net.minecraftforge.fml.ModLoadingContext -import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext import net.minecraftforge.fml.loading.FMLPaths import net.minecraftforge.fml.network.simple.SimpleChannel +import net.minecraftforge.scorge.lang.ScorgeModLoadingContext import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger @@ -53,19 +53,19 @@ object OpenComputers { } } -@Mod(OpenComputers.ID) class OpenComputers { val modContainer: ModContainer = ModLoadingContext.get.getActiveContainer val Version = modContainer.getModInfo.getVersion - FMLJavaModLoadingContext.get.getModEventBus.register(this) + ScorgeModLoadingContext.get.getModEventBus.register(this) OpenComputers.instance = Some(this) MinecraftForge.EVENT_BUS.register(OpenComputers.proxy) - FMLJavaModLoadingContext.get.getModEventBus.register(OpenComputers.proxy) + ScorgeModLoadingContext.get.getModEventBus.register(OpenComputers.proxy) Settings.load(FMLPaths.CONFIGDIR.get().resolve(Paths.get("opencomputers", "settings.conf")).toFile()) OpenComputers.proxy.preInit() + MinecraftForge.EVENT_BUS.register(ThreadPoolFactory) @SubscribeEvent def registerBlocks(e: RegistryEvent.Register[Block]) { @@ -80,10 +80,8 @@ class OpenComputers { @SubscribeEvent def imc(e: InterModProcessEvent): Unit = { // Technically requires synchronization because IMC.sendTo doesn't check the loading stage. - e.enqueueWork(() => { + e.enqueueWork((() => { InterModComms.getMessages(OpenComputers.ID).sequential.iterator.foreach(IMC.handleMessage) - - Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) - }) + }): Runnable) } } diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index b1b9ddd833..93d043869b 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -20,10 +20,10 @@ import org.apache.commons.codec.binary.Hex import org.apache.maven.artifact.versioning.DefaultArtifactVersion import org.apache.maven.artifact.versioning.VersionRange -import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable import scala.io.Codec import scala.io.Source +import scala.jdk.CollectionConverters._ import scala.util.matching.Regex class Settings(val config: Config) { @@ -44,7 +44,7 @@ class Settings(val config: Config) { val beepSampleRate = config.getInt("client.beepSampleRate") val beepAmplitude = config.getInt("client.beepVolume") max 0 min Byte.MaxValue val beepRadius = config.getDouble("client.beepRadius").toFloat max 1 min 32 - val nanomachineHudPos = Array(config.getDoubleList("client.nanomachineHudPos"): _*) match { + val nanomachineHudPos = config.getDoubleList("client.nanomachineHudPos").asScala.toArray match { case Array(x, y) => (x: Double, y: Double) case _ => @@ -60,14 +60,14 @@ class Settings(val config: Config) { val startupDelay = config.getDouble("computer.startupDelay") max 0.05 val eepromSize = config.getInt("computer.eepromSize") max 0 val eepromDataSize = config.getInt("computer.eepromDataSize") max 0 - val cpuComponentSupport = Array(config.getIntList("computer.cpuComponentCount"): _*) match { + val cpuComponentSupport = config.getIntList("computer.cpuComponentCount").asScala.toArray match { case Array(tier1, tier2, tier3, tierCreative) => Array(tier1: Int, tier2: Int, tier3: Int, tierCreative: Int) case _ => OpenComputers.log.warn("Bad number of CPU component counts, ignoring.") Array(8, 12, 16, 1024) } - val callBudgets = Array(config.getDoubleList("computer.callBudgets"): _*) match { + val callBudgets = config.getDoubleList("computer.callBudgets").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) case _ => @@ -85,7 +85,7 @@ class Settings(val config: Config) { val allowGC = config.getBoolean("computer.lua.allowGC") val enableLua53 = config.getBoolean("computer.lua.enableLua53") val defaultLua53 = config.getBoolean("computer.lua.defaultLua53") - val ramSizes = Array(config.getIntList("computer.lua.ramSizes"): _*) match { + val ramSizes = config.getIntList("computer.lua.ramSizes").asScala.toArray match { case Array(tier1, tier2, tier3, tier4, tier5, tier6) => Array(tier1: Int, tier2: Int, tier3: Int, tier4: Int, tier5: Int, tier6: Int) case _ => @@ -107,7 +107,7 @@ class Settings(val config: Config) { val itemDamageRate = config.getDouble("robot.itemDamageRate") max 0 min 1 val nameFormat = config.getString("robot.nameFormat") val uuidFormat = config.getString("robot.uuidFormat") - val upgradeFlightHeight = Array(config.getIntList("robot.upgradeFlightHeight"): _*) match { + val upgradeFlightHeight = config.getIntList("robot.upgradeFlightHeight").asScala.toArray match { case Array(tier1, tier2) => Array(tier1: Int, tier2: Int) case _ => @@ -165,7 +165,7 @@ class Settings(val config: Config) { val bufferRobot = config.getDouble("power.buffer.robot") max 0 val bufferConverter = config.getDouble("power.buffer.converter") max 0 val bufferDistributor = config.getDouble("power.buffer.distributor") max 0 - val bufferCapacitorUpgrades = Array(config.getDoubleList("power.buffer.batteryUpgrades"): _*) match { + val bufferCapacitorUpgrades = config.getDoubleList("power.buffer.batteryUpgrades").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) case _ => @@ -196,7 +196,7 @@ class Settings(val config: Config) { val robotTurnCost = config.getDouble("power.cost.robotTurn") max 0 val robotMoveCost = config.getDouble("power.cost.robotMove") max 0 val robotExhaustionCost = config.getDouble("power.cost.robotExhaustion") max 0 - val wirelessCostPerRange = Array(config.getDoubleList("power.cost.wirelessCostPerRange"): _*) match { + val wirelessCostPerRange = config.getDoubleList("power.cost.wirelessCostPerRange").asScala.toArray match { case Array(tier1, tier2) => Array((tier1: Double) max 0.0, (tier2: Double) max 0.0) case _ => @@ -236,7 +236,7 @@ class Settings(val config: Config) { // power.rate val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 val assemblerRate = config.getDouble("power.rate.assembler") max 0 - val caseRate = (Array(config.getDoubleList("power.rate.case"): _*) match { + val caseRate = (config.getDoubleList("power.rate.case").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) case _ => @@ -276,14 +276,14 @@ class Settings(val config: Config) { // filesystem val fileCost = config.getInt("filesystem.fileCost") max 0 val bufferChanges = config.getBoolean("filesystem.bufferChanges") - val hddSizes = Array(config.getIntList("filesystem.hddSizes"): _*) match { + val hddSizes = config.getIntList("filesystem.hddSizes").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Int, tier2: Int, tier3: Int) case _ => OpenComputers.log.warn("Bad number of HDD sizes, ignoring.") Array(1024, 2048, 4096) } - val hddPlatterCounts = Array(config.getIntList("filesystem.hddPlatterCounts"): _*) match { + val hddPlatterCounts = config.getIntList("filesystem.hddPlatterCounts").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Int, tier2: Int, tier3: Int) case _ => @@ -302,8 +302,8 @@ class Settings(val config: Config) { val httpEnabled = config.getBoolean("internet.enableHttp") val httpHeadersEnabled = config.getBoolean("internet.enableHttpHeaders") val tcpEnabled = config.getBoolean("internet.enableTcp") - val httpHostBlacklist = Array(config.getStringList("internet.blacklist").map(new Settings.AddressValidator(_)): _*) - val httpHostWhitelist = Array(config.getStringList("internet.whitelist").map(new Settings.AddressValidator(_)): _*) + val httpHostBlacklist = config.getStringList("internet.blacklist").asScala.map(new Settings.AddressValidator(_)).toArray + val httpHostWhitelist = config.getStringList("internet.whitelist").asScala.map(new Settings.AddressValidator(_)).toArray val httpTimeout = (config.getInt("internet.requestTimeout") max 0) * 1000 val maxConnections = config.getInt("internet.maxTcpConnections") max 0 val internetThreads = config.getInt("internet.threads") max 1 @@ -319,14 +319,14 @@ class Settings(val config: Config) { // ----------------------------------------------------------------------- // // hologram - val hologramMaxScaleByTier = Array(config.getDoubleList("hologram.maxScale"): _*) match { + val hologramMaxScaleByTier = config.getDoubleList("hologram.maxScale").asScala.toArray match { case Array(tier1, tier2) => Array((tier1: Double) max 1.0, (tier2: Double) max 1.0) case _ => OpenComputers.log.warn("Bad number of hologram max scales, ignoring.") Array(3.0, 4.0) } - val hologramMaxTranslationByTier = Array(config.getDoubleList("hologram.maxTranslation"): _*) match { + val hologramMaxTranslationByTier = config.getDoubleList("hologram.maxTranslation").asScala.toArray match { case Array(tier1, tier2) => Array((tier1: Double) max 0.0, (tier2: Double) max 0.0) case _ => @@ -344,14 +344,14 @@ class Settings(val config: Config) { val maxNetworkPacketSize = config.getInt("misc.maxNetworkPacketSize") max 0 // Need at least 4 for nanomachine protocol. Because I can! val maxNetworkPacketParts = config.getInt("misc.maxNetworkPacketParts") max 4 - val maxOpenPorts = Array(config.getIntList("misc.maxOpenPorts"): _*) match { + val maxOpenPorts = config.getIntList("misc.maxOpenPorts").asScala.toArray match { case Array(wired, tier1, tier2) => Array((wired: Int) max 0, (tier1: Int) max 0, (tier2: Int) max 0) case _ => OpenComputers.log.warn("Bad number of max open ports, ignoring.") Array(16, 1, 16) } - val maxWirelessRange = Array(config.getDoubleList("misc.maxWirelessRange"): _*) match { + val maxWirelessRange = config.getDoubleList("misc.maxWirelessRange").asScala.toArray match { case Array(tier1, tier2) => Array((tier1: Double) max 0.0, (tier2: Double) max 0.0) case _ => @@ -471,7 +471,7 @@ class Settings(val config: Config) { val maxSignalQueueSize: Int = (if (config.hasPath("computer.maxSignalQueueSize")) config.getInt("computer.maxSignalQueueSize") else 256) min 256 // >= 1.7.6 - val vramSizes: Array[Double] = Array(config.getDoubleList("gpu.vramSizes"): _*) match { + val vramSizes: Array[Double] = config.getDoubleList("gpu.vramSizes").asScala.toArray match { case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) case _ => OpenComputers.log.warn("Bad number of VRAM sizes (expected 3), ignoring.") diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index a132dad559..a9ea5fe644 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -5,12 +5,14 @@ import com.mojang.blaze3d.systems.RenderSystem import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.client +import li.cil.oc.client.gui.GuiTypes import li.cil.oc.client.renderer.HighlightRenderer import li.cil.oc.client.renderer.MFUTargetRenderer import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer import li.cil.oc.client.renderer.block.ModelInitialization +import li.cil.oc.client.renderer.block.NetSplitterModel import li.cil.oc.client.renderer.entity.DroneRenderer import li.cil.oc.client.renderer.tileentity._ import li.cil.oc.common @@ -36,8 +38,12 @@ import net.minecraftforge.fml.client.registry.RenderingRegistry import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent import net.minecraftforge.fml.network.NetworkRegistry -@Deprecated private[oc] class Proxy extends CommonProxy { + modBus.register(classOf[GuiTypes]) + modBus.register(ModelInitialization) + modBus.register(NetSplitterModel) + modBus.register(Textures) + override def preInit() { super.preInit() @@ -49,7 +55,7 @@ private[oc] class Proxy extends CommonProxy { CommonPacketHandler.clientHandler = PacketHandler - e.enqueueWork(() => { + e.enqueueWork((() => { ModelInitialization.preInit() ColorHandler.init() @@ -89,9 +95,7 @@ private[oc] class Proxy extends CommonProxy { MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) MinecraftForge.EVENT_BUS.register(Audio) MinecraftForge.EVENT_BUS.register(HologramRenderer) - - Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) - }) + }): Runnable) runOnRenderThread(() => MinecraftForge.EVENT_BUS.register(TextBufferRenderCache)) } diff --git a/src/main/scala/li/cil/oc/client/Sound.scala b/src/main/scala/li/cil/oc/client/Sound.scala index 4c1ff23482..8e123e9ce3 100644 --- a/src/main/scala/li/cil/oc/client/Sound.scala +++ b/src/main/scala/li/cil/oc/client/Sound.scala @@ -33,7 +33,7 @@ object Sound { if (Settings.get.soundVolume > 0) { updateTimer.scheduleAtFixedRate(new TimerTask { override def run() { - sources.synchronized(updateCallable = Some(() => processQueue())) + sources.synchronized(Sound.updateCallable = Some(() => processQueue())) } }, 500, 50) } @@ -162,7 +162,7 @@ object Sound { override def isStopped() = stopped // Required by ITickableSound, which is required to update position while playing - override def tick() = Unit + override def tick() = () def stop() { stopped = true diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index dc7815b6db..5eb23ac767 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -10,15 +10,11 @@ import net.minecraft.inventory.container.PlayerContainer import net.minecraft.client.renderer.texture.SimpleTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.util.ResourceLocation -import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.mutable -@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object Textures { object Font extends TextureBundle { diff --git a/src/main/scala/li/cil/oc/client/gui/GuiTypes.java b/src/main/scala/li/cil/oc/client/gui/GuiTypes.java index 056f709993..f609824e7d 100644 --- a/src/main/scala/li/cil/oc/client/gui/GuiTypes.java +++ b/src/main/scala/li/cil/oc/client/gui/GuiTypes.java @@ -6,13 +6,9 @@ import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -@Mod.EventBusSubscriber(value = {Dist.CLIENT}, modid = "opencomputers", bus = Bus.MOD) public final class GuiTypes { @SubscribeEvent public static void clientSetup(FMLClientSetupEvent e) { diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index 3fe2c65bce..cddcef15b8 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -99,7 +99,7 @@ class Manual extends screen.Screen(StringTextComponent.EMPTY) with traits.Window } scrollButton = new ImageButton(leftPos + scrollPosX, topPos + scrollPosY, 6, 13, new Button.IPressable { - override def onPress(b: Button) = Unit + override def onPress(b: Button) = () }, Textures.GUI.ButtonScroll) addButton(scrollButton) diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index a50b5cbd76..e69a3987f8 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -101,7 +101,7 @@ class Robot(state: container.Robot, playerInventory: PlayerInventory, name: ITex override def onPress(b: Button) = ClientPacketSender.sendRobotPower(inventoryContainer, !inventoryContainer.isRunning) }, Textures.GUI.ButtonPower, canToggle = true) scrollButton = new ImageButton(leftPos + scrollX + 1, topPos + scrollY + 1, 6, 13, new Button.IPressable { - override def onPress(b: Button) = Unit + override def onPress(b: Button) = () }, Textures.GUI.ButtonScroll) addButton(powerButton) addButton(scrollButton) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala index e688bfc765..5f8c87dce2 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/DroneModel.scala @@ -14,8 +14,8 @@ import net.minecraft.item.ItemStack import net.minecraft.util.Direction import net.minecraft.util.math.vector.Vector3d -import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable +import scala.jdk.CollectionConverters._ object DroneModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride @@ -23,9 +23,9 @@ object DroneModel extends SmartBlockModelBase { override def getQuads(state: BlockState, side: Direction, rand: util.Random): util.List[BakedQuad] = { val faces = mutable.ArrayBuffer.empty[BakedQuad] - faces ++= Boxes.flatMap(box => bakeQuads(box, Array.fill(6)(droneTexture), None)) + faces ++= Boxes.flatMap(box => bakeQuads(box, Array.fill(6)(droneTexture), None).toSeq) - bufferAsJavaList(faces) + faces.asJava } protected def droneTexture = Textures.getSprite(Textures.Item.DroneItem) diff --git a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala index 63218e4714..30e80adde1 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/ModelInitialization.scala @@ -19,18 +19,14 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.IItemProvider import net.minecraft.util.Direction -import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.{ModelBakeEvent, ModelRegistryEvent} import net.minecraftforge.client.model.data.IDynamicBakedModel import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.convert.ImplicitConversionsToScala._ import scala.collection.mutable -@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object ModelInitialization { final val CableBlockLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "") final val CableItemLocation = new ModelResourceLocation(Settings.resourceDomain + ":" + Constants.BlockName.Cable, "inventory") diff --git a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala index 36ca8059cc..6f29ea7cf4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala +++ b/src/main/scala/li/cil/oc/client/renderer/block/NetSplitterModel.scala @@ -21,17 +21,13 @@ import net.minecraft.inventory.container.PlayerContainer import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation import net.minecraft.util.math.vector.Vector3d -import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import scala.collection.JavaConverters.bufferAsJavaList import scala.collection.mutable -@Mod.EventBusSubscriber(value = Array(Dist.CLIENT), modid = OpenComputers.ID, bus = Bus.MOD) object NetSplitterModel extends SmartBlockModelBase { override def getOverrides: ItemOverrideList = ItemOverride diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index d33ebe9c84..b52d6ce108 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -33,8 +33,8 @@ import net.minecraft.util.text.TextFormatting import net.minecraftforge.client.ForgeHooksClient import net.minecraftforge.common.MinecraftForge -import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.mutable +import scala.jdk.CollectionConverters._ import scala.language.implicitConversions object RobotRenderer extends Function[TileEntityRendererDispatcher, RobotRenderer] { @@ -369,7 +369,7 @@ class RobotRenderer(dispatch: TileEntityRendererDispatcher) extends TileEntityRe case _ => } - lazy val availableSlots = slotNameMapping.keys.to[mutable.Set] + lazy val availableSlots = slotNameMapping.keys.to(mutable.Set).asJava lazy val wildcardRenderers = mutable.Buffer.empty[(ItemStack, UpgradeRenderer)] lazy val slotMapping = Array.fill(mountPoints.length)(null: (ItemStack, UpgradeRenderer)) diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 45f3c6627e..9f7585da5b 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -1,19 +1,22 @@ package li.cil.oc.common -import java.util.function.BiConsumer -import java.util.function.Function -import java.util.function.Predicate import java.util.function.Supplier import com.google.common.base.Strings import li.cil.oc._ +import li.cil.oc.common.{PacketHandler => CommonPacketHandler} import li.cil.oc.common.capabilities.Capabilities +import li.cil.oc.common.container.ContainerTypes import li.cil.oc.common.entity.Drone +import li.cil.oc.common.entity.EntityTypes import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items +import li.cil.oc.common.tileentity.TileEntityTypes +import li.cil.oc.common.recipe.RecipeSerializers import li.cil.oc.integration.Mods import li.cil.oc.server import li.cil.oc.server._ +import li.cil.oc.server.loot.LootFunctions import li.cil.oc.server.machine.luac.LuaStateFactory import li.cil.oc.server.machine.luac.NativeLua52Architecture import li.cil.oc.server.machine.luac.NativeLua53Architecture @@ -41,12 +44,19 @@ import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent import net.minecraftforge.fml.network.NetworkEvent import net.minecraftforge.fml.network.NetworkRegistry import net.minecraftforge.registries.ForgeRegistries +import net.minecraftforge.scorge.lang.ScorgeModLoadingContext -import scala.collection.convert.ImplicitConversionsToScala._ +import scala.jdk.CollectionConverters._ import scala.reflect.ClassTag -@Deprecated class Proxy { + protected val modBus = ScorgeModLoadingContext.get.getModEventBus + modBus.register(classOf[ContainerTypes]) + modBus.register(classOf[EntityTypes]) + modBus.register(classOf[TileEntityTypes]) + modBus.register(classOf[RecipeSerializers]) + LootFunctions.init() + def preInit() { OpenComputers.log.info("Initializing OpenComputers API.") @@ -81,33 +91,21 @@ class Proxy { api.Machine.LuaArchitecture = if (Settings.get.forceLuaJ) classOf[LuaJLuaArchitecture] - else api.Machine.architectures.head + else api.Machine.architectures.asScala.head } @SubscribeEvent def init(e: FMLCommonSetupEvent) { - e.enqueueWork(() => { - OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), new Supplier[String] { - override def get = "" - }, new Predicate[String] { - override def test(ver: String) = "".equals(ver) - }, new Predicate[String] { - override def test(ver: String) = "".equals(ver) - }) - OpenComputers.channel.registerMessage(0, classOf[Array[Byte]], new BiConsumer[Array[Byte], PacketBuffer] { - override def accept(msg: Array[Byte], buff: PacketBuffer) = buff.writeByteArray(msg) - }, new Function[PacketBuffer, Array[Byte]] { - override def apply(buff: PacketBuffer) = buff.readByteArray() - }, new BiConsumer[Array[Byte], Supplier[NetworkEvent.Context]] { - override def accept(msg: Array[Byte], ctx: Supplier[NetworkEvent.Context]) = { + e.enqueueWork((() => { + OpenComputers.channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(OpenComputers.ID, "net_main"), () => "", "".equals(_), "".equals(_)) + OpenComputers.channel.registerMessage(0, classOf[Array[Byte]], + (msg: Array[Byte], buff: PacketBuffer) => buff.writeByteArray(msg), _.readByteArray(), + (msg: Array[Byte], ctx: Supplier[NetworkEvent.Context]) => { val context = ctx.get - context.enqueueWork(new Runnable { - override def run = PacketHandler.handlePacket(context.getDirection, msg, context.getSender) - }) + context.enqueueWork(() => CommonPacketHandler.handlePacket(context.getDirection, msg, context.getSender)) context.setPacketHandled(true) - } - }) - PacketHandler.serverHandler = server.PacketHandler + }) + CommonPacketHandler.serverHandler = server.PacketHandler Loot.init() Achievement.init() @@ -119,9 +117,7 @@ class Proxy { Capabilities.init() api.API.isPowerEnabled = !Settings.get.ignorePower - - Unit // Avoid ambiguity with e.enqueueWork(Supplier[_]) - }) + }): Runnable) } @SubscribeEvent @@ -152,7 +148,7 @@ class Proxy { @SubscribeEvent def missingBlockMappings(e: MissingMappings[Block]) { - for (missing <- e.getMappings(OpenComputers.ID)) { + for (missing <- e.getMappings(OpenComputers.ID).asScala) { blockRenames.get(missing.key.getPath) match { case Some(name) => if (Strings.isNullOrEmpty(name)) missing.ignore() @@ -164,7 +160,7 @@ class Proxy { @SubscribeEvent def missingItemMappings(e: MissingMappings[Item]) { - for (missing <- e.getMappings(OpenComputers.ID)) { + for (missing <- e.getMappings(OpenComputers.ID).asScala) { itemRenames.get(missing.key.getPath) match { case Some(name) => if (Strings.isNullOrEmpty(name)) missing.ignore() diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index 56883a9c34..6b73936ad0 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -221,7 +221,7 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w precisionMode = args.checkBoolean(0) result(oldValue) } - else result(Unit, "unsupported operation") + else result((), "unsupported operation") } // ----------------------------------------------------------------------- // @@ -258,7 +258,7 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w override def getMaximumHeight: Int = maxResolution._2 - override def setAspectRatio(width: Double, height: Double): Unit = this.synchronized(aspectRatio = (width, height)) + override def setAspectRatio(width: Double, height: Double): Unit = this.synchronized(this.aspectRatio = (width, height)) override def getAspectRatio: Double = aspectRatio._1 / aspectRatio._2 @@ -884,7 +884,7 @@ object TextBuffer { args += player.getName.getString } - owner.node.sendToReachable("computer.checked_signal", args: _*) + owner.node.sendToReachable("computer.checked_signal", args.toSeq: _*) } private def sendToKeyboards(name: String, values: AnyRef*) { diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala index 94d9202d47..452182a6b8 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala @@ -53,7 +53,7 @@ trait VideoRamRasterizer { } } } - case _ => Unit + case _ => } count } diff --git a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java index 4b5e98a85a..c6af141266 100644 --- a/src/main/scala/li/cil/oc/common/container/ContainerTypes.java +++ b/src/main/scala/li/cil/oc/common/container/ContainerTypes.java @@ -9,14 +9,11 @@ import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.network.IContainerFactory; import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ObjectHolder; -@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) @ObjectHolder("opencomputers") public final class ContainerTypes { public static final ContainerType ADAPTER = null; diff --git a/src/main/scala/li/cil/oc/common/container/Player.scala b/src/main/scala/li/cil/oc/common/container/Player.scala index 8831c9b605..1aeeebc872 100644 --- a/src/main/scala/li/cil/oc/common/container/Player.scala +++ b/src/main/scala/li/cil/oc/common/container/Player.scala @@ -1,5 +1,7 @@ package li.cil.oc.common.container +import java.util.Arrays + import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.common import li.cil.oc.common.InventorySlots.InventorySlot @@ -16,8 +18,10 @@ import net.minecraft.inventory.container.ContainerType import net.minecraft.inventory.container.IContainerListener import net.minecraft.inventory.container.Slot import net.minecraft.item.ItemStack -import net.minecraft.nbt.INBT +import net.minecraft.nbt.ByteArrayNBT import net.minecraft.nbt.CompoundNBT +import net.minecraft.nbt.INBT +import net.minecraft.nbt.IntArrayNBT import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.OnlyIn import net.minecraftforge.common.util.FakePlayer @@ -260,12 +264,18 @@ abstract class Player(selfType: ContainerType[_ <: Player], id: Int, val playerI } override def putByteArray(key: String, value: Array[Byte]): Unit = this.synchronized { - if (value.deep != getByteArray(key).deep) delta.putByteArray(key, value) + get(key) match { + case arr: ByteArrayNBT if !Arrays.equals(value, arr.getAsByteArray) => delta.putByteArray(key, value) + case _ => + } super.putByteArray(key, value) } override def putIntArray(key: String, value: Array[Int]): Unit = this.synchronized { - if (value.deep != getIntArray(key).deep) delta.putIntArray(key, value) + get(key) match { + case arr: IntArrayNBT if !Arrays.equals(value, arr.getAsIntArray) => delta.putIntArray(key, value) + case _ => + } super.putIntArray(key, value) } diff --git a/src/main/scala/li/cil/oc/common/entity/EntityTypes.java b/src/main/scala/li/cil/oc/common/entity/EntityTypes.java index 9dd5988aad..9e1a61473f 100644 --- a/src/main/scala/li/cil/oc/common/entity/EntityTypes.java +++ b/src/main/scala/li/cil/oc/common/entity/EntityTypes.java @@ -6,12 +6,9 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ObjectHolder; -@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) @ObjectHolder("opencomputers") public final class EntityTypes { public static final EntityType DRONE = null; diff --git a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala index e5bfe49178..b881c78a6c 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala @@ -366,8 +366,8 @@ class ControllerImpl(val player: PlayerEntity) extends Controller with WirelessE if (activeBehaviorsDirty) { configuration.synchronized(if (activeBehaviorsDirty) { val newBehaviors = configuration.behaviors.filter(_.isActive).map(_.behavior) - val addedBehaviors = newBehaviors -- activeBehaviors - val removedBehaviors = activeBehaviors -- newBehaviors + val addedBehaviors = newBehaviors.clone --= activeBehaviors + val removedBehaviors = activeBehaviors.clone --= newBehaviors activeBehaviors.clear() activeBehaviors ++= newBehaviors activeBehaviorsDirty = false diff --git a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java index addd545603..24d5c7f666 100644 --- a/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java +++ b/src/main/scala/li/cil/oc/common/recipe/RecipeSerializers.java @@ -6,13 +6,10 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistryEntry; import net.minecraftforge.registries.ObjectHolder; -@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) @ObjectHolder("opencomputers") public class RecipeSerializers { public static final IRecipeSerializer CRAFTING_LOOTDISK_CYCLING = null; diff --git a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala index 588493ae02..6b0e0b2d9b 100644 --- a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala +++ b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala @@ -73,7 +73,7 @@ object AssemblerTemplates { class Slot(val kind: String, val tier: Int, val validator: Option[Method], val hostClass: Option[Class[_ <: EnvironmentHost]]) { def validate(inventory: IInventory, slot: Int, stack: ItemStack) = validator match { - case Some(method) => IMC.tryInvokeStatic(method, inventory, slot.underlying(), tier.underlying(), stack)(false) + case Some(method) => IMC.tryInvokeStatic(method, inventory, Integer.valueOf(slot), Integer.valueOf(tier), stack)(false) case _ => Option(hostClass.fold(api.Driver.driverFor(stack))(api.Driver.driverFor(stack, _))) match { case Some(driver) => try driver.slot(stack) == kind && driver.tier(stack) <= tier catch { case t: AbstractMethodError => diff --git a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala index 6f512f93ca..00362d76d5 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/DiskDrive.scala @@ -88,7 +88,7 @@ class DiskDrive(selfType: TileEntityType[_ <: DiskDrive]) extends TileEntity(sel @Callback(doc = "function(): string -- Return the internal floppy disk address") def media(context: Context, args: Arguments): Array[AnyRef] = { if (filesystemNode.isEmpty) - result(Unit, "drive is empty") + result((), "drive is empty") else result(filesystemNode.head.address) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala index d022d12ba1..718770fb75 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala @@ -339,7 +339,7 @@ class Hologram(selfType: TileEntityType[_ <: Hologram], var tier: Int) extends T result(true) } - else result(Unit, "not supported") + else result((), "not supported") } @Callback(doc = """function(speed:number, x:number, y:number, z:number):boolean -- Set the rotation speed of the displayed hologram.""") @@ -358,7 +358,7 @@ class Hologram(selfType: TileEntityType[_ <: Hologram], var tier: Int) extends T result(true) } - else result(Unit, "not supported") + else result((), "not supported") } @Callback(direct = true, doc = "function():number, number, number -- Get the dimension of the x,y,z axes.") diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 33f87eaf6e..84b09653e7 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -291,11 +291,11 @@ class Microcontroller(selfType: TileEntityType[_ <: Microcontroller]) extends Ti } // Uses the loot system, so still nope. - override def forAllLoot(dst: Consumer[ItemStack]) = Unit + override def forAllLoot(dst: Consumer[ItemStack]) = () // Nope. override def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction]) = false // Nope. - override def dropAllSlots() = Unit + override def dropAllSlots() = () } diff --git a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala index 7df8dadb93..ccc3b44d23 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/NetSplitter.scala @@ -159,7 +159,7 @@ class NetSplitter(selfType: TileEntityType[_ <: NetSplitter]) extends TileEntity def setSideHelper(args: Arguments, value: Boolean): Array[AnyRef] = { val sideIndex = args.checkInteger(0) if (sideIndex < 0 || sideIndex > 5) - return result(Unit, "invalid direction") + return result((), "invalid direction") val side = Direction.from3DDataValue(sideIndex) result(setSide(side, value)) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index f102625cb8..04a8d8034a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -179,7 +179,7 @@ class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfTyp @Callback(doc = """function(minX:number, minY:number, minZ:number, maxX:number, maxY:number, maxZ:number, texture:string[, state:boolean=false][,tint:number]) -- Adds a shape to the printers configuration, optionally specifying whether it is for the off or on state.""") def addShape(context: Context, args: Arguments): Array[Object] = { if (data.stateOff.size > Settings.get.maxPrintComplexity || data.stateOn.size > Settings.get.maxPrintComplexity) { - return result(Unit, "model too complex") + return result((), "model too complex") } val minX = (args.checkInteger(0) max 0 min 16) / 16f val minY = (args.checkInteger(1) max 0 min 16) / 16f @@ -220,7 +220,7 @@ class Printer(selfType: TileEntityType[_ <: Printer]) extends TileEntity(selfTyp @Callback(doc = """function([count:number]):boolean -- Commit and begin printing the current configuration.""") def commit(context: Context, args: Arguments): Array[Object] = { if (!canPrint) { - return result(Unit, "model invalid") + return result((), "model invalid") } limit = (args.optDouble(0, 1) max 0 min Integer.MAX_VALUE).toInt isActive = limit > 0 diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 1e533ceeb7..ac94acfda1 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -85,11 +85,11 @@ class Raid(selfType: TileEntityType[_ <: Raid]) extends TileEntity(selfType) wit } // Uses the loot system, so nope. - override def forAllLoot(dst: Consumer[ItemStack]) = Unit + override def forAllLoot(dst: Consumer[ItemStack]) = () override def dropSlot(slot: Int, count: Int = getMaxStackSize, direction: Option[Direction]) = false - override def dropAllSlots() = Unit + override def dropAllSlots() = () def tryCreateRaid(id: String) { if (items.count(!_.isEmpty) == items.length && filesystem.fold(true)(fs => fs.node == null || fs.node.address != id)) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 20651441e0..1e5266f4da 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -143,7 +143,7 @@ class RobotProxy(selfType: TileEntityType[_ <: RobotProxy], val robot: Robot) ex def setName(context: Context, args: Arguments): Array[AnyRef] = { val oldName = robot.name val newName: String = args.checkString(0) - if (machine.isRunning) return result(Unit, "is running") + if (machine.isRunning) return result((), "is running") setName(newName) ServerPacketSender.sendRobotNameChange(robot) result(oldName) diff --git a/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java b/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java index 970a23f6d9..0b3e3de4a0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java +++ b/src/main/scala/li/cil/oc/common/tileentity/TileEntityTypes.java @@ -7,12 +7,9 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ObjectHolder; -@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) @ObjectHolder("opencomputers") public final class TileEntityTypes { public static final TileEntityType ADAPTER = null; diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index a39f609b6f..8727a627ef 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -54,7 +54,7 @@ trait BundledRedstoneAware extends RedstoneAware { val sideIndex = checkSide(side) val bundled = _bundledInput(sideIndex) val rednet = _rednetInput(sideIndex) - (bundled, rednet).zipped.map((a, b) => a max b max 0) + bundled.lazyZip(rednet).map((a, b) => a max b max 0) } def getBundledInput(side: Direction, color: Int): Int = { diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala index f7e3615518..b9bbcc4571 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverExportBus.scala @@ -74,7 +74,7 @@ object DriverExportBus extends driver.DriverBlock { val part = host.getPart(side) if (part == null || !AEUtil.isExportBus(part.getItemStack(PartItemStack.PICK))) { - return result(Unit, "no export bus") + return result((), "no export bus") } val exportBus = part.asInstanceOf[ISegmentedInventory with IConfigurableObject with IUpgradeableHost with IActionHost with IGridHost] @@ -82,7 +82,7 @@ object DriverExportBus extends driver.DriverBlock { val inventory: IItemHandler = InventoryUtils.inventoryAt(new BlockPosition(location.x, location.y, location.z, Some(location.getWorld)).offset(side), side.getOpposite) match { case Some(inv) => inv - case _ => return result(Unit, "no inventory") + case _ => return result((), "no inventory") } val targetSlot: Option[Int] = args.optSlot(inventory, 1, -1) match { @@ -120,7 +120,7 @@ object DriverExportBus extends driver.DriverBlock { } } if (potentialWork == count) - result(Unit, "no items moved") + result((), "no items moved") else result(potentialWork - count) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index 644e50242d..84cfee3a6d 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -70,7 +70,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IActionHost with IGridHos } } - private def reduceSequentialTable(map: scala.collection.mutable.HashMap[_, _]): AnyRef = { + private def reduceSequentialTable[K, V](map: scala.collection.mutable.HashMap[K, V]): AnyRef = { // in place of a table pack, we want a hash map of tuples val tuples = new util.LinkedList[AnyRef]() map.collect { @@ -309,10 +309,10 @@ object NetworkControl { private def withController(f: (TileEntity with IActionHost with IGridHost) => Array[AnyRef]): Array[AnyRef] = { if (delayData != null) { - result(Unit, "waiting for ae network to load") + result((), "waiting for ae network to load") } else { if (controller == null || controller.isRemoved) { - result(Unit, "no controller") + result((), "no controller") } else { f(controller) } @@ -322,7 +322,7 @@ object NetworkControl { private def withGridNode(f: (IGridNode) => Array[AnyRef]): Array[AnyRef] = { withController(c => Option(c.getGridNode(pos)) match { case Some(grid: IGridNode) => f(grid) - case _ => result(Unit, "no ae grid") + case _ => result((), "no ae grid") }) } @@ -500,7 +500,7 @@ object NetworkControl { } def asCraft(f: (ICraftingLink) => Array[AnyRef]): Array[AnyRef] = { - if (isComputing) result(Unit, "computing") + if (isComputing) result((), "computing") else link match { case Some(craft: ICraftingLink) if !failed => f(craft) case _ => result(false, reason) diff --git a/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala b/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala index 572090f758..3775ea6005 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/PartEnvironmentBase.scala @@ -25,7 +25,7 @@ trait PartEnvironmentBase extends ManagedEnvironment { val slot = args.optSlot(config, 1, 0) val stack = config.getStackInSlot(slot) result(stack) - case _ => result(Unit, "no matching part") + case _ => result((), "no matching part") } } @@ -60,7 +60,7 @@ trait PartEnvironmentBase extends ManagedEnvironment { config.insertItem(slot, stack, false) context.pause(0.5) result(true) - case _ => result(Unit, "no matching part") + case _ => result((), "no matching part") } } } diff --git a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala index b85b522ac9..fafbd84abf 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ManualUsageHandler.scala @@ -40,7 +40,7 @@ object ManualUsageHandler { private var background: IDrawable = _ private var icon: IDrawable = _ private val button = new Button((160 - 100) / 2, 10, 100, 20, Localization.localizeLater("nei.usage.oc.Manual"), new Button.IPressable { - override def onPress(b: Button) = Unit + override def onPress(b: Button) = () }) def initialize(guiHelper: IGuiHelper) { diff --git a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala index 5724bd4c3d..d4862511b8 100644 --- a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala +++ b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala @@ -24,7 +24,7 @@ object BundledRedstone { if (pos.world.get.blockExists(pos.offset(side))) { val inputs = providers.map(_.computeBundledInput(pos, side)).filter(_ != null) if (inputs.isEmpty) null - else inputs.reduce((a, b) => (a, b).zipped.map((l, r) => math.max(l, r))) + else inputs.reduce((a, b) => a.lazyZip(b).map((l, r) => math.max(l, r))) } else null } diff --git a/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala b/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala index 5a5d77766c..0be775f8c2 100644 --- a/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala +++ b/src/main/scala/li/cil/oc/integration/waila/ModWaila.scala @@ -7,5 +7,5 @@ import net.minecraftforge.fml.InterModComms object ModWaila extends ModProxy { override def getMod = Mods.Waila - override def initialize() = Unit + override def initialize() = () } diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index e8321089af..584919f530 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -11,6 +11,7 @@ import li.cil.oc.api.event._ import li.cil.oc.api.internal import li.cil.oc.api.network.Connector import li.cil.oc.common.EventHandler +import li.cil.oc.server.agent.{Inventory => AgentInventory} import li.cil.oc.util.BlockPosition import li.cil.oc.util.InventoryUtils import net.minecraft.block.PistonBlock @@ -59,7 +60,7 @@ import net.minecraftforge.eventbus.api.{Event, EventPriority, SubscribeEvent} import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.wrapper._ -import scala.collection.convert.ImplicitConversionsToScala._ +import scala.jdk.CollectionConverters._ object Player { // These use unobfuscated names because they're added by forge (LazyOptional / capabilities). @@ -111,7 +112,7 @@ object Player { val agent = player.agent def setCopyOrNull(inv: net.minecraft.util.NonNullList[ItemStack], agentInv: IInventory, slot: Int): Unit = { val item = agentInv.getItem(slot) - inv(slot) = if (item != null) item.copy() else ItemStack.EMPTY + inv.set(slot, if (item != null) item.copy() else ItemStack.EMPTY) } for (i <- 0 until 4) { @@ -121,7 +122,7 @@ object Player { // items is 36 items // the agent inventory is 100 items with some space for components // leaving us 88..we'll copy what we can - val size = player.inventory.items.length min agent.mainInventory.getContainerSize + val size = player.inventory.items.size min agent.mainInventory.getContainerSize for (i <- 0 until size) { setCopyOrNull(player.inventory.items, agent.mainInventory, i) } @@ -140,11 +141,11 @@ object Player { } } for (i <- 0 until 4) { - setCopy(agent.equipmentInventory(), i, player.inventory.armor(i)) + setCopy(agent.equipmentInventory(), i, player.inventory.armor.get(i)) } - val size = player.inventory.items.length min agent.mainInventory.getContainerSize + val size = player.inventory.items.size min agent.mainInventory.getContainerSize for (i <- 0 until size) { - setCopy(agent.mainInventory, i, player.inventory.items(i)) + setCopy(agent.mainInventory, i, player.inventory.items.get(i)) } } } @@ -165,7 +166,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc refreshDimensions() { - this.inventory = new Inventory(this, agent) + this.inventory = new AgentInventory(this, agent) // because the inventory was just overwritten, the container is now detached this.inventoryMenu = new PlayerContainer(inventory, !level.isClientSide, this) this.containerMenu = this.inventoryMenu @@ -195,7 +196,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc val bounds = BlockPosition(agent).offset(side).bounds val candidates = level.getEntitiesOfClass(clazz, bounds, null) if (candidates.isEmpty) return None - Some(candidates.minBy(e => distanceToSqr(e))) + Some(candidates.asScala.minBy(e => distanceToSqr(e))) } def entitiesOnSide[Type <: Entity](clazz: Class[Type], side: Direction): util.List[Type] = { @@ -211,8 +212,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc } private def collectDroppedItems(itemsBefore: Iterable[ItemEntity]) { - val itemsAfter = adjacentItems - val itemsDropped = itemsAfter -- itemsBefore + val itemsDropped = adjacentItems.asScala --= itemsBefore if (itemsDropped.nonEmpty) { for (drop <- itemsDropped) { drop.setDefaultPickUpDelay() @@ -518,7 +518,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc else ForgeEventFactory.onPlayerDestroyItem(this, newStack, Hand.OFF_HAND) } } - collectDroppedItems(itemsBefore) + collectDroppedItems(itemsBefore.asScala) } } diff --git a/src/main/scala/li/cil/oc/server/component/Agent.scala b/src/main/scala/li/cil/oc/server/component/Agent.scala index 5dab607def..71a8cdef09 100644 --- a/src/main/scala/li/cil/oc/server/component/Agent.scala +++ b/src/main/scala/li/cil/oc/server/component/Agent.scala @@ -270,7 +270,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits val sneaky = args.isBoolean(2) && args.checkBoolean(2) val stack = agent.mainInventory.getItem(agent.selectedSlot) if (stack.isEmpty) { - return result(Unit, "nothing selected") + return result((), "nothing selected") } for (side <- sides) { diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index b73a6c9338..bf08bc698a 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -274,7 +274,7 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D node.connect(other) result(true) case _ => - result(Unit, "no node found at this position") + result((), "no node found at this position") } } @@ -423,7 +423,7 @@ object DebugCard { checkAccess() ServerLifecycleHooks.getCurrentServer.getPlayerList.getPlayerByName(name) match { case player: ServerPlayerEntity => f(player) - case _ => result(Unit, "player is offline") + case _ => result((), "player is offline") } } @@ -838,9 +838,9 @@ object DebugCard { tileEntity.setChanged() world.notifyBlockUpdate(blockPos) result(true) - case nbt => result(Unit, s"nbt tag COMPOUND expected, got 'nbt.getType.getName'") + case nbt => result((), s"nbt tag COMPOUND expected, got 'nbt.getType.getName'") } - case _ => result(Unit, "no tile entity") + case _ => result((), "no tile entity") } } @@ -921,7 +921,7 @@ object DebugCard { stack.setTag(tag) stack.setDamageValue(damage) result(InventoryUtils.insertIntoInventory(stack, inventory)) - case _ => result(Unit, "no inventory") + case _ => result((), "no inventory") } } @@ -936,7 +936,7 @@ object DebugCard { val removed = inventory.extractItem(slot, count, false) if (removed.isEmpty) result(0) else result(removed.getCount) - case _ => result(Unit, "no inventory") + case _ => result((), "no inventory") } } @@ -952,7 +952,7 @@ object DebugCard { val side = args.checkSideAny(5) world.getBlockEntity(position) match { case handler: IFluidHandler => result(handler.fill(new FluidStack(fluid, amount), IFluidHandler.FluidAction.EXECUTE)) - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } @@ -964,7 +964,7 @@ object DebugCard { val side = args.checkSideAny(4) world.getBlockEntity(position) match { case handler: IFluidHandler => result(handler.drain(amount, IFluidHandler.FluidAction.EXECUTE)) - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } diff --git a/src/main/scala/li/cil/oc/server/component/EEPROM.scala b/src/main/scala/li/cil/oc/server/component/EEPROM.scala index 1fb8d05057..2c1ab3c07c 100644 --- a/src/main/scala/li/cil/oc/server/component/EEPROM.scala +++ b/src/main/scala/li/cil/oc/server/component/EEPROM.scala @@ -56,10 +56,10 @@ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { @Callback(doc = """function(data:string) -- Overwrite the currently stored byte array.""") def set(context: Context, args: Arguments): Array[AnyRef] = { if (readonly) { - return result(Unit, "storage is readonly") + return result((), "storage is readonly") } if (!node.tryChangeBuffer(-Settings.get.eepromWriteCost)) { - return result(Unit, "not enough energy") + return result((), "not enough energy") } val newData = args.optByteArray(0, Array.empty[Byte]) if (newData.length > Settings.get.eepromSize) throw new IllegalArgumentException("not enough space") @@ -74,7 +74,7 @@ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { @Callback(doc = """function(data:string):string -- Set the label of the EEPROM.""") def setLabel(context: Context, args: Arguments): Array[AnyRef] = { if (readonly) { - return result(Unit, "storage is readonly") + return result((), "storage is readonly") } label = args.optString(0, "EEPROM").trim.take(24) if (label.length == 0) label = "EEPROM" @@ -93,7 +93,7 @@ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { readonly = true result(true) } - else result(Unit, "incorrect checksum") + else result((), "incorrect checksum") } @Callback(direct = true, doc = """function():number -- Get the storage capacity of this EEPROM.""") @@ -105,7 +105,7 @@ class EEPROM extends AbstractManagedEnvironment with DeviceInfo { @Callback(doc = """function(data:string) -- Overwrite the currently stored byte array.""") def setData(context: Context, args: Arguments): Array[AnyRef] = { if (!node.tryChangeBuffer(-Settings.get.eepromWriteCost)) { - return result(Unit, "not enough energy") + return result((), "not enough energy") } val newData = args.optByteArray(0, Array.empty[Byte]) if (newData.length > Settings.get.eepromDataSize) throw new IllegalArgumentException("not enough space") diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index 1d27d14b52..2d83b36405 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -197,7 +197,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option result(bytes) } else { - result(Unit) + result(()) } case _ => throw new IOException("bad file descriptor") } @@ -309,7 +309,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option nbt.getList("owners", NBT.TAG_COMPOUND).foreach((ownerNbt: CompoundNBT) => { val address = ownerNbt.getString("address") if (address != "") { - owners += address -> ownerNbt.getIntArray("handles").to[mutable.Set] + owners += address -> ownerNbt.getIntArray("handles").to(mutable.Set) } }) diff --git a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala index 6225e8fecf..a6c44b80ef 100644 --- a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala @@ -112,11 +112,11 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit } if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) - return result(Unit, "not enough energy") + return result((), "not enough energy") val event = new GeolyzerEvent.Scan(host, options, minX, minY, minZ, maxX, maxY, maxZ) MinecraftForge.EVENT_BUS.post(event) - if (event.isCanceled) result(Unit, "scan was canceled") + if (event.isCanceled) result((), "scan was canceled") else result(event.data) } @@ -151,15 +151,15 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit val options = args.optTable(1, Map.empty[AnyRef, AnyRef]) if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) - return result(Unit, "not enough energy") + return result((), "not enough energy") val globalPos = BlockPosition(host).offset(globalSide) val event = new Analyze(host, options, globalPos.toBlockPos) MinecraftForge.EVENT_BUS.post(event) - if (event.isCanceled) result(Unit, "scan was canceled") + if (event.isCanceled) result((), "scan was canceled") else result(event.data) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function(side:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack representation of the block on the specified side in a database component.""") def store(computer: Context, args: Arguments): Array[AnyRef] = { @@ -170,12 +170,12 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit } if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) - return result(Unit, "not enough energy") + return result((), "not enough energy") val blockPos = BlockPosition(host).offset(globalSide) val blockState = host.world.getBlockState(blockPos.toBlockPos) val item = blockState.getBlock().asItem() - if (item == null) result(Unit, "block has no registered item representation") + if (item == null) result((), "block has no registered item representation") else { val stacks = Block.getDrops(blockState, host.world.asInstanceOf[ServerWorld], blockPos.toBlockPos, host.world.getBlockEntity(blockPos.toBlockPos)) val stack = if (!stacks.isEmpty) { diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 8bb1c3c1ff..37a1382d65 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -51,12 +51,12 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device if (index == RESERVED_SCREEN_INDEX) { screenInstance match { case Some(screen) => screen.synchronized(f(screen)) - case _ => Array(Unit, "no screen") + case _ => Array(null, "no screen") } } else { getBuffer(index) match { case Some(buffer: api.internal.TextBuffer) => f(buffer) - case _ => Array(Unit, "invalid buffer index") + case _ => Array(null, "invalid buffer index") } } } @@ -118,7 +118,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device val previousIndex: Int = bufferIndex val newIndex: Int = args.checkInteger(0) if (newIndex != RESERVED_SCREEN_INDEX && getBuffer(newIndex).isEmpty) { - result(Unit, "invalid buffer index") + result((), "invalid buffer index") } else { bufferIndex = newIndex if (bufferIndex == RESERVED_SCREEN_INDEX) { @@ -139,12 +139,12 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device val height: Int = args.optInteger(1, maxResolution._2) val size: Int = width * height if (width <= 0 || height <= 0) { - result(Unit, "invalid page dimensions: must be greater than zero") + result((), "invalid page dimensions: must be greater than zero") } else if (size > (totalVRAM - calculateUsedMemory)) { - result(Unit, "not enough video memory") + result((), "not enough video memory") } else if (node == null) { - result(Unit, "graphics card appears disconnected") + result((), "graphics card appears disconnected") } else { val format: PackedColor.ColorFormat = PackedColor.Depth.format(Settings.screenDepthsByTier(tier)) val buffer = new li.cil.oc.util.TextBuffer(width, height, format) @@ -173,7 +173,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device def freeBuffer(context: Context, args: Arguments): Array[AnyRef] = { val index: Int = args.optInteger(0, bufferIndex) if (removeBuffers(Array(index)) == 1) result(true) - else result(Unit, "no buffer at index") + else result((), "no buffer at index") } @Callback(direct = true, doc = """function(): number -- Closes all buffers and returns the count. If the active buffer is closed, index moves to 0""") @@ -263,7 +263,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device component.GpuTextBuffer.bitblt(dst, col, row, w, h, src, fromRow, fromCol) result(true) } - } else result(Unit, "not enough energy") + } else result((), "not enough energy") }) }) } @@ -273,7 +273,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device val address = args.checkString(0) val reset = args.optBoolean(1, true) node.network.node(address) match { - case null => result(Unit, "invalid address") + case null => result((), "invalid address") case node: Node if node.host.isInstanceOf[api.internal.TextBuffer] => screenAddress = Option(address) screenInstance = Some(node.host.asInstanceOf[api.internal.TextBuffer]) @@ -294,7 +294,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device else context.pause(0) // To discourage outputting "in realtime" to multiple screens using one GPU. result(true) }) - case _ => result(Unit, "not a screen") + case _ => result((), "not a screen") } } @@ -318,7 +318,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device (s.getPaletteColor(oldValue), oldValue) } else { - (oldValue, Unit) + (oldValue, ()) } s.setBackgroundColor(color, args.optBoolean(1, false)) result(oldColor, oldIndex) @@ -342,7 +342,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device (s.getPaletteColor(oldValue), oldValue) } else { - (oldValue, Unit) + (oldValue, ()) } s.setForegroundColor(color, args.optBoolean(1, false)) result(oldColor, oldIndex) @@ -464,7 +464,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device (s.getPaletteColor(fgValue), fgValue) } else { - (fgValue, Unit) + (fgValue, ()) } val bgValue = s.getBackgroundColor(x, y) @@ -473,7 +473,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device (s.getPaletteColor(bgValue), bgValue) } else { - (bgValue, Unit) + (bgValue, ()) } result(s.get(x, y), fgColor, bgColor, fgIndex, bgIndex) @@ -491,7 +491,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device if (resolveInvokeCosts(bufferIndex, context, setCosts(tier), value.length, Settings.get.gpuSetCost)) { s.set(x, y, value, vertical) result(true) - } else result(Unit, "not enough energy") + } else result((), "not enough energy") }) } @@ -508,7 +508,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device s.copy(x, y, w, h, tx, ty) result(true) } - else result(Unit, "not enough energy") + else result((), "not enough energy") }) } @@ -527,7 +527,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device result(true) } else { - result(Unit, "not enough energy") + result((), "not enough energy") } }) else throw new Exception("invalid fill value") diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index bf7794405f..6f3e3aa75c 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -68,7 +68,7 @@ class InternetCard extends AbstractManagedEnvironment with DeviceInfo { checkOwner(context) val address = args.checkString(0) if (!Settings.get.httpEnabled) { - return result(Unit, "http requests are unavailable") + return result((), "http requests are unavailable") } if (connections.size >= Settings.get.maxConnections) { throw new IOException("too many open connections") @@ -79,7 +79,7 @@ class InternetCard extends AbstractManagedEnvironment with DeviceInfo { }.toMap else Map.empty[String, String] if (!Settings.get.httpHeadersEnabled && headers.nonEmpty) { - return result(Unit, "http request headers are unavailable") + return result((), "http request headers are unavailable") } val method = if (args.isString(3)) Option(args.checkString(3)) else None val request = new InternetCard.HTTPRequest(this, checkAddress(address), post, headers, method) @@ -96,7 +96,7 @@ class InternetCard extends AbstractManagedEnvironment with DeviceInfo { val address = args.checkString(0) val port = args.optInteger(1, -1) if (!Settings.get.tcpEnabled) { - return result(Unit, "tcp connections are unavailable") + return result((), "tcp connections are unavailable") } if (connections.size >= Settings.get.maxConnections) { throw new IOException("too many open connections") @@ -273,10 +273,10 @@ object InternetCard { if (checkConnected()) { val buffer = ByteBuffer.allocate(n) val read = channel.read(buffer) - if (read == -1) result(Unit) + if (read == -1) result(()) else { setupSelector() - result(buffer.array.view(0, read).toArray) + result(buffer.array.view.slice(0, read).toArray) } } else result(Array.empty[Byte]) @@ -388,7 +388,7 @@ object InternetCard { def response(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { response match { case Some((code, message, headers)) => result(code, message, headers) - case _ => result(Unit) + case _ => result(()) } } @@ -396,7 +396,7 @@ object InternetCard { def read(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.optInteger(0, Int.MaxValue))) if (checkResponse()) { - if (eof && queue.isEmpty) result(Unit) + if (eof && queue.isEmpty) result(()) else { val buffer = ByteBuffer.allocate(n) var read = 0 @@ -407,7 +407,7 @@ object InternetCard { if (read == 0) { readMore() } - result(buffer.array.view(0, read).toArray) + result(buffer.array.view.slice(0, read).toArray) } } else result(Array.empty[Byte]) diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index cf3ada270c..00bd5fe09d 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -55,7 +55,7 @@ class LinkedCard extends AbstractManagedEnvironment with QuantumNetwork.QuantumN } result(true) } - else result(Unit, "not enough energy") + else result((), "not enough energy") } @Callback(direct = true, doc = "function():number -- Gets the maximum packet size (config setting).") diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala index d53463e6b4..fae2b88066 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneSignaller.scala @@ -40,7 +40,7 @@ trait RedstoneSignaller extends AbstractManagedEnvironment { val flatArgs = ArrayBuffer[Object]("redstone_changed", side, Int.box(args.oldValue), Int.box(args.newValue)) if (args.color >= 0) flatArgs += Int.box(args.color) - node.sendToReachable("computer.signal", flatArgs: _*) + node.sendToReachable("computer.signal", flatArgs.toArray: _*) if (args.oldValue < wakeThreshold && args.newValue >= wakeThreshold) { if (wakeNeighborsOnly) node.sendToNeighbors("computer.start") diff --git a/src/main/scala/li/cil/oc/server/component/Robot.scala b/src/main/scala/li/cil/oc/server/component/Robot.scala index ad68547b40..9b71ceec94 100644 --- a/src/main/scala/li/cil/oc/server/component/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/Robot.scala @@ -78,9 +78,9 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with case SomeStack(item) => ToolDurabilityProviders.getDurability(item) match { case Some(durability) => result(durability) - case _ => result(Unit, "tool cannot be damaged") + case _ => result((), "tool cannot be damaged") } - case _ => result(Unit, "no tool equipped") + case _ => result((), "no tool equipped") } } @@ -92,18 +92,18 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with if (agent.isAnimatingMove) { // This shouldn't really happen due to delays being enforced, but just to // be on the safe side... - result(Unit, "already moving") + result((), "already moving") } else { val (something, what) = blockContent(direction) if (something) { context.pause(0.4) PacketSender.sendParticleEffect(BlockPosition(agent), ParticleTypes.CRIT, 8, 0.25, Some(direction)) - result(Unit, what) + result((), what) } else { if (!node.tryChangeBuffer(-Settings.get.robotMoveCost)) { - result(Unit, "not enough energy") + result((), "not enough energy") } else if (agent.move(direction)) { context.pause(Settings.get.moveDelay) @@ -113,7 +113,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with node.changeBuffer(Settings.get.robotMoveCost) context.pause(0.4) PacketSender.sendParticleEffect(BlockPosition(agent), ParticleTypes.CRIT, 8, 0.25, Some(direction)) - result(Unit, "impossible move") + result((), "impossible move") } } } @@ -130,7 +130,7 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with result(true) } else { - result(Unit, "not enough energy") + result((), "not enough energy") } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala index df5f08f0a5..c830f98a6b 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeExperience.scala @@ -82,11 +82,11 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A @Callback(doc = """function():boolean -- Tries to consume an enchanted item to add experience to the upgrade.""") def consume(context: Context, args: Arguments): Array[AnyRef] = { if (level >= MaxLevel) { - return result(Unit, "max level") + return result((), "max level") } val stack = host.mainInventory.getItem(host.selectedSlot) if (stack.isEmpty) { - return result(Unit, "no item") + return result((), "no item") } var xp = 0 if (stack.getItem == Items.EXPERIENCE_BOTTLE) { @@ -99,12 +99,12 @@ class UpgradeExperience(val host: EnvironmentHost with internal.Agent) extends A } } if (xp <= 0) { - return result(Unit, "could not extract experience from item") + return result((), "could not extract experience from item") } } val consumed = host.mainInventory().removeItem(host.selectedSlot, 1) if (consumed.isEmpty) { - return result(Unit, "could not consume item") + return result((), "could not consume item") } addExperience(xp * Settings.get.constantXpGrowth) result(true) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index 2dec5cb1b1..da4cb818c4 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -52,22 +52,22 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab def insert(context: Context, args: Arguments): Array[AnyRef] = { val count = args.optInteger(0, 64) val stack = host.mainInventory.getItem(host.selectedSlot) - if (stack.isEmpty) return result(Unit, "selected slot is empty") + if (stack.isEmpty) return result((), "selected slot is empty") if (ForgeHooks.getBurnTime(stack, null) <= 0) { - return result(Unit, "selected slot does not contain fuel") + return result((), "selected slot does not contain fuel") } val container: ItemStack = stack.getContainerItem() val inQueue: ItemStack = inventory match { case SomeStack(q) if q != null && q.getCount > 0 => if (!q.sameItem(stack) || !ItemStack.tagMatches(q, stack)) { - return result(Unit, "different fuel type already queued") + return result((), "different fuel type already queued") } q case _ => ItemStack.EMPTY } val space = if (inQueue.isEmpty) stack.getMaxStackSize else inQueue.getMaxStackSize - inQueue.getCount if (space == 0) { - return result(Unit, "queue is full") + return result((), "queue is full") } val previousSelectedFuel: ItemStack = stack.copy val insertLimit: Int = math.min(stack.getCount, math.min(space, count)) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala index 46f59b8a0e..65771661e8 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala @@ -53,7 +53,7 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra @Callback(doc = """function(side:number):boolean -- Tries to put an entity on the specified side of the device onto a leash.""") def leash(context: Context, args: Arguments): Array[AnyRef] = { - if (leashedEntities.size >= MaxLeashedEntities) return result(Unit, "too many leashed entities") + if (leashedEntities.size >= MaxLeashedEntities) return result((), "too many leashed entities") val side = args.checkSideAny(0) val nearBounds = position.bounds val farBounds = nearBounds.move(side.getStepX * 2.0, side.getStepY * 2.0, side.getStepZ * 2.0) @@ -64,7 +64,7 @@ class UpgradeLeash(val host: Entity) extends AbstractManagedEnvironment with tra leashedEntities += entity.getUUID context.pause(0.1) result(true) - case _ => result(Unit, "no unleashed entity") + case _ => result((), "no unleashed entity") } } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index 4a7b2b77f1..5f958b1f34 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -59,7 +59,7 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra if (math.abs(relativeX) <= size / 2 && math.abs(relativeZ) <= size / 2) result(relativeX, host.yPosition, relativeZ) else - result(Unit, "out of range") + result((), "out of range") } @Callback(doc = """function():number -- Get the current orientation of the robot.""") @@ -72,7 +72,7 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends Abstra def findWaypoints(context: Context, args: Arguments): Array[AnyRef] = { val range = args.checkDouble(0) max 0 min Settings.get.maxWirelessRange(Tier.Two) if (range <= 0) return result(Array.empty) - if (!node.tryChangeBuffer(-range * Settings.get.wirelessCostPerRange(Tier.Two) * 0.25)) return result(Unit, "not enough energy") + if (!node.tryChangeBuffer(-range * Settings.get.wirelessCostPerRange(Tier.Two) * 0.25)) return result((), "not enough energy") context.pause(0.5) val position = BlockPosition(host) val positionVec = position.toVec3 diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala index b1164c9678..e07a8c6468 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala @@ -45,7 +45,7 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { protected def getValue(tileEntity: Option[SignTileEntity]): Array[AnyRef] = { tileEntity match { case Some(sign) => result(sign.messages.map(_.getString).mkString("\n")) - case _ => result(Unit, "no sign") + case _ => result((), "no sign") } } @@ -60,7 +60,7 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { val lines = text.lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line).toArray if (!canChangeSign(player, sign, lines)) { - return result(Unit, "not allowed") + return result((), "not allowed") } lines.map(line => new StringTextComponent(line)).copyToArray(sign.messages) @@ -69,7 +69,7 @@ abstract class UpgradeSign extends AbstractManagedEnvironment with DeviceInfo { MinecraftForge.EVENT_BUS.post(new SignChangeEvent.Post(sign, lines)) result(sign.messages.mkString("\n")) - case _ => result(Unit, "no sign") + case _ => result((), "no sign") } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala index 54487cf023..8c2ad15370 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala @@ -19,7 +19,7 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware { val slot = optSlot(args, 0) result(inventory.getItem(slot)) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function(otherSlot:number):boolean -- Get whether the stack in the selected slot is equivalent to the item in the specified slot (have shared OreDictionary IDs).""") def isEquivalentTo(context: Context, args: Arguments): Array[AnyRef] = { diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala index 594ada358c..b2fdf4ef60 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala @@ -22,7 +22,7 @@ trait InventoryTransfer extends traits.WorldAware with traits.SideRestricted { onTransferContents() match { case Some(reason) => - result(Unit, reason) + result((), reason) case _ => val extractor = if (args.count > 3) { val sourceSlot = args.checkSlot(InventoryUtils.inventoryAt(sourcePos, sourceSide.getOpposite).getOrElse(throw new IllegalArgumentException("no inventory")), 3) @@ -35,7 +35,7 @@ trait InventoryTransfer extends traits.WorldAware with traits.SideRestricted { Option(extractor) match { case Some(ex) => result(ex()) - case _ => result(Unit, "no inventory") + case _ => result((), "no inventory") } } } @@ -50,7 +50,7 @@ trait InventoryTransfer extends traits.WorldAware with traits.SideRestricted { onTransferContents() match { case Some(reason) => - result(Unit, reason) + result((), reason) case _ => val moved = FluidUtils.transferBetweenFluidHandlersAt(sourcePos, sourceSide.getOpposite, sinkPos, sinkSide.getOpposite, count) if (moved > 0) context.pause(moved / 1000 * 0.25) // Allow up to 4 buckets per second. diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala index 69b782664c..96aad1ee46 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala @@ -62,6 +62,6 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR private def withInventory(blockPos: BlockPosition, fromSide: Direction, f: IItemHandler => Array[AnyRef]) = InventoryUtils.inventoryAt(blockPos, fromSide) match { case Some(inventory) if mayInteract(blockPos, fromSide) => f(inventory) - case _ => result(Unit, "no inventory") + case _ => result((), "no inventory") } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala index 34e2add739..adc040d715 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala @@ -73,8 +73,8 @@ trait TankControl extends TankAware { from.fill(tmp, FluidAction.EXECUTE) result(true) } - else result(Unit, "incompatible or no fluid") - case _ => result(Unit, "invalid index") + else result((), "incompatible or no fluid") + case _ => result((), "invalid index") } } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala index 8b205e2e12..326f712cfc 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala @@ -26,13 +26,13 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware def getFluidInTankInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { withFluidInfo(optSlot(args, 0), (fluid, _) => result(fluid.orNull)) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function([tank:number]):table -- Get a description of the fluid in the tank in the specified slot or the selected slot.""") def getFluidInInternalTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { result(Option(tank.getFluidTank(optTank(args, 0))).map(_.getFluid).orNull) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""") def drain(context: Context, args: Arguments): Array[AnyRef] = { @@ -49,12 +49,12 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware inventory.setItem(selectedSlot, handler.getContainer) result(true, transferred) } - else result(Unit, "incompatible or no fluid") - case _ => result(Unit, "item is not a fluid container") + else result((), "incompatible or no fluid") + case _ => result((), "item is not a fluid container") } - case _ => result(Unit, "nothing selected") + case _ => result((), "nothing selected") } - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } @@ -73,12 +73,12 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware inventory.setItem(selectedSlot, handler.getContainer) result(true, transferred) } - else result(Unit, "incompatible or no fluid") - case _ => result(Unit, "item is not a fluid container") + else result((), "incompatible or no fluid") + case _ => result((), "item is not a fluid container") } - case _ => result(Unit, "nothing selected") + case _ => result((), "nothing selected") } - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } @@ -92,7 +92,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware inventory.getItem(slot) match { case stack: ItemStack => fluidInfo(stack) match { case Some((fluid, capacity)) => f(fluid, capacity) - case _ => result(Unit, "item is not a fluid container") + case _ => result((), "item is not a fluid container") } } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala index 44ffdec6b9..3016d9f6c0 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala @@ -44,16 +44,16 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { val filled = tank.fill(drained, FluidAction.EXECUTE) result(true, filled) } - else result(Unit, "incompatible or no fluid") + else result((), "incompatible or no fluid") case _ => val transferred = tank.fill(handler.drain(amount, FluidAction.EXECUTE), FluidAction.EXECUTE) result(transferred > 0, transferred) } - case _ => result(Unit, "incompatible or no fluid") + case _ => result((), "incompatible or no fluid") } } - else result(Unit, "tank is full") - case _ => result(Unit, "no tank selected") + else result((), "tank is full") + case _ => result((), "no tank selected") } } @@ -74,15 +74,15 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { tank.drain(filled, FluidAction.EXECUTE) result(true, filled) } - else result(Unit, "incompatible or no fluid") + else result((), "incompatible or no fluid") case _ => - result(Unit, "tank is empty") + result((), "tank is empty") } - case _ => result(Unit, "no space") + case _ => result((), "no space") } } - else result(Unit, "tank is empty") - case _ => result(Unit, "no tank selected") + else result((), "tank is empty") + case _ => result((), "no tank selected") } } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala index ffd3e2a557..7714e2b8e2 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala @@ -78,7 +78,7 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ val facing = checkSideForAction(args, 0) withInventory(facing, inventory => result(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function(side:number):userdata -- Get a description of all stacks in the inventory on the specified side of the device.""") def getAllStacks(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { @@ -91,7 +91,7 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ result(new ItemStackArrayValue(stacks)) }) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function(side:number):string -- Get the the name of the inventory on the specified side of the device.""") def getInventoryName(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { @@ -105,10 +105,10 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ } withInventory(facing, inventory => blockAt(position.offset(facing)) match { case Some(block) => result(block.getRegistryName) - case _ => result(Unit, "Unknown") + case _ => result((), "Unknown") }) } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") @Callback(doc = """function(side:number, slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address.""") def store(context: Context, args: Arguments): Array[AnyRef] = { @@ -126,6 +126,6 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ private def withInventory(side: Direction, f: IItemHandler => Array[AnyRef]) = InventoryUtils.inventoryAt(position.offset(side), side.getOpposite) match { case Some(inventory) if mayInteract(position.offset(side), side.getOpposite, inventory) => f(inventory) - case _ => result(Unit, "no inventory") + case _ => result((), "no inventory") } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala index c8f720f1e5..89133115c5 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala @@ -18,7 +18,7 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { case properties: TankProperties => result(Option(properties.contents).fold(0)(_.getAmount)) case _ => result((0 until handler.getTanks).map(i => Option(handler.getFluidInTank(i)).fold(0)(_.getAmount)).sum) } - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } @@ -30,7 +30,7 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { case properties: TankProperties => result(properties.capacity) case _ => result((0 until handler.getTanks).map(handler.getTankCapacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) } - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } @@ -42,8 +42,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { case properties: TankProperties => result(properties) case _ => result((0 until handler.getTanks).map(i => new TankProperties(handler.getTankCapacity(i), handler.getFluidInTank(i))).toArray) } - case _ => result(Unit, "no tank") + case _ => result((), "no tank") } } - else result(Unit, "not enabled in config") + else result((), "not enabled in config") } diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index b074308b4a..b098c76537 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -149,7 +149,7 @@ private[oc] object Registry extends api.detail.DriverAPI { def convert(value: Array[AnyRef]): Array[AnyRef] = if (value != null) value.map(arg => convertRecursively(arg, new util.IdentityHashMap())) else null - def convertRecursively(value: Any, memo: util.IdentityHashMap[AnyRef, AnyRef], force: Boolean = false): AnyRef = { + def convertRecursively(value: Any, memo: util.IdentityHashMap[Any, AnyRef], force: Boolean = false): AnyRef = { val valueRef = value match { case number: ScalaNumber => number.underlying case reference: AnyRef => reference @@ -160,7 +160,7 @@ private[oc] object Registry extends api.detail.DriverAPI { memo.get(valueRef) } else valueRef match { - case null | Unit | None => null + case null | () | None => null case arg: java.lang.Boolean => arg case arg: java.lang.Byte => arg @@ -230,7 +230,7 @@ private[oc] object Registry extends api.detail.DriverAPI { } } - def convertList(obj: AnyRef, list: Iterator[(Any, Int)], memo: util.IdentityHashMap[AnyRef, AnyRef]): Array[AnyRef] = { + def convertList(obj: Any, list: Iterator[(Any, Int)], memo: util.IdentityHashMap[Any, AnyRef]): Array[AnyRef] = { val converted = mutable.ArrayBuffer.empty[AnyRef] memo += obj -> converted for ((value, index) <- list) { @@ -239,7 +239,7 @@ private[oc] object Registry extends api.detail.DriverAPI { converted.toArray } - def convertMap(obj: AnyRef, map: Map[_, _], memo: util.IdentityHashMap[AnyRef, AnyRef]): AnyRef = { + def convertMap[K, V](obj: Any, map: Map[K, V], memo: util.IdentityHashMap[Any, AnyRef]): AnyRef = { val converted = memo.getOrElseUpdate(obj, mutable.Map.empty[AnyRef, AnyRef]) match { case map: mutable.Map[AnyRef, AnyRef]@unchecked => map case map: java.util.Map[AnyRef, AnyRef]@unchecked => mapAsScalaMap(map) diff --git a/src/main/scala/li/cil/oc/server/fs/Buffered.scala b/src/main/scala/li/cil/oc/server/fs/Buffered.scala index 7a513e4ee8..c005462c45 100644 --- a/src/main/scala/li/cil/oc/server/fs/Buffered.scala +++ b/src/main/scala/li/cil/oc/server/fs/Buffered.scala @@ -79,7 +79,7 @@ trait Buffered extends OutputStreamFileSystem { read = in.read(buffer) if (read > 0) { if (read == buffer.length) stream.write(buffer) - else stream.write(buffer.view(0, read).toArray) + else stream.write(buffer.view.slice(0, read).toArray) } } while (read >= 0) in.close() diff --git a/src/main/scala/li/cil/oc/server/loot/LootFunctions.java b/src/main/scala/li/cil/oc/server/loot/LootFunctions.java index 5233f90e06..fbd8b77e38 100644 --- a/src/main/scala/li/cil/oc/server/loot/LootFunctions.java +++ b/src/main/scala/li/cil/oc/server/loot/LootFunctions.java @@ -6,11 +6,7 @@ import net.minecraft.loot.functions.ILootFunction; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.Registry; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; -// No registry events or ObjectHolder - this is to load the class. -@Mod.EventBusSubscriber(modid = "opencomputers", bus = Bus.MOD) public final class LootFunctions { public static final ResourceLocation DYN_ITEM_DATA = new ResourceLocation(OpenComputers.ID(), "item_data"); public static final ResourceLocation DYN_VOLATILE_CONTENTS = new ResourceLocation(OpenComputers.ID(), "volatile_contents"); @@ -24,6 +20,10 @@ private static LootFunctionType register(String name, ILootSerializer null + case ResultWrapper.unit | None => null case arg => arg } } @@ -209,7 +210,7 @@ class ArgumentsImpl(val args: Seq[AnyRef]) extends Arguments { s"bad argument #${index + 1} ($want expected, got ${typeName(have)})") private def typeName(value: AnyRef): String = value match { - case null | Unit | None => "nil" + case null | ResultWrapper.unit | None => "nil" case _: java.lang.Boolean => "boolean" case _: java.lang.Number => "double" case _: java.lang.String => "string" diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 52298dff53..718b5a12c6 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -35,6 +35,7 @@ import li.cil.oc.server.PacketSender import li.cil.oc.server.driver.Registry import li.cil.oc.server.fs.FileSystem import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.ResultWrapper import li.cil.oc.util.ResultWrapper.result import li.cil.oc.util.ThreadPoolFactory import net.minecraft.client.Minecraft @@ -45,7 +46,6 @@ import net.minecraft.server.integrated.IntegratedServer import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.server.ServerLifecycleHooks -import scala.Array.canBuildFrom import scala.collection.JavaConverters.mapAsJavaMap import scala.collection.convert.ImplicitConversionsToJava._ import scala.collection.convert.ImplicitConversionsToScala._ @@ -338,7 +338,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac } else { signals.enqueue(new Machine.Signal(name, args.map { - case null | Unit | None => null + case null | ResultWrapper.unit | None => null case arg: Map[_, _] if arg.isEmpty || arg.head._1.isInstanceOf[String] && arg.head._2.isInstanceOf[String] => arg case arg: mutable.Map[_, _] if arg.isEmpty || arg.head._1.isInstanceOf[String] && arg.head._2.isInstanceOf[String] => arg.toMap case arg: java.util.Map[_, _] => { diff --git a/src/main/scala/li/cil/oc/server/network/Component.scala b/src/main/scala/li/cil/oc/server/network/Component.scala index 28b8e5ca79..53954d2c00 100644 --- a/src/main/scala/li/cil/oc/server/network/Component.scala +++ b/src/main/scala/li/cil/oc/server/network/Component.scala @@ -7,6 +7,7 @@ import li.cil.oc.api.network.{Node => ImmutableNode} import li.cil.oc.common.item.data.NodeData import li.cil.oc.server.driver.CompoundBlockEnvironment import li.cil.oc.server.driver.Registry +import li.cil.oc.server.network.Node import li.cil.oc.server.machine.ArgumentsImpl import li.cil.oc.server.machine.Callbacks import li.cil.oc.server.machine.Callbacks.ComponentCallback diff --git a/src/main/scala/li/cil/oc/server/network/Connector.scala b/src/main/scala/li/cil/oc/server/network/Connector.scala index 485315f875..7311408dd0 100644 --- a/src/main/scala/li/cil/oc/server/network/Connector.scala +++ b/src/main/scala/li/cil/oc/server/network/Connector.scala @@ -113,7 +113,7 @@ trait Connector extends network.Connector with Node { override def onDisconnect(node: ImmutableNode) { super.onDisconnect(node) if (node == this) { - this.synchronized(distributor = None) + this.synchronized(this.distributor = None) } } diff --git a/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala b/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala index 4702e7fcae..d7454c6f29 100644 --- a/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala @@ -8,7 +8,7 @@ object DebugNetwork { val cards = mutable.WeakHashMap.empty[DebugNode, Unit] def add(card: DebugNode) { - cards.put(card, Unit) + cards.put(card, ()) } def remove(card: DebugNode) { diff --git a/src/main/scala/li/cil/oc/server/network/Network.scala b/src/main/scala/li/cil/oc/server/network/Network.scala index 29226e96aa..160e370cc5 100644 --- a/src/main/scala/li/cil/oc/server/network/Network.scala +++ b/src/main/scala/li/cil/oc/server/network/Network.scala @@ -8,8 +8,12 @@ import li.cil.oc.api.network._ import li.cil.oc.api.network.{Node => ImmutableNode} import li.cil.oc.common.capabilities.Capabilities import li.cil.oc.common.tileentity +import li.cil.oc.server.network.Component +import li.cil.oc.server.network.ComponentConnector +import li.cil.oc.server.network.Connector import li.cil.oc.server.network.{Node => MutableNode} import li.cil.oc.util.Color +import li.cil.oc.util.ResultWrapper import li.cil.oc.util.SideTracker import net.minecraft.item.DyeColor import net.minecraft.nbt._ @@ -652,7 +656,7 @@ object Network extends api.detail.NetworkAPI { def remove() = { edges.foreach(edge => edge.other(this).edges -= edge) - searchGraphs(edges.map(_.other(this))) + searchGraphs(edges.map(_.other(this)).toSeq) } override def toString = s"$data [${edges.length}]" @@ -708,7 +712,7 @@ object Network extends api.detail.NetworkAPI { } values.length * 2 + values.foldLeft(0)((acc, arg) => { acc + (arg match { - case null | Unit | None => 4 + case null | ResultWrapper.unit | None => 4 case _: java.lang.Boolean => 4 case _: java.lang.Byte => 4 case _: java.lang.Short => 4 @@ -733,7 +737,7 @@ object Network extends api.detail.NetworkAPI { nbt.putInt("ttl", ttl) nbt.putInt("dataLength", data.length) for (i <- data.indices) data(i) match { - case null | Unit | None => + case null | ResultWrapper.unit | None => case value: java.lang.Boolean => nbt.putBoolean("data" + i, value) case value: java.lang.Integer => nbt.putInt("data" + i, value) case value: java.lang.Double => nbt.putDouble("data" + i, value) diff --git a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala index 2ea00f3fef..17dca3e5df 100644 --- a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala @@ -9,7 +9,7 @@ object QuantumNetwork { val tunnels = mutable.Map.empty[String, mutable.WeakHashMap[QuantumNode, Unit]] def add(card: QuantumNode) { - tunnels.getOrElseUpdate(card.tunnel, mutable.WeakHashMap.empty).put(card, Unit) + tunnels.getOrElseUpdate(card.tunnel, mutable.WeakHashMap.empty).put(card, ()) } def remove(card: QuantumNode) { diff --git a/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala b/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala index 09df5b81b4..0f739e9d25 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedLuaState.scala @@ -37,7 +37,7 @@ object ExtendedLuaState { case null => null case primitive => primitive.asInstanceOf[AnyRef] }) match { - case null | Unit | _: BoxedUnit => lua.pushNil() + case null | () | _: BoxedUnit => lua.pushNil() case value: java.lang.Boolean => lua.pushBoolean(value.booleanValue) case value: java.lang.Byte => lua.pushNumber(value.byteValue) case value: java.lang.Character => lua.pushString(String.valueOf(value)) @@ -68,7 +68,7 @@ object ExtendedLuaState { } } - def pushList(obj: AnyRef, list: Iterator[(Any, Int)], memo: util.IdentityHashMap[Any, Int]) { + def pushList(obj: Any, list: Iterator[(Any, Int)], memo: util.IdentityHashMap[Any, Int]) { lua.newTable() val tableIndex = lua.getTop memo += obj -> tableIndex @@ -86,7 +86,7 @@ object ExtendedLuaState { lua.rawSet(-3) } - def pushTable(obj: AnyRef, map: Map[_, _], memo: util.IdentityHashMap[Any, Int]) { + def pushTable(obj: Any, map: Map[_, _], memo: util.IdentityHashMap[Any, Int]) { lua.newTable(0, map.size) val tableIndex = lua.getTop memo += obj -> tableIndex diff --git a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala index 5c4fed26c6..9244d1888c 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedNBT.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedNBT.scala @@ -276,7 +276,7 @@ object ExtendedNBT { while (iterable.size > 0) { buffer += f((iterable.remove(0): INBT).asInstanceOf[Tag]) } - buffer + buffer.toIndexedSeq } def toTagArray[Tag: ClassTag] = map((t: Tag) => t).toArray diff --git a/src/main/scala/li/cil/oc/util/ResultWrapper.scala b/src/main/scala/li/cil/oc/util/ResultWrapper.scala index fc844b560a..cb06793282 100644 --- a/src/main/scala/li/cil/oc/util/ResultWrapper.scala +++ b/src/main/scala/li/cil/oc/util/ResultWrapper.scala @@ -5,6 +5,8 @@ import net.minecraft.item.ItemStack import scala.math.ScalaNumber object ResultWrapper { + final val unit = ().asInstanceOf[AnyRef] + def result(args: Any*): Array[AnyRef] = { def unwrap(arg: Any): AnyRef = arg match { case x: ScalaNumber => x.underlying diff --git a/src/main/scala/li/cil/oc/util/RotationHelper.scala b/src/main/scala/li/cil/oc/util/RotationHelper.scala index 3cf4d2f2e7..46fba888eb 100644 --- a/src/main/scala/li/cil/oc/util/RotationHelper.scala +++ b/src/main/scala/li/cil/oc/util/RotationHelper.scala @@ -36,7 +36,7 @@ object RotationHelper { getOrElseUpdate(pitch, mutable.Map.empty). getOrElseUpdate(yaw, { val t = translationFor(pitch, yaw) - t.indices.map(Direction.from3DDataValue).map(t.indexOf).map(Direction.from3DDataValue).toArray + t.indices.map(Direction.from3DDataValue).map(t.indexOf(_)).map(Direction.from3DDataValue).toArray })) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/util/ScalaClosure.scala b/src/main/scala/li/cil/oc/util/ScalaClosure.scala index 56fca9c3a4..33955e137d 100644 --- a/src/main/scala/li/cil/oc/util/ScalaClosure.scala +++ b/src/main/scala/li/cil/oc/util/ScalaClosure.scala @@ -34,7 +34,7 @@ object ScalaClosure { case null => null case primitive => primitive.asInstanceOf[AnyRef] }) match { - case null | Unit | _: BoxedUnit => LuaValue.NIL + case null | () | _: BoxedUnit => LuaValue.NIL case value: java.lang.Boolean => LuaValue.valueOf(value.booleanValue) case value: java.lang.Byte => LuaValue.valueOf(value.byteValue) case value: java.lang.Character => LuaValue.valueOf(String.valueOf(value)) @@ -64,7 +64,7 @@ object ScalaClosure { table } - def toLuaTable(value: Map[_, _]): LuaValue = { + def toLuaTable[K, V](value: Map[K, V]): LuaValue = { LuaValue.tableOf(value.flatMap { case (k, v) => Seq(toLuaValue(k), toLuaValue(v)) }.toArray) diff --git a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala index ccbd576d5c..2805d6b31a 100644 --- a/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala +++ b/src/main/scala/li/cil/oc/util/ThreadPoolFactory.scala @@ -12,14 +12,11 @@ import li.cil.oc.Settings import li.cil.oc.common.SaveHandler import li.cil.oc.server.fs.Buffered import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent import net.minecraftforge.fml.event.server.FMLServerStoppedEvent import scala.collection.mutable -@Mod.EventBusSubscriber(modid = OpenComputers.ID, bus = Bus.FORGE) object ThreadPoolFactory { val priority = { val custom = Settings.get.threadPriority From 589235e7cbe238dc1fb45c235c2fdde5b0819edc Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:00:07 +0100 Subject: [PATCH 135/159] Fix serial provider getting registered too late --- src/main/scala/li/cil/oc/OpenComputers.scala | 2 ++ .../scala/li/cil/oc/integration/ModProxy.java | 8 ++++- .../scala/li/cil/oc/integration/Mods.scala | 33 +++++++++++++++++-- .../integration/minecraft/ModMinecraft.scala | 2 +- .../cil/oc/integration/tis3d/ModTIS3D.scala | 13 ++++++-- .../SerialInterfaceProviderAdapter.scala | 5 --- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 1fc79c3e81..b6895b6c78 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -6,6 +6,7 @@ import li.cil.oc.common.IMC import li.cil.oc.common.Proxy import li.cil.oc.common.init.Blocks import li.cil.oc.common.init.Items +import li.cil.oc.integration.Mods import li.cil.oc.util.ThreadPoolFactory import net.minecraft.block.Block import net.minecraft.entity.player.PlayerEntity @@ -66,6 +67,7 @@ class OpenComputers { Settings.load(FMLPaths.CONFIGDIR.get().resolve(Paths.get("opencomputers", "settings.conf")).toFile()) OpenComputers.proxy.preInit() MinecraftForge.EVENT_BUS.register(ThreadPoolFactory) + Mods.preInit() // Must happen after loading Settings but before registry events are fired. @SubscribeEvent def registerBlocks(e: RegistryEvent.Register[Block]) { diff --git a/src/main/scala/li/cil/oc/integration/ModProxy.java b/src/main/scala/li/cil/oc/integration/ModProxy.java index a33ed4bdc5..fed1dbd932 100644 --- a/src/main/scala/li/cil/oc/integration/ModProxy.java +++ b/src/main/scala/li/cil/oc/integration/ModProxy.java @@ -3,5 +3,11 @@ public interface ModProxy { Mod getMod(); - void initialize(); + default void preInitialize() + { + } + + default void initialize() + { + } } diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index 3fea571c55..0acc5287b9 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -13,7 +13,8 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer object Mods { - private val handlers = mutable.Set.empty[ModProxy] + private var preInited = false + private var inited = false private val knownMods = mutable.ArrayBuffer.empty[ModBase] @@ -51,13 +52,39 @@ object Mods { integration.opencomputers.ModOpenComputers ) + def preInit(): Unit = { + if (!preInited) { + preInited = true + for (proxy <- Proxies) { + tryPreInit(proxy) + } + } + } + + private def tryPreInit(mod: ModProxy) { + val handlers = mutable.Set.empty[ModProxy] + val isBlacklisted = Settings.get.modBlacklist.contains(mod.getMod.id) + val alwaysEnabled = mod.getMod == null || mod.getMod == Mods.Minecraft + if (!isBlacklisted && (alwaysEnabled || mod.getMod.isModAvailable) && handlers.add(mod)) { + li.cil.oc.OpenComputers.log.debug(s"Pre-initializing mod integration for '${mod.getMod.id}'.") + try mod.preInitialize() catch { + case e: Throwable => + li.cil.oc.OpenComputers.log.warn(s"Error pre-initializing integration for '${mod.getMod.id}'", e) + } + } + } + def init(): Unit = { - for (proxy <- Proxies) { - tryInit(proxy) + if (!inited) { + inited = true + for (proxy <- Proxies) { + tryInit(proxy) + } } } private def tryInit(mod: ModProxy) { + val handlers = mutable.Set.empty[ModProxy] val isBlacklisted = Settings.get.modBlacklist.contains(mod.getMod.id) val alwaysEnabled = mod.getMod == null || mod.getMod == Mods.Minecraft if (!isBlacklisted && (alwaysEnabled || mod.getMod.isModAvailable) && handlers.add(mod)) { diff --git a/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala b/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala index 88e1e39c1a..9844fc89c2 100644 --- a/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala +++ b/src/main/scala/li/cil/oc/integration/minecraft/ModMinecraft.scala @@ -16,7 +16,7 @@ import net.minecraftforge.common.MinecraftForge object ModMinecraft extends ModProxy with RedstoneProvider { def getMod = Mods.Minecraft - def initialize() { + override def initialize() { Driver.add(DriverBeacon) Driver.add(DriverBrewingStand) Driver.add(DriverComparator) diff --git a/src/main/scala/li/cil/oc/integration/tis3d/ModTIS3D.scala b/src/main/scala/li/cil/oc/integration/tis3d/ModTIS3D.scala index ee83aef8c8..83cc319bdf 100644 --- a/src/main/scala/li/cil/oc/integration/tis3d/ModTIS3D.scala +++ b/src/main/scala/li/cil/oc/integration/tis3d/ModTIS3D.scala @@ -2,11 +2,20 @@ package li.cil.oc.integration.tis3d import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods +import li.cil.tis3d.api.serial.SerialInterfaceProvider +import net.minecraftforge.event.RegistryEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.scorge.lang.ScorgeModLoadingContext object ModTIS3D extends ModProxy { override def getMod = Mods.TIS3D - override def initialize(): Unit = { - SerialInterfaceProviderAdapter.init() + @SubscribeEvent + def registerSerialInterfaceProviders(e: RegistryEvent.Register[SerialInterfaceProvider]) { + e.getRegistry.register(SerialInterfaceProviderAdapter) + } + + override def preInitialize(): Unit = { + ScorgeModLoadingContext.get.getModEventBus.register(this) } } diff --git a/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala b/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala index 97f1e82cfe..f17b89a002 100644 --- a/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala +++ b/src/main/scala/li/cil/oc/integration/tis3d/SerialInterfaceProviderAdapter.scala @@ -30,11 +30,6 @@ import scala.collection.mutable object SerialInterfaceProviderAdapter extends ForgeRegistryEntry[SerialInterfaceProvider] with SerialInterfaceProvider { setRegistryName(OpenComputers.ID, "serial_port") - def init(): Unit = { - //ManualAPI.addProvider(new ResourceContentProvider(Settings.resourceDomain, "doc/tis3d/")) - SerialInterfaceProviders.MODULE_PROVIDER_REGISTRY.get.register(this) - } - override def getDocumentationReference = Optional.of(new SerialProtocolDocumentationReference(new StringTextComponent("OpenComputers Adapter"), "protocols/opencomputersadapter.md")) override def matches(world: World, pos: BlockPos, side: Direction): Boolean = world.getBlockEntity(pos).isInstanceOf[Adapter] From 82dfe4fe5d444f9c57d8c4e3968ee316b6717d9f Mon Sep 17 00:00:00 2001 From: KosmosPrime <5663514+KosmosPrime@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:59:17 +0100 Subject: [PATCH 136/159] Switch to automatically built JNLua and LuaJ This reverts commit e269289a94c555cf8e0409ce7eb6c3bf1640a6a4. --- build.gradle | 32 ++++++-- libs/OpenComputers-JNLua.jar | Bin 133597 -> 0 bytes libs/OpenComputers-LuaJ.jar | Bin 456431 -> 0 bytes .../opencomputers/lib/lua52/native.32.arm.so | Bin 682388 -> 0 bytes .../opencomputers/lib/lua52/native.32.bsd.so | Bin 714709 -> 0 bytes .../opencomputers/lib/lua52/native.32.dll | Bin 947200 -> 0 bytes .../opencomputers/lib/lua52/native.32.dylib | Bin 442148 -> 0 bytes .../opencomputers/lib/lua52/native.32.so | Bin 709120 -> 0 bytes .../opencomputers/lib/lua52/native.64.bsd.so | Bin 819437 -> 0 bytes .../opencomputers/lib/lua52/native.64.dll | Bin 1202688 -> 0 bytes .../opencomputers/lib/lua52/native.64.dylib | Bin 431496 -> 0 bytes .../opencomputers/lib/lua52/native.64.so | Bin 795962 -> 0 bytes .../opencomputers/lib/lua53/native.32.bsd.so | Bin 770387 -> 0 bytes .../opencomputers/lib/lua53/native.32.dll | Bin 1341440 -> 0 bytes .../opencomputers/lib/lua53/native.32.dylib | Bin 486228 -> 0 bytes .../opencomputers/lib/lua53/native.32.so | Bin 758731 -> 0 bytes .../opencomputers/lib/lua53/native.64.bsd.so | Bin 872342 -> 0 bytes .../opencomputers/lib/lua53/native.64.dll | Bin 1676800 -> 0 bytes .../opencomputers/lib/lua53/native.64.dylib | Bin 472560 -> 0 bytes .../opencomputers/lib/lua53/native.64.so | Bin 851843 -> 0 bytes .../loot/openos/lib/core/lua_shell.lua | 2 +- .../server/machine/luac/LuaStateFactory.scala | 72 +++++++++--------- .../li/cil/oc/server/machine/luac/OSAPI.scala | 2 +- .../server/machine/luac/PersistenceAPI.scala | 8 +- .../oc/server/machine/luac/UnicodeAPI.scala | 6 +- 25 files changed, 69 insertions(+), 53 deletions(-) delete mode 100644 libs/OpenComputers-JNLua.jar delete mode 100644 libs/OpenComputers-LuaJ.jar delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.32.arm.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.32.bsd.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.32.dll delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.32.dylib delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.32.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.64.bsd.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.64.dll delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.64.dylib delete mode 100644 src/main/resources/assets/opencomputers/lib/lua52/native.64.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.32.bsd.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.32.dll delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.32.dylib delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.32.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.64.bsd.so delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.64.dll delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.64.dylib delete mode 100644 src/main/resources/assets/opencomputers/lib/lua53/native.64.so diff --git a/build.gradle b/build.gradle index b3029396d3..8847b7f14d 100644 --- a/build.gradle +++ b/build.gradle @@ -61,11 +61,7 @@ minecraft { workingDirectory project.file("run") property "forge.logging.markers", "REGISTRIES" property "forge.logging.console.level", "debug" - mods { - opencomputers { - source sourceSets.main - } - } + environment "MOD_CLASSES", "opencomputers%%" } } } @@ -83,6 +79,18 @@ repositories { includeGroup "li.cil.tis3d" } } + ivy { + name 'asie dependency mirror' + artifactPattern "http://asie.pl/javadeps/[module]-[revision](-[classifier]).[ext]" + content { + includeModule '', 'OC-LuaJ' + includeModule '', 'OC-JNLua' + includeModule '', 'OC-JNLua-Natives' + } + metadataSources { + artifact() + } + } maven { url "https://dvs1.progwml6.com/files/maven" content { @@ -125,8 +133,9 @@ configurations { dependencies { var jeiSlug = "jei-${config.minecraft.version}" - minecraft "net.minecraftforge:forge:${config.minecraft.version}-${config.forge.version}" + runtimeOnly files(jar.archiveFile) + compileOnly "org.scala-lang:scala-library:2.13.4" implementation "net.minecraftforge:Scorge:${config.scorge.version}" embedded "com.typesafe:config:1.2.1" @@ -159,7 +168,9 @@ dependencies { exclude module: "CBMultipart" } - embedded files('libs/OpenComputers-JNLua.jar', 'libs/OpenComputers-LuaJ.jar') + embedded name: 'OC-LuaJ', version: '20220907.1', ext: 'jar' + embedded name: 'OC-JNLua', version: '20220928.1', ext: 'jar' + embedded name: 'OC-JNLua-Natives', version: '20220928.1', ext: 'jar' compileOnly "codechicken:EnderStorage:${config.enderstorage.version}:universal" } @@ -228,7 +239,12 @@ artifacts { archives javadocJar } -jar.finalizedBy('reobfJar') +publish.dependsOn('reobfJar') +afterEvaluate { + prepareRuns { + dependsOn(jar) + } +} publishing { publications { diff --git a/libs/OpenComputers-JNLua.jar b/libs/OpenComputers-JNLua.jar deleted file mode 100644 index b27363fb26456ca13a8703e7964c5eb752953207..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133597 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 diff --git a/libs/OpenComputers-LuaJ.jar b/libs/OpenComputers-LuaJ.jar deleted file mode 100644 index 1d2cc019b4d10fe67767e94cd4e4781e2ce08659..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456431 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 z 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 z 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 Ta6NK0pK50Fa2Oa){_Fn%Fp2^T diff --git a/src/main/resources/assets/opencomputers/lib/lua52/native.32.arm.so b/src/main/resources/assets/opencomputers/lib/lua52/native.32.arm.so deleted file mode 100644 index e21c6aa41212fca76179d60c9d46bd1e7b110dfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 682388 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 z

    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ⅇ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±nQ|BBc+feaW4&>Ne!FH!@;X*EgW`2dtlScj9J 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Ξgqr*E`*aLpOKQp9Gb? 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

    `xT0x#`CsGR; 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 QR$(WzmrWBBY>_PdAGDB$t^fc4 diff --git a/src/main/resources/assets/opencomputers/lib/lua52/native.32.dll b/src/main/resources/assets/opencomputers/lib/lua52/native.32.dll deleted file mode 100644 index db48708e322e5ef445e8a81076208855d85fe169..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 947200 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

    Q_v~yLRcQWM;b1j0q?=dVBH{|yTF-b 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∈B%zxUx4DcGC;yO5v zHc@;1s9V17`U7-WfB5!Mo%IXouzpq3>#ToV`5OJNyM6+_ruh8Zl zuQ~tf?%!PfHRdn!U;Bx(zy51K{oMYpyMMzvP}6?@ul+>M zPrUsH>vYZazpniV^qTueUHJk1zxI>d{`jx`RQvqO>Cb=dr|R>||L^T5VqaC?2FBCB z_NPLv`;4-{#i(|w9J4z4%=5898np*+{*bS5V)Ld}k&%eljVFGfKThoC(Ch9{)b8lo zwnx`)`+-hc;_W?oyZpBmK6V4s(O=c&9Op&}UnQ)l#?=!?+zr?<6|GRwyFV=P2%2GW z$v{bS2Ab;pZR~}v`RJ(9v81UJ5|K_~$AcYu??Fvk>U#e*FcBF#IZjVcJ&BA*D5}qY z9Ebz~!Pd&p)}zOFU#b=;1fYf&+iUOKybG-}9FcLX*AbNcdB#A+iNol*sTkcEwQ%#J z2kF&(@cgb{-D!9u^R3nC6+wn}Llibue@YyctMuQ)gK-i*u;B{E)!4OpIQJ+Uaa9 zIEVsT&Fp+|e+nu-X4rD>rGsdgiD%)*#0yC41@g}QXjILw`)URq=7Z+qVrFm}sTg9DRWDI(PH= zFgP1Uwkh%6);tIOHSwhGl$Tj(>)AC$5kqs3dzbF>mf|;Hx|i&e`7b(y4sS?r)$ZUT zB$}3v4mUl8?gxzQsWc`RSuMNTen3nlJr@?EfNg2r7R)_V%?Hn~ZA__`*SE{n>4W-s zZPiz}P>qk8(A3II@eDFr@pi=>{DR_y)6ad}OHQDt`X?6OdHV?6K2~pd>_{mRbni9o zkkVcBwx_*|ZSD~?U*zm^T>TD`4hueT?#Tf(wsPIV1+rYUsAZ?SYRmSbZUyztq%*J6 z^Wt3;qLp#5$LyGD`C4$->CaU2!Sk!|wEpF09}23|9bRm(?|bk;HNIv?sKti3d+1`j z9IGRhPmy}(_8a%c-9ail9K6btUZQRLG;`Kx+(KIAD(r#}PmuBRAp?AK@I1_%jm(xk zK}tOiKk90lhk9=Ak)EAbZ%<@BB5BohuA11C8oY~C#Ig8O*NsP_y1^27@ z;F%siVB5Klqm@KFK5CQoORL2y)%fs|O^Y5`G!tF&^LLq2{t;>1UNwC8R&`M;=2SxV z#|rfLMfcR%-!(-Q9&^8&$5)~`eySl&*0vB0i#aktqmU6jurKK7v$D15YScu0slUD` zZFZOR0pHq*@-y>Vx6V)$xp#ijm9cw;o(blvAC4SH<;#c;JD5E-c$&xZeDN^YdvTEW zVx4OK$ogwgjq6!lNw-b!&f_Ao{+f!&IbBiIr?rFm;&v9IZCl~fSVY!eD-l_L)kWHZ z-kYu-vlTTQv0HJ(?&c!0{`M1*^|vjp_lNbCZ;z?_$vy1kR{qMYx*yTQJ~w%FSWrC< z>NE9oN{=>XU#@wgmoa=BYot5Gu(|3d>p_cJ=C;E^>6d!>!|hCgVvpVvTdC+VhYQL zUv<99wzf#f@IBlC`xVINT7XmAMQOJvAEZCp$tEz(!6m+0b&*Mz)z@NSfmQ*VY~U+x8gYum<%mxjd-lkekx&M=Z+Txro^V>*k+Z z_)=!QxBc?@C&e+S>4-6A>zuYqhou`dw%g#zK1se)pnY(W^{{bcZ#8UXhueA8^~JFZ ztlulWq%b`$z6Hl{)#9WA+h_Sd@O!mU)sf_Ode!`<#SY3YEN)iKuf$a)Wxn4h=~>5F zQ)F${u@yZ7_q@q1vNqk^rt7QNmFWGl8-OIJU-2O!y)O0JsqqNBfHk+s`D9k*uv(l*?8{# zZ;s!HciPj7gMZ}LD|_k0VSU#kA?ArJB=BLfuY<;bZGJqyDR%*H~Xo?^bzhYarScKR#fBQ@XT`x9-ygGQ>(= z3|qU?GRnH5$BU;uR+(acd*!G0JHkqCoIF`RS`*%P=RSISDocV7@b;8ke*-VIIX%Z- zv6|m#tKIi*jQeWMmor>!yE=R4P3ikDDf&zGX0vxX%<62Tb>8~SoYuQ7moGs>9u@S+ zoscb!HVYoQb%Gyz%3j$wf8bx%U$z!*a6IdeR_>h_zW#B7RJnoc{lVpCY|!JX6>k&* ztnF7%I~;tb2Vz8pMgEs}v#FWk3X+xT)%;f6KJsi|>W}{RJM%oxw9tKje7^tQzwiI``ngxm zo%76c=FFKhXU@z#v-*lj>7e8_jX!P#pSB*oRVC{#&RHk5&DO&A5GVWI!Gq@}KTn2x z+=voE*P_XzmX2|~nD#CrYr9nk+4K2|4ZCTMH92jZ?6oD6dg*ftFV_Fqb=9P@a(XGA!r7h7Q0f?)E2me^_-4Y7Zy;xUGvUWKkTbrS@Z%fE8Q*{(-^%Hg zH@@-qLpi;2><8fO2asbwnDF)k$gv+xc>4k5*bl(l59RdAvmbc-tejps_8IW@8OX8E zOnCbYynP08>@(o)vkO$8$+ORR`!}A(lVktV=kapv-+12sjhAEp#`E@Xyd3*i zM$6}+=`Z2(AV0r|rwBRoi+Fy1A(Jz|i09`QGCA{$cz%8nFK2!sjNqGJbw6lQTb- z@$+MuocXbgpC8NQ%#UUK{Fvu&A;_B_^Zti&dgb^ZWW4`DCddCET4Z%H;Sb zWxRh9r7kJs#KZP0iW$dfc_jK~qo9vv0|J z!>p}4EVncsYE-9B`tHd77VLulyZ*;}vd{T(ip*oau`Xw-FL@Zo!HRW%E*wGSVKyI+ z2Nmz%aNhO&b;6fSA!C@O7N->j%{!Ma{5Ig4khyZa({tA-*xaq{WY2H;LZ*F-p+9#= z!PU892gCeVLT0;RsMhN!_@Xgw`M2h8g-jcbSj!G4z-dLZotH(qLPq&VaA@fiYw06%4aCms_BY*}sg-qXNjxzUXm@#u^@g8_2WP0T`D%cwhv$}3oN&A5LwP;>XkQEJG3`~xj z9laxD4s;%3*D(eTw~U`-)hSxYOz3}efj9bUFoN%C<<9UOf~to%0hWdYl5I6s10sGUA2Isl?Y? z_Md{T-qAl*9j^$PT&Z))n^R!9vp|2?fX6~+O`d+^&Zl9t*SBj+nqYZGK9fg!o`$jk zU3WNMM*j9}&}hwp(~y?9P1Uz=mXJCBrPIT=ry*x$aroiS2|^~p{#X;8GtjEZ=%>an zaerff(<9DaXP{fA#MEdazK1*NjrNYnGm!c~v!F;B^E)lk%*s9kKi2mvOgr{c$Q)%h z>2-;PI^zy)KQFr~WKyn$2Ybgt`mT3b4UG$gj9qreD+goYyGOexra$)y8Qpbe8t-F4 zboAEK;JEifX7Gm(6LinQabKNv!jEr+%$yzrj!!xZ5w(kLBP?DBneaL76b_#SVM_OT zk!cvefBn4{eTL}epNFH(RydEfj1e-mFLhn59|z7W1x+VQ zFuuj=rzMg&NOd@eWl8Jg+S{YL3;LZ(Ohr}>lPq1%y; zPo6Hr`uhKPQ2$6gG_fDl1k|1jnISv!x8=sem=Mndk1V{mtg!aP?s}I%<>RNt#T~GH zUo7c!-S-mQ2(CTnNIjIVH**(?j$Hy5t@J_hKd^is=Lrf0mtf`bZKlcx@t*3{(-Ze~ zzYN-ueOr8(fbV4=@0D-kcNttY;|iSFt$j+t3H8+rqv;(2u7KmJo1Z<^u>Q)gzGg;Wfz4;y1)qpU z`iAPSJzR1HtRh;DQ7^;#>%Jcm-0LdjE&8r+x*E$5%eNeY_k`Q1re-ZH#{Lr9eo5Oi zSK+ntB-yF5Vj=Tws?L{>SK)a0wESjT*q+|`y;J+*`ot1wf8he&`z+jW{Zi;P7<}ZF zwC(Fcl<$S_j-I;)S3<;=H$P(jud`3={CW+N2Y>$Q@C@Z+d$Hjr;dO9&qxvkYCGsQx z-SqV{ufx7ASqAftpnf^tX7{Fx*Wu~y?;_KA$PeX$(-A+f!+uSJeQP&f5;7493CB%u zz^*3s-Ws;S@-uptU7d3S=11y_S~+6=?N3;wU%mm`tu{n1E63Xr=B!w;Y}D7Ztz^(1?#(VWzg~)H{r+b^>5W2ZVH)$ zcedV8xdo5zU)l>xK4JSFbygjE3)tkK(Io^Ri77Od&3=&p?d>;R33JBs8n`yp6WoPCC;P9h^9toZ zUSmdv=q{KY6CRJ!{wieNPQSHt&t2&1{QjWhU>u)(pVSj)-31fJ+Nn$Hpgd+t$M@)b z4{GmP$yhzb{1YP*>UiIS!QWFfqEoPcq`#169l8gC)MxiLOR#lf@54M3X{$*`u)pd@xkUNh2SfY9=*^d~{;|%xj-R{_n(KDFzv6@ZE8aBg zOv!za>W7?6Yl_c1_N?vRIuW)n{M7iubUZ3&BsluXIuRP)neEj6T5V&-RpDNTRf({o zP@$Enr>Zg2R=t7fK_U#!YEWzMGz>2ue?6-415nr)vAy@Add7_O+0e#L4`ArZV!=sQ zWn-qZQOV-)2hjc0>cAH!SfAj$R*h00fGB5TLds*5mpv{mj<!8qS&qLVI%ih<)2;+}hxQBW35bRD(YjeEdEFO<}u(VJo2{K+Z zKRIgRE(L7hQ~kr zOoqvJ8cCKbaQsf}ZN6pDBe=FxE$xU4+B>}>SB+Wp2yX6LGFe#{<1fAcspp+XutK+M zh_?=oUnd4ma=D9tikpGuJRpsDg zn7*yO?|C!S*Fux9vw4qUOI_U-;XSZ@@4qWt)Z+=b4Zj;I9o)c}ar%5eAovON9oF{D zGHujHN(a`>jC%ru-YWV;{zUn{e>Qrb!c!<(wNX!TZz_&=s}AN`K80HwmLGmK9rbUv zcA?$!r;xTZvWwXrq(8ia($fb|;l`JoOIKRp_+60gHoN&V072xBSr1U&_N`rIIsO^M zZ9mZIR7QQgw_a7X`<`de&uD#Qp`Js(cNo^aQ+HJt%+CkP#NAWjyIJpT?Jbes zAcNpjL8-8K_~x9AtFe6#PoAg~mkMjren#gu!v1A6-&j^F4TkwGQ>=AK(U=*mwQZVJ z8fagf=@8rt^XoxdWYkh zTS8GE*u9*aE`AQf$Lr-LiqRg>j;uBI=yT}#ddK^tA5ovZIzRt=$#d9NGIESG6ZtR6 z-G5W~0u1%ktja>tgp9`))eG}qfU?!m)8h{yy$i07NKJSFd3T$?cT$G?OFb2q>7x`y&D>-?k7{+AFs-YdRU0|o3Kqdk=hUP9d$_7B|Z zqyBT9S^H<}bf~lWl=id3_z2Ww5!VTAe0vunDvHfDByFEkdXL;sOc?q93|>Le z+B1%cZE$>S?EcDT`YSklKE*BB5bNXdedpxsufTgm%tD1OO^lg+(Y|F3GhjtqwOfUD zC~rPv(?5*QfVK(R*S2|U7&F1$qumc>z{O8(Vh7Db`nKrY8u2j$g675IxK zUxRWZ!MT>5+88ru772E*eGT3}1{u~JkMvl0*KYLcH8}4#ZsM7QVcYey)D1FWbe#e3 zin5UZZ3N>t&d!9kOZOb~{f_O|YI{`7{Y+RlFstvfP;CG7KG%H&SzzC+_r0tfO=G+@ zLjUTNEa*CM`6H!N+^{( zu)mM-wK{n5EhraBZF5ZVdDOGf@0(^r#_SXKWs0bO>L|=tmt;fG-NbN+Mt*c$JGteJ zY#3uPT(9#4q({Ttp>_Lr(0|P3+Y;ke$e&V!HZ$Hqox~9@z8%1OcfX&C(S7m`d}mtK zc{m;OpZ!T}+v`1a{W5cmRRauXjEvd3?md)FkhX9efc>#n=Z39|--B9EOn3iR$j@*0 zQM7Vkf%omi=Z2{NlyS8kRxXU) ztP-Kx3j6EU45gCHT$pOsrry^BkAzI`vc(;S=fQ_{4GY^pL;a!FR-?(8JecD0DY#(= zw5QH*diF{yA0D3AyxT4l`_tSLjgPF(hsiCCkGC6yaZPLpn~ud+2vw6I_3R_t19_ zx$8O<;myUaU!(Cp`n;ekt6C$}8I#m*4&FDtgtxwx#_YrU>0OG$ zdnh6_S?F+h0^U=9IY_l#EZ$Q;r%m{Z1_+G;)63lPzWSAQFOP}E`|5QxG|#Fc9A$O4 z;b^?KzDdg%_x*Tp{gdg(P88w2^)pnqeCm&|d7*v3xp;s5i|?@kH}L-YTATJAP(!%l zTxs$!yvH6C7JOZb_t^Vv*Kd6j@3DV8=59|#gsXN8un^*X_P0IkM+D%xwyQ()M@Hg( z_G_nQS*PNC_DZsbrs@c%hYssH0PnTGvS(6#FTB^j;J)R%wRo?+p3a%`=kZ?q-IBEn zGVoq|)9=SD>mb~DE#pTQyx%^_R`J--`vovamSyja_uJ3DIiYX?-f!RZyxF{ngaYuM zAyz+&_uDUAyk*&ayx;y~Q4Eub_uFp}Et*`0_uIQQ%e+$?p>x)@_DydTK;M4zeA?qZ z_a2SBj&#L)?xSjJzUqnh+;^;`k$O;TzzJGKD&^zLOk=5zsMZt2ujFQx#3H+4??cmn0% z)$Ebyju$}MV811SM+>0nP>;Et4kJBfpZg_77JymH567nLM|t=OFV*%Iz|_SGx3=yo zfFpBm5vST*{{X$!S@di zt}1|+8#|hQT8?<7osK3;3!ulLZW=Qd6+mLzHs2HTF<#i|$uH& z+s!C|OJ{8x_Y5t7<(e?lWNHCy+%aUvzyOr@tF`M3{V+e5x(52b1rRtt{bln>*zR*x zT7UMyc6{{x#RE5#-?lxw?4DQv3C$XR^d4UT(eoM0E@KK{Sr4bi=}rZ(|CPtMmG)R) znAz6Iwg57oq&hvYM!C?qP|L%r0D?5^+kLP=d6eBa7-UuehB3mOg(d~CUFXg>*8$kR zdGllmeF|Xlcxk4dQ32GqUE#IR0QsT*Xh=%80@&QY*W5N;3c%{=x`yLAV*7TxSG=Zu z0lbfj?I>$g05elUubmf$9(A5v|c~Ef_&(>_O0E`H~Cl?0k4@E({qHln)cfnZ|#bln-6T557KSVm{Qh zUgUPoF&~Z%4e~1HMqA3B*AZ;xn{ z4?d3PU%acG4`(b4+||G4!LZST7?pxN_}pJo;y?_MRMAnPM_Se^w4CXXvdtG%5#HPZ$?4+8_t|+xhM3qml!@yIiLYeEA** ze=+JDaOyo+Jq$f^bLo5NSnJSpW0&_(pgd^Yy&mu3=7`AkLCWtT#rM)V{m1X1*^lAZ zOAfq)rHkH5F5`Z~VZ$OjueE#!bBcXhO=yDS`Qd$9f>+s4wCUiD2S>7D;j@{&eS)*0 zO`D(nvrX|lbe}POO;oc%d%*>PN8(!$ckbddYU5j&W^P(vb?jSMf8f)kDQ(|EiY+sA zVfq`eJbUn_aMv417}p9u}eg!)@Ocnzn&?mpaj_G?)6J?Z2D z-PfSz@a)FhqzouZD{dc+`y!vTOPrq7HUqXFGg#~t{|XlOZDaLx!YeqMeSF}6FX<4! zAt89s+H|-*ba0;&ozmfA=Z4Bbl9zDe#M~}T7QF!9vi(~_H$I0iif6aRZB2tiD`q6! z+>i?0v@X6KIqwLJ)p$(f}2FcDOK)O|4e++Aqsr%|}K+b!7K$VV$>$rW%L9lfM<%W3p?HtE@E z{~BQnJ>rl5jQ(}@6UIXV=zF9o#J3#(oXwx>gP!as8qWBpp7u@M5ASv_(?6HJu^fM6 ziYETs$?xyJ1|xE>+T>ncQyyR2KO`h1auICVOg_t@%6@Vzh@*q2`TJGl`7rPQS4F=%`w8PA0o3nL zA?yFI^ql?4uLsEZ^#B>a9w7U7*8|e|^@=}T7bxt*(Ot-|7ZlE>>kSmrbr2rP?AVIY zbF&OH^^oOSZL7<0WGz{*xFuWqqUY!b7H6Qt=y|PgOo@}-mYm5i)i?p0!w0DKI}t5o z_P_3Km3aufhBZ-Yu6ay$vDFgyUG4TkU&BX^ZJI~QcJJ04(thwJxbtCghkM$)Wcy;% z4Q6O~z=dHa?3^SEWjPLnfQs^GSv6Tr%5UMhi7mgu5VJi4u>ATbWcgRIe3caqroY>I z46cWKnwPRATBcA->DQ*hFntdvT~`s9a*-%x-;<1}2%f*DCa3Ni&e&LMIDd|V-l@sd zQYM#Fl8Yh9rdlOqXVG^ zD}}4fD8y33Fb!!>4uN8Lq_A5Z;~)0gm{x1jH~6@@#jKr&o0;}H z5uEm6+6S4+Kz*6dv@txNe=V1UGu0v$XF%(ZBlY?!n_zujoZdca87Lck{h>O!uQ3z0 z@x?K}_0Zu zf#Q9xcaL1{j`Y5p5TJY-cFx?@;r^B8nBUM9!IRIxG)Y9GY(?B>7N2QTHzgM4TH7RK zF<2i{ZLOG|=b+apPb0sg-kAP?!|4s@;fBpr?Fsd9pHup~TAkj#@&6$nGfVml5}O#k@v-CB7|q zIr)9hYJu$uAwKL2e2sq?! z?OlT9`F)=8X7L^Hj|kD~@DurEs(JA{8e$=lZ?fd7#<>n%^?zRuN+ zMPD193X6ZOc7a zR5WLGX%0SLy?&~5yZiY5q2}*%j^n;)ztnbNo9~0y#t$E7254b@R4vA;CBp2E_8rDf zMt*01?GrX65w15ndv@ISPKJvT;aIV3<}(q}7dxrVlI?%NM(wK7eKCw9EuTq-V8v%T3cBz~*s!?L$AH{5u~pn0D#`%$onx z)#N_%Bes9j)WQewvc7r;edo@`%n-8?^*cR;&+Yc=Xv}Jg`8C|{>WcfgTdM1~ifUxc zJQ)-}CHx_T_Std7yI)t7-?~#TJ$eW-l?E4Iyzh_laQ;BFdJ^<_a=PW_wb-Al(_fYJ zAFClo^7@d6h1t(o{Xt=R7#F56z8x2)Fyi7-6{hjSEv4Qkul3r!eds7p9Pz$Au~UWW$9i3~A1% z=b_(g?mbx)rk~`(6h4{7g()0j!-Xk~#d8EiE+~vZ`6OWq7hmDR6#8xC!W24tb72Z& zfeTX@(TEFE$h=Er^QZ91WiCu%-_2Z@LX7|}Orf6@7p5@26Bnj1oZ-S0rpKT@zz>CC zQZ7uPK$i?||ry7p-YU8#7EbP84Xy&8g z*Br0O4)Auh{j`PQXh(x4n{~RZ+VbZXaCA_p!iOnbo6kL`5cdbN`A~Q)j|)?1#)m0< zlFL1((4P-eSf3A5cqE65PoWJTrtkxMPY1(L7|w?&G~>e*=Dy?dqi_o!rf>)!rtk}U zPYAE%`8owfHcFm)Uzv7>2^Re3(KrK1^Y4 zK1|{5EUtVCSMp&Bo%k??ZTT>TZ`gZQ7>2?qK1|^>K1`t{AEvMgAEq$lHAfGHC;2di zbNMiZc6^vZZ9Yt4Sq7IMg}3=Ig**5#g@JsS!l8VaLIEG9@Y5?Ue+m=%FoltPn8LYy zn8I;K0?=cl@8-!hjwJoPvLm$WszM;4-eXxi|Sf%rgke z-_v#d>U6vaX}H&N&;Yd8!;)w0cpMLzRw47XVuYA}qI%Ni%kWesEtDCK`GraSmyNgz zFP(wG*) z#}Gf&;bW=*=NI97VevaW$M<27=jY>CUxOt}lfs|CsP1j+c$6W3CMxPE4Sfpclb1)u zy+!)HWF;Mno`*T5ufj~1PG z+xT4i*>>~DOYpf-U!$H0 z&L0wQ?VH;%9hScexAEzQ^OH2warOOie=kHaff>lJzP;Ys$GA z)mQMgu1OcqXE;Adj!o+6_zJEhMMY|cqrB{`d+zl1SKzgK?*eB}oG*NmdcJ(~3J%RX zJLjA;&ZpuoK8Z5OfaxAXB(=X`df~0MJAyMHbZf7Go_5F|&H10Z$TA@JOp>VYQaqQW zKEh~Jo!4Mg=<1ZcmB{ag$S~{IaKY%vw{@m%jG2V2=ca34L$vdm_o?fU9}cZ1Z+Y?> zOxi}e+`WPA$1E(l-#QcK>()P;BE|Vwn(6(qZkcfOcBAA+*~kxVBiHObnczPEL+FO%t;-p+j$j#P}0f|EpU&`RZoDttDk%TD7qJPnwU{_+-HjN$&^8r{lRorG8^= z4`;y_H*wiCS3Eb>Li^C%{4BU=`AofUEzGZa`z4Im2s|veKYqrel|i0WKrUuHi{*YZ z>^&mnH+>I?0so#51L2RkUB!c&jxwbX2iYHH`U_Fta4-ZnTFPnZ;I58~PWJ@=OX$M zNl~L2Ms)Ly$mZij;mxf|aD=^dh7It#DwpaL4@8Gd5W(v${uyeH^c~CfQ-ruqrkXy?{D%kcZ=}QSuevy$qLMhEBnm{N|y>#mb)$_&j0LGlptEYAZ5BKP8Rt_k5M`XEXPcDndo3%gh!D%e(Iq z-agpB&w(k5%(4l~0=hgrAS~ONWt4hAk@2X!Z{!A_-a@(8I^naT zDl@7Mni;R$Rtvj040&GXurkvt-o5La9_xie+8=EyeXGos>~tR&w>Zp8-ycl zUhJGZQ$8|J0 zspzQ2M2`uO%=exzlzk6%oV-JgS=95++Td2pg-4UdY~Goz#%ye(m*%0pTll5vh}hjd z>oUibH=NLKxJzg`;p>N~v+FWM;lE z81G-OCs4R(@7J^z&h?lhYcD;r9kWunphv3Wn}hY3wVQ@&2AmHUPS`rV=caGkb7piJ2ri*y-(83Er4-! zu+-~|1QBT1+uDNA=4fLz2(MAb`#g2=PD&}(k=#^E?mZS@9UTzg8S7+#|98PJx#v@g zcaxI4cs=kYQ6Kz8?oHOhyT>KSJMR8VcT7v43d-XP%JbJ^x?zso-QyKy{(c8$&9+Mg zr_OxB#(_hf7G`-UKl&z~60L zKpZ|XDTo5oqmU~usODee`dN|&J>`YjV64ImEGRQrluJ}}f|C4)OiR}4cN*^{= zP0Q!6Wl{Mttd_PvtbcHz6z{*4Ohpb5d9X%_Fk_|D1NrKYIg?v#1$@sUx6;zPX0;I8 z3BLkfBK|BDDT91n@RnTC>xgWU`=o!DA+BGzq0EM2Y;uEcP4hDl%MZXmT7o{-Rl>F% zN1-`B4Xlb8x+z+zIT4Us8{Na_-kOot4YrsMNu>AUA4z{U#Bd&f(;f z+^Ox)wv{KwfFk@far)nOT+1u>Pw!*>Kc-d4dzBlAA)7s;V*|O6#N-w zRr`n^$`%<>YnuNw--_k+Bpko}erscn*Y?P3 zE99^RwzGLTk2&lpp7R!LOB20cnWIEU29!&+3u*x|)|hbEA9+l4gNGc=K<-iJ#v(0j zWun7F5#xw$Y{}>`hN$BNi1S9d3}ksIz&r^*y;;7Jv=y=Y;%`o;1fVYQ!><&}C(^}P zMgv$>GC$;mamDy*z?Lnj#_9_|&WeyqG6E8sD0M_*cEdkXL)yoyOHXA) zeo7bQO6Ari8k@UQelp7wBHN^FPW$64zA$#hxaz41TYRMxj+6HvfLn=PKp%#JNlpI&h@(x ztcB4p<$0ssDMA`6EPv8YGg!^4Q;AEJ<8l3@GNyt{q=h-Vi_|Lw+lTwqtUX^*f)76j zvhwi}dy0GnuAzZqL4bHlkXYhI^s2eHzZea8|4@NM8YmF^`UQqEmO&D?KyRspMS1)B z`G|eRl0dOLq0~ky@R7Pr4iJc@ioAV9u0EKFrMHh*BJvdra3X+DC7u}1P8=W&@^=#p z+{Geyk^&P*z1`VN@L7;gAQxNY?@7pD%%#2}Z;3$c?=K~)h%xh*P8Lf9e%^j!#sRbS z6-hz`KHieaD4cefrJhvc6Dsh)k_7>w0fAy)Ok*XPD)RAm$Lfh$fk#>c{6t8DkJuwn zfc0?~PxZ!H6M8F`ZY2@<`T0xz{JllU9INgl1a4AEpuf~dAoUabiwG+!Xh4*NEjOS# zoB-?a8$DRZAi_aXFDvAnz@0Lk@H4Oqg3uh~=0!53{sk&XritjG(Q{3OWXSy#tAuV;+=TzOPEq3Z-)x!ljxX|G9Zm!8lz_QMMeIcCdB&v+1%cnG!L2BO3)4k-{lk)l)s_jrJU4&NC{VZKc*kAZ*@^4 zli7$T(oH4#SD!ZF{qM1CaMovyUtCN4C4I#clJ3{`kY5VnZdHnFTAqK+pX)dJZc<`4 zGWy~W)&|vg6|pv&zMC&=>yl@jC0(IobdaB9)<9a3>M#M$fQbxnPq>gD-~Jqq)Ud*% z7Ql=}Ng2I$C27rSKnVt z|Ihhqp~Mk6)Z%6Bzv_d07t}4M?oZ{0ZNeHJCo9Vh*N+H~DBmb$mF1p{Tx8S;!9Suw zi6GerNrp=$4ng8kVs{6xAWMI5dsfhfiX@g|*Wn`nfqwqOMWI83BtwII1_pWBi~UBp z1=>ibnv2Pj0csxV8+6A$7mA~dH)6a{qH4OI1Y*vFdNPUymg|1vIWhmGC zvU*eDo2hCcaqB^pX2RL3IdUF9DwY2^$I2c;POnH%cIkXa2WwqXi?KMSk7qR$v2Tbj zaA3x;uxBNG_D|^z{vdtj(uog`P({n(DCB%uIxLoW-@4+TVL3hj)z}|6EkMl9VI0N~ z86&mjk^72~g|pYn=RY+%U*LF7 z)?X{zguj+eB%H{;FVe>8k)CMpkXSX%x5{O3ocqHu#?v>@+qc5XBNYF?`4M1$srn^0 zEcdO7uRb({(fG&jeCGFW)f97>imw9mXWz;>%w;ORwU|Hq()5D=Hygc5t$%M(+JXemRjByZQuwnkP5*oC|69y|>H(yrCitZ#l2FBO4dxFk%t!k*{-4#h zB9;bI@tgalpg_bSY|h+y^A{{!w0OzVWy@ErT(x@5+I8zUY}^#SdCS&q+js1Y*tL7l z-hKNIL>@eJ_{h;?$D>Z1jE*^V`b_NEbLZnOT$IIMx_ss8wd*%--nxA!;qJZri4Pto zB|l1e{N(Ag)U@X>UZ%gwc%7N`=56-7_c^(F`2~eVABsy#%RYYk{N?Mn?>~Mr3bhoK zlxx>fQB|v}Uax)wjfRaHH)-0ed5e~L*?@5z=d?ix9DgFV0K~sZ6LZ^)#H-3W4METFB&zL!D z_WySM|F_Hk|IGh^W`oQvEC&y<8am8+xXlRLk#_bDj!vVTM~~t7FaP=fFZf@x{z786 zackh5{X?xf2CZpsCDt2#1t$304ecUoB@tg6nOFF;&xp^|12NRo#;u$ZPuL&YINlOm z#-LWtzf(W52gtX4e)xOc$gGTb&A50jShE_alO%|2BIG~+PQA#U8p$8ji;Gu7_41WU zxO!FPBiU;kigF{ydCfnS#4+UG;RmJRzp}B3{J8~&`mvP?!hqOaFCY+?R>?eo4^Q@% zc)AnsTcAHicgMxUPPiiLD)N+K99%gL^wOIw#RUicx5&fa+f77j%03fRdM2p!%tty^ zge$*(L1OGu3-S;03=)Nwhq?L)*!s{oxLO&6Nh;!q{6%b{ zDmYw~$Bbzj3G$GQRVj|I)Lrb4m7{Tdac$X6BqxXn8QVowMGgn2LC~ z@J!~tG@Yx|$9pP9p<%M0KoZC`IIdp@A}7TGVm()X(KK%#Z>$)8BvSuiu_p%fB%$~_ zfNU11z`05%W8JF6!vM+vNv=oR)7zi49+3!y_}mj$k1IWmw?$h}e;SFsPN6ep*IIwFTWh4{M? z9xF`o<^3Ut-(KJtz=aQeHi_-;qWQ^vC*>-5JOj+p281YaW zp{1P}(}abC7>0$`Okotk+cW71%`FBp2`n7M#In$uiDaQY6OPcr#)?^t(8|G{nSsz| zgdO9>LkAYxFhYd34rYuFLJNC)rUeVl7!?*;GbP6fegu=w!a>Yc7Me3LEVN`ISZKj4 zXW?KbgoQ&GFBT4GoLOkYn6uD|F=F9BM!>=`j0Owsm~Y2OIgU&&3r90gSm?yuL}>41 z$;2Zxx3Fj82;OEe6U#zJCWePc5IWiqWcDH)Y-Y!7LpW%N4YQuWF-RYepTWaGgf`aZ zj2FTo!v-_sNO+7jAge1Kw4~-Do+F3E(5ZXHpV>A(t8EDC9ARLB#W%zK(Q7)X0 z(7|B@OD~}(5yJ$=BP4VZ`UzB5Z>3&GqqmZ}ik_09lDdY5N*fiMay@EOu17gfVO9UF zAMO|>dspo7kF#+D(8p?xK1|~G4?;-RH;6B}Vt*jny{UtC@}H$89^mfyM_0H0yVPTG zwU(^L(G^=8v;zHc{gmuUP=~MWU=xk%eAY60CzdxKWNF_Mt69>3GyOECW{xj%F_H>f- zOJu(%@kbL+F5S6HctQ3+ko6l8)|hzd$vH5xB0_9vayq~Z@gxPYYl*+wgXNzuzZT7% z4lc4LulgUU4rdgv0kFf zW6ADicdP|>#)HT=nbVOyx)p1%ewdQmN5Gv1`aKr6p3LorCH{3PbCsow%sUBpYue9F z=d*O5_F7rW~hYQ+|CoS zmmz=+la+gJU4^Xoaq+mkNnSNu4?-DPizKvE?6vvBej5;oJ2|Iv?ETZej%mefDT|CZWG5Fbn=6@X)#}a< zliou2#d7D0$xejItxEJDIbTxUE=5vPq7&usE~v1==qU{yq?PPBA-+vAqI0|zFyy>O z)hFcS29YefBG2hk!bx)8fz!mK{F>FHO%NWMuDo>it!me zIV|O5bPd3fhU`F=upN^+hImG~PtDec*sC;ut`4LnNPi>NEBVxHd6jeL%5z~g6X`4Dj4^3h zZbwJWmP@oBErTmpo<681d>}U(NEeVdC^dEswEU-Q4 z*(ndi(HuCLAR4`D9yFB;?*F&Kc>$MR@DK4w8D==*3}#FEr=%}SIkr?56n>tJ*)75Xn1?1sNy6ekt8W%$&7@|=*c{XoZ$S`79l&x ziJeVOrE}*ux%niSEmqEv)ce=*nT$!(ct65$Y!Py5p7R@UJF1Ov)ai$N!HF&>Z&>)> zq@V74CG{ZlVRE8@dt(6M6jw9Sv&l|!GVajb#D7{pd7L11Cw39-%iQQqPUjLC=RVws zS9!MjPq|g*D(AByx=@Tgw{i=Ry#MF<)>K=Kz}i*>}X_n5@cYbP(}e`*zuuYW#%S0@crcB{$*(Yqa4IqQhph0G0z zW+50Vj}^Xz|3iJk5$A8!F zq!#~4XIIwne-givj>lH}r?I0wvh7bw=eS$Z{#EN=J=cD%KcW2J)9?(#>Ufk?+8P2R zT8JYhXKxEo^GR?`iL9uQGkd7`@k8bw+?=1h6NP&-hXgUio~({9mcNX<)S4Htxebv_%b#eNK;$}^$xttaJv-MW??JM|(g$LT6D>t&yz=5Z;Jcb^ z(RQt{(h2_pD~$?VDK1z8ZvNzp&&kXo2x$;wT|4o0bK%nvf8bcmf!jYu(vg~T=XS)H zmd?~F|Kd(_lDC}Ishj4?mFXg^vIp2xL zVFc<-2jnaH-v!}7>{Ir%8i@Hf`Agm$7R0ZI{z*;#?e-=f1oA$-|IE{WmoHrV{_oR4 z-o-?G6oG%l6WZT@?N8)g1)Q&&JHt*+%2n-gLd>7Mi)}Eg4V)|W@S5N==OuPywcPK0 z{Lgt!>sMWV75*lo$!I;f^;5DVfYU6r2NK_{J?qQ0!e2J{d=Q>|Cmg7p`d2QI_h1O* zaH=Nw6pS2l$F}h*r?)~A{wbG;Mg4m||J&T*#yKn0n#5Kh>yE@~AgeyVj%8#YF_9E* zcM7phiDu>20BQUG-Y(qRII7C{|IN0qyuLtWjq`;QdTWP;Eex9z#xNR+3&R%94QryT zY++%sV8H@hC^j$%vkhC!s4zO6``V5QW9l${3=C{-7h^JkPT1lwOvOy}n&{=_rJ_eeh)O1!onIcc_sSLU;77krR&GJ7+K{^0y}5 z)57&cvM+?L6B0QkZ%+=wGQ~Jssg91S?+mg=3hC~nUr$0KoqzUz9BBYw0N>b#`>5Hy3aoLeHeBi+LYjc=Rd>U!zgV6`Afj$bHR6^G@)vg_Z<=GN zsZ?iosT=#UxAIK6WC8j(=cC!OFUza&^N}{-`tz@Tc?LNV@r>N&Yj*i z1)iUId=$ZJx5z&OPU|KHE^f|GvYA+d4?p)v2H`h*NT!RFxyd&ZPw?6KDOX@&n|jNJ zCw$`bxk~Uurk}k5b*CFOeB$4hkAIWkZ6B|`16pNT8p`XM@Obv#wNF7~@58E8-IRJa zYw`F*5V`WmyVXHhrs7Fr3oE-k;ca_{k^m&MejGZ!pSUI34OP4bVdoBj&sjoZ4j^rH!nXYbE)Ub!Iy zmKx^Pd#%!e$FuizX%Z7kzha^2H9X9SUi9K&V;;}mD;BY8Qzk^V z)K>jy)Q`ur_mzc|_RWIHYi^br3=#5p_8zqPOCDu`f0MBICoz3_JbRy7Uk~3mpmq1tUn*0y@$@>Yx{T5v(x^DmUR_)JbT|mBopxtgmX`3JQ>UL zCyd1xc5L+?vPSpst~0PVAHRt3`P0(X@1fQF-8S!^^x*JeEWYmu#T?k=-#oK@XH`D_ z2NFMKjeic5Ezj#aeV8U6pT+0;y~u%n8_xOs=%>xcFDCKh)CT85nrM=0wl~k`Fc$AH z<47)a9@qNAj1H8~B_zIfM*TdvH}j8oHvWnD0uZ}<$U5EP`%xH+ z@7B0>A;=baoR8W+kdObF#P^cv7Q)n`I_DD0DF0c!wx>-Y*jbE=t)DoIkN<_lzuI_8 zA#|SbO67r-1y2u)H@~;G5Vr2@B-(L#2p|6|iEq6#x)7Fi8s^_-q$MAp#dlkkR0z*I zbl=-KoA%#tB>t0q9|~c-^If-EKl%O`#^RYAjUrGwv3?O0ShaQ^ zwSli!7>iGCGpq;>K73LC-fG&fe~|djEj^21{V5~ebqX{-i&t`)TLg2*-o3F9!&;Tp07FtpQ)JFc!c1)#)O*>*dzr!}!5`e8v>xchgNQf~O<1L)6~#^)+Sj ze#zNIAf7zO?wGy}&j%KNB#Zd~_mVEYi;|df_;?my=-T1~T&ut4WKqfpK7R$0zxK%P zAE5rAp$8XyW_)%{^z{?wYbBB5g z`21P?;`N_DK->>4(KB0K4&zyTh;Q9u=w`oU!Lz+opD2<1zh!C`!`$9?-+8(6`p}fc zGe7i-;ojtK%`Z3P>lMc0ozI&T!wG}Z!*f6KbjGvz*mfg|Ve8{CE9d)Eo|Q@d@p>-B z@Z?hSc1FzD zNu7AT!PF-CTld*k47)#u9RJvt?~kS|e*W1*#o%_QgVVYOW;}mbyg_knG4y#BssDa- z3l1O8;%(ntNBY~0%~A2|#NnAbB>yMtlZ(O2X!-E`PrN*sviKuRMlo2V_+9g1DE%yc zTi1eO*eh+iMZ18<$Fq2^=HH58+{K)qy2(^OsF3`xUQ;Onz0J_<{576VQxkeaYhMCEH?kg>zUKQ~Jc~E=(<^~94V@b0e&+Qgqe}AM+qYi{?8=&* zTolubtCuN@7v3>1fsdyfUk`gs<&(vGb+Ii06}yS%w@2{(BA&&EIgBlVejV>KE4EPj z)kyw{!#zr%W%TRy-P5W3v-tJRrj$V3m9=eNI938vFOBHt?MlZNb&~(heP>Fb^^Z>0ch?T$`NQI48(k`a%@1a_c>UFy z!-ui>c<0+CU@hIfc=;LLK8fe>AxYSd$*u24Y^L@}J(7P!Kw1g(wzM*D4LshI#rGYW zRRYU5AHL`rK=Wtu?%(rDpkdSSh)@mQK8$Da*(=IQz+`8q^9KERI+^+;|MlN~lz@`j z`me%kR6bd}&d@rgpkcMDnUg9XKa9ms3~o>g=jTsQGTle@4~zdazj-Mv>TvdM$3r|n znFb{PZ2|2{L6O-w{A*jj-A!3MW8S$G?j1WjGAmh+=MRg&TBctL{pz-liOi?+!{XO3 z?_CP5_4nSo|BlM12FYKgwn-`I8`fE4K7xD2;S-kar>r$w- zNRmBdHc$UV7C+>keJLCq)i^v?g|}zISbX2KF{Pk(bMApDhX-(UMzHuJiK0@lX;apH z!B}4Y<5|4U5wB8cw#0a^rZ3O8bQW(Qm6pPqAsT&pwHwam!!#uH4{0^E6l~92*Ke}B z1BVx|_{^g-N@2_3w+;5C^7)&x_%PjhrI6RbX#V*gR$TmvEPnm!C8Z#3z3Ne>l<(JJ z96o(jDHMK8)jK$!&p(32XSdo|3dvr3)^}9r$IW;auVlKd6x!My4w*39oXbC*#oOBM zE`^27t}_8kY5O)J^cz`6BK@me+N)ip^s{&kqhqCTBy(Q>4lDU~G-dJDDlw%n#dBr< zPAWW|6IuL_TW3q5m$kP3<9Ob_3S;r(f@P&pxBUe}rS&{NBUpTPqidz0e(6&7*|Ag) zuz2%rw@cyJIpL$(`@V4VwR9GLwQ(ZSpRYFS!ZRMvG$!=Ng+3~U!`~KV{y0eGhsDpo z_pB67&C-uAddbsc%Hq8mrI$j+m!K^(Uh?{3B8!i;$SQ?_ZTf~HpZ~+&w*WR(Wc?3NsHmu@sJO7K4^&Wmv&t4!)c^0yy*DokvW4&e z?e~2NoX)-TJagvEnKLtI=5qfH$MtKG9=YtPkrk>Q%X$(1(TmP(k^We*^^uGmmHymc z_0{SI1NCzu|3T zi}Xdk`={sDsQs7wZ_Kc@NIzefw`k#83ZJj%{yT>`Tcl0P&mH;4%mWJlaQ}iac`edg zzhs=gZ^>uMI>5<JW`lkAyBL+DYe%rzQ zs}{^{k*0Khedk*{Rr%SQ@L%xrycS9K@Sdxe+^qO(x&Mdm`7P48&o1tD-PWCQd*yQf z=!;jiNMlRNyFB=}A*7+al-F%>4`2 z+}I*rc-_XA&RM6>c|G?p{$_EDw6XW;%#m}p!vEag+J8xl^vV8(i!b|H;WyTY@Xsk- z+9JKF{nzsk{id|LmirepE^Co~xb;V0V44y?m;2v&^F1w6*5d1Le$k-FjVkV6_Tcgs z>HO>0jT|shp~F1xKku29Ez%Wpml}k*LuGuLxqt4954K2GT|BbOtVK${SkL{}zPP4E z@^?97T)+0&3jW-G_p@tTq+eE5Y_?Q&R`};s!k?}A3;L<2?(y6ADfwu*f6paPwn(2k z#?M%rtJEu(`-^j)0Y7}bvERP=3Z7NmfAM+iTBJF1Z#%*9f;une{yD-6Ez%a+;Ck*a z8n(Ac%?rlf^wc_qKX-8drdK{}ku1U0yM{lc(2eya{11%%q(xeJZq4>7tCad`x&M}J zpSDQ9&K`POUOQE9=l(sX>}-(+jOh5!f*MtSkmG;wMT@lcjE}2|?il7mzKqS|{@UWN zAeZ{>ZSVOiOZIQ({@wrj4f5IQA23t9SMC?Jnw#%;o;&_w8$u z?&^5{u{6P9sHbo z-kY~vqR@Fg_s@Ns?UTx{@;`R%B9(u*|NibB_DM6(Fb4W`P}hIXApF^=j{BrrgC%!3 z?^OAp``6Fuv`_lI_`Qqztx@E5F83GLblE43IO*EJ8I3CcaQ}sW@3v3+?XfLYSLO_n z={Ar1d)oEbC*AS*u5KM)RpK{u|1ASG`y_w6NB`DGtCi!g=l(nMPuwS6e94gWZ(E@7 z$qw$n`uv{zq<72CT|c&f$?;h~!at|+9?F)b5jvhM|$;L#qBG`>z%1UY3j}^__$FyVSia+U1f%)jOUOPY{wF3ElN9 z+bbuBP`+gMC!ZlBdrR6Iqk3wwo28!0rW31l+aB%Hl2ssC(5dJA$tps&i8#Xjaoij& zAC4zG6*^H&U&f>J{PZ;d^-d7=EG*Sb{pKy5%PN75J=I6Hev!Y0tNK%25_6z03?$a} zP^sekdx`Zr_U#&{nYv$>j9v1r=B>3i-SF?lZlikpfqD`*@qIj6iN5=tulcxTK!pU0hm?{ur*H$0Nq^qt3ee4@Te-^A~h5{>_R>n-y|++HRA z6;&Cx5bW{Pv!}_NN-5(f=o0O_sIFugOBTe$lT`C#%}0s(%Uncb7;zVUQ;2@YmT%*E zt32D$ikku zPv$N<`=@>%iez14ENXN59ozn#T!z{Yj!^fif#!wFjAYwH`R=lD!C+J#lc*=idS~)X z;Mn3Xb8);JxdzEPHkHp2CoyBkR>7AL#N)RA%J@X z{?Dqos%39{e3nlWQ6B2uHpi|#54KmOa^!n$e7N_=3caBMV+M_5Uc6aiww$^%$Cl)k zv?fo;;xoeSD&;#@q;5>CP-F8HtJ=EMJZ+KbkR(RC1%d z3V5p>U48|z{KoUTQkHGSZ+uF92V#y#{Wkf$%dzUG2e+%tMRX5M6EByjns^rtE9$JN zc6+qmrW$XE-z3FY`Rq)M*4bIJ=$_6RlF#uoVX0prKUR3F^YrA>RQaOnDXPVZOT1heBRav9s=3JbLZ~CSI-ghHiy+(L_gxX5iFXK&S2R>LRq850YxP%c z-03@V@>x=KR*`!1R_i#Zu}SX4XDIQ!lIy3QBso-G^8HdYj?4GqP;ZfM#5q)39ou%2 zDG)DHZl^=lhUlMIlhm(uCAVekb&Au!^{QeN4*C}ru_`m zEBO>>YA-pqbWbf$?Xjx7Y+cW9jW+S#L321d=|Xf*&A*n<;?#9QIXrMsjn%f1zZNf_ zprtmCe=k1nNBo+6dc;pr4^ehvSj|D@s<&>^+7UyF21uwP6|26V0RUeYaI_gbLSQC?f$Chv6C0wXz7B!KE9qp8|$msykcD%Wd>a_)9 z(%NO6HGMD2)Z{kHV^i$==?P<(fbiyf-Zaf;JNvf&8V~cNM338u3Hu)c{e|WLBK({8+IZJiLm`aS_ z|4O$v6XodqO(nD?I$LnKdk{%erS!A7EYNa2oK_fU_D8FZgSk)qY#hlT(t6}Dy2nnw z=U6@iMC-dWH>7#Fo9pWG9CnB@v!ic+C*~bOdYU0jK(6@mJkrof50btxkcgcoRm-&F zbQ#r2jz!yB!(nYMgZ$eWT$l^^Bd+Zc4MY7Nc`(MQy^_<%;( zGl*5;ixIdZ)uWw(J63TTsU{ejg+KT*M7WWA;L|B#1=%Vk&Wvip>2NU_Q{x^kR+VQK zP9Xs$e98m2f8%6TtW+F{(h_-CEDn9q#fOg{%=U)0N@$h0UPb#)QA&t=IFt^eM*>a> zlmK1_(XCaIv8s^YUE^)Q_drlR;(@8tv^D-<1V^{z`lW^^gjNL`0zRDm^7|vcI%;<{ z2jUxiIA-cJ;32o|jM}KX+E0xVMcV}T1zh@pOkAA*dI~l{FC~u(pd>_ zeALJrtd--;Al^Wm&8X(BPo7l@C(!fxPR8Z{&H{*{let_@mWAixfT4gPfU^N}<{-{l za48MoVn8;;e-2zE;T!fyjy*!U`v~a)LtSU%BtG(>X9D~gf|s*-7-KH=Pz>aWC6Nsyz4*3v3N(XiPF*XyqK(R+Ky4=4v10X+eGdm$gdj8HH76TRlngA65 z5zrIx?Mc7`umms%Fd1M3bOwBQBGLnheyiaw0?Ywa0CEBO6YW}R3%i{$&c}GhSCr^u zRzr#9iO2B4Cy0WmcW4MVsWm{BhKI62h+O3k1Ou}gf{hU^oodA{N(~pTRq&P*ADoiP zs%pi1M$qS>u#ySI#pHUMP)tBOAb)_CO^w#$!0a?&h=W@E+fg#kI8qzM=;`&~H5B&E z0E#?76=4)#8GPeqs>89z9d&CP@PQ_bYjJ6yrH>YPp&`U@Y%PAG^i&g|tmN^&l}ss) z+cO>CdkTgx!t)HcBxe(!y-j#S9|g<>jpq*1vcY&E`j8)bm)#y{^!w2@E73=11kp0` zXR%n0==J-3p@=WyVmWl+8@~iEMk+{fCUljAM7%1LFYo?1I~-7-7@JUXZb|7R+~iR? z(`?k@o-Mjc4~2>e$CoQBeSxS>Z>6&MGnW_eGvICn9%hzZ#HbDixh(Jm(TGr7{d7|! z($)HdZd8T8!(m9xBK5e}5zka4`FZgLKrOuo5$X-sg4O61?=@VJItj$ne=xjIW ztl+El)%zIb1{UZc+$EHh3yy7uACP zbCDM!Uqt#5;;g;9i$(=@YZr|ckPBe9;Tcc`5SDb&gaFL|;r1??GC&BRl_bi7HQ&)i zvj>p7w2Njmd9?M{rH5wT+)Pckwx&;~OH&|Cfiwlu6i8DbO@TB8(iBKjAWeZZ1=18q zQy@)&GzHQWNK+t9fiwlu6i8DbO@TB8(iBKjAWeZZ1=18qQy@)&GzHQWNK+t9fiwlu z6i8DbO@TB8(iBKjAWeZZ1=18qQy@)&GzHQWNK+t9fiwlu6i8DbO@TB8(iBKjAWeZZ z1=18qQy@)&GzHQWNK+t9fiwlu6i8DbO@TB8(iBKjAWeZZ1=18qQy@)&GzHQWNK+t9 zfiwlu6i8DbO@TB8(iBKj;6IE4A>s{3 zDry6b-kJ-%6$ReN1<_!rqBMVK32t?t;f+*OH~RdZifUiL9rochAbXF;jpbfXv9B5* zubEZRWHlFfs~hXo&|x7@^?w)%{v*-J?%P9Ck8gpg$hF5q6W-?oniYSFLv2OBKky?r z7eMJoBaGhZml(EANlX5ec2h!r zq%BbLp?9iVIo_)PYTZU7PHS~bOh;)wh_@&uuV%cjPRNVmw3b(5I?8K4;(eHq7k!)U zTf84g$&ca@&h$&HdtzESH2wF#iUPSmWU$=3ILJ2B1ZPOM4WjjaM~1F-(x zST^8%K>C-aK$-$+3ZyA;Fa>^B^oAvhtG;jVlEKG!^?gf9c(iMB_@Q_}xy%HhKc zrzMA{=Ks<8hY#;xn;f3neqT*b4&Ru9-wziiho{o7$Bg9gZ7Jz@{^juD_dXuy-OMNA z&#Cpi?CE&eGYQYB{4nS_xjf>boM+?DdzELEuTtyr_Pfb=rsn(it;yl3e16sTJwVjH-Ob$;?|I1g&;fd)V-;<$v3y@fTDt#~P*)b_R5r0be z{mC6QXxcwQ+HRTw_?|N9`-`uN&z~rc`lB$F&Z*^O4@xd4m5w8Zbd1Te+Y_D>>qF)A z9u|*xMZ$AxyS5Z1mzRjopv4_ElK_;Tif?MYC){)R@JsGX4o}RV^7TBL9KJfGf6RO` zIXso`Rvm&nJhcmcQoJW@KidR-%busEq~-Y$>FK;x8s)N@YMWk zKS&Nw#sB)h9X`BjTXJ}6{{PsX9G;s0**gv&zW=-A@KpZjd(v@9{F7S#oo6J6r|JQ- z4awolQ~2X$^Kr5MaFr$goJ!{rj^un&`RH2baT@Bs%?bUt{MvuN|BgCeE{AygG(4;Q zCbhhW?>sy{TOLUcPvuMRhQo({krMuR3f;mRlhdd2Eqn9u;TN3HDXCqbPRQTatCQw( z0MS6Le`5Z&lRIf7z@fswL)cF#;i-J}=DBkEol3b29!b`VN-N}WDr47_P8v1doJ*49 zUFD6(`=z>5ti4jp`^WZpyvzR{kC)o6pMN69Q|q1DpN{`DIXsargzJwf;i>Za{@uyx zQ~7*)ht5fKO$}ewB{@8`o_F`?%=^LSgnm$Q{h|8-;mn+Jo=Xsq)}@xiU9Y5}HA0Gl zX5xBlcRZIV@zpiL#IPOB$@rzxw;@jscfvgfpwhWn$zOe+Tp-6gFTb;<7NGhkw#TcZ zI%_ro2*1|CKN#Iv^AA8`cw#@iyr{G0dcdK=pDXRG*$6mPxK!3zQw%s%_&S8Wewgsl zV>@dm0CFh<<#+u!x&7!>rK=i7dIv%GMVVYjp|iU`oJ&A;i>%Q zJ&+upnttGK$>B{Ye0X9}|EngVGG zq$v=mK*^xe3G}x3_Dn7-CgPdEdTSfx3;VF#mKsp!Ve26rDydQ^j|3KJM#O;r`=i~WO zz-{mk0uCVVc}Q~=!Z!lS0DBR>1^#a#kGm0;19%zF6A-o^d0dJ22%ZNZ-i`1-9&iSr z5ou0D_yC0e1osid(ZX*U@aO;#@je9pkHZZhz8hgT;JqiFJ0pAr+$jia2F)J^JP+t{ zM=&>4j0~P|_0CZlH$qE4%1D*!ZsMnr#U>U3< zJC1c?omm&wm33p?8Pc-jnTDOfPGl#so~#!;nVrIVvp(!pb{gx;PG@JZeoV{yvjOZ( zHjrhpv)CY(%?7ix*$|e)hO%L7I1`wT>6w8UnTeU1g;|-6*_nfh%*k9#V!14jjbQn# zfEBWlY!n;Kir6{qTvp7+uo70v%Gg*oj*VyKYyu0&1_;AEem|=`r=+N|G*BFLdvdMn z+k^nV<}SaxXSgf4an4%s@4QUvvPIjHk29mf}^7DYCr1RhV-7s zhESU@uB&NNd3D}stkhPuUcN|8ZDXJ&>I=3t{ZS^L z4pDgwvItgB_tvzPfB|3I`k5~h2nO0FedVOV@KF@q*ih{ax3SS9(XcO2*T%$c?;yTN zv>sv{GO-PE!S8Jwi*mK4Eu&n(I}?H^+O{DQiDf#G0`+gI1C&;#Q=1YuwXGinL*9Vj zSKX$z3Dvm$=tFH7mO~JjZQ+JcW2F9Q1Rqa7E9!NJ!)_=fZQw`B!54^n>)O~0Dg9AP zKQ)u1kbjf~iJ>;|8c(Xop|-R=N844 z^|iIwwn{-18)-YnNZLk>y|rzllD7_nMR=Axr)mRhhP@5J8ExbYZ^-YiX=~a@Bj%%R z;VIC#?OHbyAH{?LsgF))M+R-z3Mg?KM*$k28zXJvFpe+h5xw!>AEJ5ch;Me=xFi}p znxPA+k80qG29IXgiluJbQb#m+)MK8U#ZioS(O{tM%CnldtbFgCj|PuoxQxyU zwT)XF17VWlZ5SqJ;1CqeEUj1yP5;sqNK+t9fiwmF&r_gs6l1h)#nKsMj~1)^9}Vj zVM5{!AgV8lQr%(9{%fPG+8yvvUe(B-yTLH4Zo(h(i+UsE>1~RJ-8E6>4+m*IiTUNE z44Y>((O_Z4U&)e^{^^gZ;|&BG>reqqb;7fh6kcQ&*7^ck`Jo=@`B53r5(L*`N))d3 z2WK)y3pSJ_Uz-}zx}QS9u$SgHoFI|L2v&?dUXM1h4U^+}u-KOr%-qqaw;>cI;?AP& z5s)|>1c}|+{Go)CwmR4t@Zj__a62e&Z8+GV1-nFi9xqZPmYWcl7W@#J717qzHwG@y zW;X;8-51dg%8CpcjC@bSjwIEcpT0&xQ8W;}AI&cs3~C$PfmvF2xDFHwL?cv1nOv0b zpV1|jo|5>JQ^)G5^)`jD%FHtY^i9*qIojih#_%|&e;ghxQDZe}1aUjV6m=m73ev@r}8qzz(iR#p~TLJbR1 zg~9VvXscv*_rq)w(ZYyzzl1LZc$CQ(4$UTO2;Y6}hL<4r=J zszAe3WC;f9C_{&6&lD2!rABc_wb{s@_Mtu6TEDvv*{bcRhRb6LL~tnVt?|+9C&pbI zhd@xv(IR|EhP$=$T$B?rj5YYM%3ZK0ipRkKq>fyNPC)The1aj`)1w9=s)0oZ$ruho z99lkr!)kEb1i8e$Sm!bhi)6P z0wHl$D~lyG(y(E}*i=PG4M19YA26+TiohHr3QZ;7I z1&d-dfryOpsM^gjwgjLQUFC)jqHjL8zs6O;!t1Y9R>19+(!y`fQH24t>b)tE-R30>Q`r!1!n*?DDcA zNGI=ZQ>K3KsXvxBwLKsof{kIQV=Pi13`Z*?;TqtGfyL|bqGw>x^}8d{#CNh4qH5GM zhQkm>6vP6JA+D1KU|i#Fh%jGd1|GsRw7|xSMpxNY(?Y3q`~0#t1|1PY2xTMVO>t$M zlgFLb{9LH7%3qBkS@Ov}kTIw`v0*8p`iJyab;Gb1%oLDmK)BREHKPg>#*pG%fvg;T zZsB?GQ{4;bxso(4uJ>YSbNjXR!65fn;u5!W9p0s7Xd>dVXji0jW^-RH7!j(Jhs(>X zNY|LYk1ZGCfSCNmJI@&|*}E{f{vhx|R`eWJ_7Lbfw!E!`DI~(UB-3g-R^`-Z9|=J$ zN+*?tcU~6USQ&Uu%9ql}*?~sT_`TK8xv9Yaz#mOps-gE0ejd!XynYC9{K)my!bNzk zTlU0w5bH-22Yn7Hz;B2LvSCb+iH^QYV;ZB`k33G2EAvpa9)kyd5K+7wa1vrex2*U1 z$phkz%Q&YK{T3hsE(X}`c=iTsU5F#fLnS@p8J!7FqAVl1A9NHSFwuJbUalM@$}3VH zC>@P?*;z7g(R_gXR5vSP&+^!zEx-@6hX6K*`HMV_sCSb}uW0#lCq=xhFy} zol-PdS+h+lN2U&NNhl-cr^*W@j|lcdy#WY7)jt7Atp{?c!3UiWd7-H|!1@3FgDe|T zh}nnMG)zy0$KLTwLk;=Cd{v(Q+O;L#nUlceqqs5MIB#wCU~Mg=7xXuDR!EIHZDUAF z@)2T~D@ELZbr@M}jORY`dnq?BqP!v7RO_}_d9A03=W(ppJA8gWRx$h-_xL?wOnq0C zdk9c2s8xTt530WCvr2kZo*(L++eRqK)Hu*;gJGJ}%R@mdFWOxo`l#^^g$p0M65^>D z`F+D-PdprA12S_sdcbh9Cmzm8I~)xpw+7l4Y5nn6^}U?i%OI>s4j;qUM9Q(=0wI&+ zHz7jPXEp_(&S#IP?14a{fNYnc*PSzEFm-wnGnyzQkAjmDO5^2W>OyQWQc+5fM|SaH zW2-fH3`Ql;FM}6=%72z(90}J3Z(&o7m(K!F49cN>x8Z)wT!)Y6>uQdqI{Y~vg{0B( zMCdxei@*OVF5_nfqVA?RDu0H@pVdun*gQXtIW)tG=lth&{Ik4A)r^VhsmC(kF|txT zn%3gH;dstROAGXgnhVNF(I}Khyw=(yh`rA#ZfS;7nQ&;XI=D zjqIsM!r;l(pbTUli`S5NIL^T(m<^Q48>w+}CWIHs5-v|!i#uE#E?D9b;$_ISj=vYl zwq8WzqQMFER{R}|amBQ>HUW=T<k*R|k_h>Rp(%CFG~g#*!yL9EE^{JO&em`Fs`2uwfq!SCKz` zS0?p*T_^E5UtUq-{vu6^QI;#AFzs|KtVVzwLNNc;MsWCvuVl*z@M)*tU5zRCa7xYd zQRn1hN>;KAQ8vE(!5}Tz(poZ>m}w7!XM@$=$RI6d>spRYA}X=@I~9UaqJhwYL`qoR zJG8$%*QOP;L0EH%m8UMjd~Gn%F>=| zYko7sv|6F;Q(z+|*2IX9bC1kjBlE5sTToO-tk4E%a!xM2*Le9-4?{G*e^1dp-Z}=Z zz*=pPZpdhVuj)pdWEJ;-!fl zP~c?{Dv=Are0>&Za6dvPSdZ*1Oi`cx`;V5y4c*o0;! zMM`|8_%%v`#Al9kVsRwS<5ftq(NO9~yZ>s9un|ExPbCR94Mjk%xWhH|*jB=Gtllxt z#OljdBZ))iv~w^A2u}BsP{~+JQOcAn7sg_k zFYJxCowBwRLjmdfSAa4V(lGpWD9;ttrizCeAjLh0$+3}^a4E|xAC1S@bET=0uW706 z(I2sK9-Hz8hFEXsZLTuIgz$hv`C|Bw; ziS{!}Ltt%-@9`_7sZ^_7`Dfw9Fs(RLTGDq985R!81~f#F{~=kw_?)o2Od^BrfS!uj*%2!nF_EFY=3Amo$D2F)OAgDU!}5=c49d#FNA|M zZVw61%1U{o7V#R1KPoFdz8Ty_@8B*;PV#RIKy1SGbNNI0;T7-T5G<=G&+-Njy&U0h zWKnV9*@(;aF6@w_`!oIrergz(3E6TC8b9UtEE;xWaT07iNI!7Piky;RLOeCk%A(Rr zs1KME$MaCb6g`7vSzI0|n~)@T!!zBG!)Qj>X@E+c4r*Eor@Y~yEiuTrvGNIZlH-gq zQ?7+9zbT%N=I)5gX`FylOmCGpOReAW0=P8QUtSiFP*R>e(DN@IqMtGY*@Sgwkf zssO3@krd1trZdtQkw`17vC!Tn-|Hcl5)T+8%?cQdhN3@Iy6{>kmH+zwnb~r;l4l z`GUA4gT@zyUWW-o5&!YOKY-ySfIm9X9c{7>5~^SnKifqaL7@YG*@ITeilK{V>jx?HBv->r72^u)Ss{T%n=8 zW)Iv&f%cmHaQ94auQ>p>?85e%jBk?fR-4L(Uh>k=z0;=78~VaD2wo~j&GWy4kyII? zuJh3}l%xc-VReWD?Vz}$a5W6Xh=nO_2{P2dm@RILO4_vQNeVuhDv_m^+zN8*#noMP zbXDWY3jv1?r%7Fcwj57>#3ZV$gb<9tQUfUx$9ZmdLRJD~lR{xfFzzQrKco&oc9|D= zXEBrtI|$kd#`qH=A98(s?_Jdqxy1zhF-OAI9PWS0X1FB?HeT|j5V5(sY$+q_!`O3! z>P^y~OIWRfKGq^5FyDgh3${lw2xwI~oaj$y-YS)#M1SbOl_k9Xam(fC{MXH zXL1av-C++vDhH{d;GLrjPsAq?e+B2m6^(f#uh&t>NyOpbr&V%$v_`|+`1UhPe69wO zV?jB*aT86c80L&@YF32Lt#V~nO7f%hyrar3B(sZKOL=gbpm`v>TIZX%f4r@sCV8Mz z5tAv);qHVL9q1!+81m17$v3~iB!?DK5_RMRJy7X@w76C6^LU`lDN-dNj-SjEptR1? zs<9I~n<}ee@P;)VZ&Eo__r`#lNX3AZ#Dwl6*8)95wqJwwbIeF?YLlQ#r3_IA6N9rcqJj;G0ZE09%_Z$SpFgdx%^nAQ8iMyhb1g##_&@nB#B|fJFVoa zZe7F(rpgPVXFk`C$?7>fpuS9e{Z6HcYO*&=s~Eeh zdo#oM<`j#!VGTHnTbITCQu5+v1YiuZ594h;jPzP%LZ_C`vxQV83vfl};U^s^L28+Y zi>vZ66iuQ#8`cVbhfCkqFtyx-;%H8UGbOFpDdL_1l7N}&U14`UKrej?4S_@|H}E_LcUU)o>T>Zm)S7Zf>s*8X70**VeQ5@JFj{~jc0Vx z^oB8^@e^iFYMMOjyxCdtbtI@B$|-$WR3J}&tcOfvvoN1H8?8d_mfpdjiOAT9N1*Z#~U%f!lHBy zBjYwIi^qqK#ch}=5q=(DZLJ#aZSvMM!W(A6G=(BNaBlz$qVnA^CiKRArqC>9@^_^>GFw&`?^LDrXI zCj(&FPJ4ZHvWKzpem>2|JJR4ChUg5cl@^ci%SNQwsf!3+IIobdp)M7Z3ZN`&Goq>5 zjq@5~epL6^ z%JNEz^6fOe#6}N4yQ}OCqiv~eP&;n>MW zlugXzet0g=8!@4b!sX{;p3nFMe>}(H%l>kllEO(ytHv)LNqOWI6tM9VMzApxidjL? zL^h_hfR&X_ViQVm_jYMHD=sWy`K2ZKdF8O%8&k###{x$@Q!eF&}WR z^NNbq=W)PgTnY9wO2-wKmX?u=TM)`vIUdwF^vna17xF1CLbmzErQ;`zD`XS%#^sG0 zg)=IX%g5#MCn~Y+ztpvKb#i-1+Yw^?LGrz~gHY_D(yOvWUYMgEqJ>46Jm-YOLobqN zDm0c2k_Q{gj?2JUzmyMx^Aq{#h?x1r^&cE&0DH$IHf0mR3MeSw#Qf-({6RlinnGx( zA^@U-Zcm_yiEFeFE#bzHyk5)2CBjo>5?TfHh|k|BkHmDUOqTCo(5hQSv3nUzh_46q zxjD1zsU#5pWgA> z5?jnvHZzEeL$JT0#^{1<2;FKo9avA^*wS|In%Sir*5?&B@f;B?vV!mAW{MAg7=K73 zgErL;tDa;|g)tDLE?ANDY>X*z<%2x5OTy!M zqaioeO_gWz=EeljksI98gQ#f@&a|pGM37F+XDB#{A)mWMI9Q(M-LfuEWnllr?ZYxC z_FUx$WFBiX9Cqc$=@t7y8ewRBPVAI?ZzqNDgkLp!lrx)5R0cD5g!&&Hd6&<3ku4Tf z0q|kMikq^*_h)*CoEPydr%|0dT+PokdxG+QFt@Qn{+Lh1iH~=+c7%}}lk|02*P#B2 zD3A|C3Ai;hdx1;516RF{6p!>wTZQDJ2=0RXG!NrV1~<7}^-4NQtFkM9R?cKKDTUEF zGz{!X=jSRX7LFTVRLUiSvTsf7ONaG&D#$mE_rDQ_t)LN=YJF4E9WG2JU-I1mDlSQ3 zRJZ@hzmhLp*7M0O5S8;$8-q!Y>C< z_|ExLWPcBP)$ltNY<-a3GcXbU;Sk%?~reEN12n0hHh9l9&88e$^ z&6csr8{XY|x~#v_IQM@dKisoIb}zkq#{qiEy?7pXS0Vn3_ievVD+eF+ z_tM{GuUy4_;t1pCDe<2~{CDo%dGD!g*S)*%oqFi}FTRJT7>j{u?Dq4wYgy&?>Dy@r zo&3A>{cRub_?a=?#WH5}UDtd>5gzTP$pK^o1_HEzzJT5U4WKiC0rsx#rr8bH0oVfA z4A=zN2-pBv4_F6y8t^z^EnqcZ6<|5wPQW6-0>C@~#cu+H0QG8 zC!jN+3kKbGYPImb|B-H*J%HVS9e{0s&4BfQwSZ>8LO>Is9#9S_1{459fDw=b7zpSK z=n3c!$N(I87(Yn$zLVfqN?4g>X-UJ0EU8xNLtn%}M0o zy+7PNaC^Xw!X;h950~_*3b>T>Zn&hU?SOj*++4V%TZwSBaIJ7j|0st`I!Q6yUU0X; zCEch1E-4|K;gSxs5iaR|>*10v^fX+m=32M|;I4*yCfrqU2f|$rHw*5aaLec^|17?m?oh6p{=d^D+=#}i z``dRPy+!y)U^Iy{nT#EU6GU(7uBiaz0t7%$00Zp2vAbq7U>#r;U@;&Br~r%xh=3eG zUw{U{0DBgp4*@m<)&cGWEC6@_6t^6%NY8+tfDFL?8@g+D1GWGt&eMv!9PT1O6i@{i z4WRHLGW!4H|F-*+U;C6_2j!<$JbFiWjUV6vQ~>6M?y3IVemm(s`QJO1j0ha)ZKkBZb{3?3L=N*Pgc@wdBS?N_TtjNV?Z*Fc< zcxo7uAN*@>pBwYs*fIY6OXt{g?w#FZ&t)$kAA26S_eAx1-mObcR-d)&p6??+XT0$G zY1bR+`JV4Ky?xqC@0a9i_bhuNG-TD9SMKuVZl3mA)~-{pFYWWq<+&H$y7iX)9v!b6 z{K&H0pI>M$`0mmZZ@BcbXL4TdaP z^3%R=K0RmlE$&sUxw7B2>?b<2VZp)f>_0RvPd+?V1KMi`}-i^~f zninX3ed%r5Yub;i+K|75dGONP4PHBDZTEtz=@atOM3bJ z9B-e`CZF=uyZ@9nAAjcjQKyYs{mRPkrQG|U^!Xp@_*cu7-$(}Mx)HN?eX#h2zkMma z^xJQ5yXT)UbJIgRrQyX>KOM8-`Ng+<@w#-=hVhQZg134;f5#)z$92yxwiT{9CGqvPkC_*|v5_50!19ryfASM>U|KpH5m_{z3o(ASr& zI$N5%`ktNZJaa~F{%C@$=Az>^omoEf@;`38)Oq7C_CB*~vhsE7SBw3xe%E^0sN0@q zzioAVxL|VU(;nUZ`IYx~aJ)Lda73pkZ*R2xW0>7=Vc+-8{D=PgqD50|_h0q=`bpbY zTz3DxPv}3Zf9!;nxT0(maqT#rMbWEe)WuHUoMz3 z=|=H|vdahbE;-j*{oCa?JaXLPxhL!#`tjXwzqc{tqx-jYB^^DnACOrT4jSU?uF3pQ z{i6o=lPX;!sc%pirSo~z(B!^VS&0koE5k5^tdvb6;?byi)4wzY(iBKjAWeZZ1&#>? zvdd_uF8{76@4*I+i@&S>A0JH#(o798#1)K%Y z16+VIKm}krpb2mp;3mNRfTsa(0Dc6p$vxPKfU^NEKnb7<5CY5tECSpQcpUHsUbFLKgNtE2Oh*`7mOq+s4Pk!U<+ajM+LAdvWOP zApdbJ-ye*4<=bDeCz=oQrl^-t1)9KNH!Eli;WISwRN|~}2R!oSj|wR9*!m9RU=>9O z(fqvtmOxQ{_f#G`zrz@q|4~RDyM<~%7|Y~B?ypi_1;{(*^G*9A!k^6Fyq*HDVy*=% z>O}t{)r^a=H&7bgTAodx6$_|%D#7KpDXG{pd{tcJtdSVxwqTZ+sV=FSo zd!vUAMoIj7X54F{mhd&&F|l4lDTjOsH%C)p`3_XWA?aGSiD+gUsmx**=O+GaW8K%9GQSOiAk%c2;uhBCSA_gud+DLDx zmi+cAy-}xN5vprK@7Zuri!t_ShjDIS#2ceD?BywyT*-XdZ18jN48NrTr{_}`KQG`J zAMyp{h(A&OM5}T#Nvo0R1vA9P5LIasdspG;lswp^b|dL78A>^V9|l0bzJuecv=eBv zI^i819Q{MvZX~QG{du7fab+Ic+J3ax9pW?tJ2AGeeNkkD+e00yEDGM)rDB&CMdMWC z$Po3*d(@ulqnph?p*WNsr@GvE8T&b7lG_(W*BTFlVZXPudOF!VE>Kbu+Ij3nj$cx5 zMt7(sT2chHTQLvIrxTB1Z;V5h5S^$uPr)H5A{2dsh|3zEOhLN$jVKCk${?R&`Va$; z3H-aOd?Nd@%yxya_?N|->_xR)O6exRcCx@*>jtkD=NA>g0wg&%Qc<@9YMXbsKksI2BITSwj)}^hqj_w+9O5ahB__Zb z0IH8742fGAyCH##fMoiYrojIe3TR{-w@%WzQjLU4X&LJYXO;0~;}^y!P0OUM677T1 zzCfN(C!D1lW&XtawQV8A%|)++-*NnT39ZhzV*L02PZqwtYv&_@YbImuI?=$s0&SxpFd2-#(5`J`^(8NxzciM|B%_|@Py4ltc# zdc)jkxxilLI8|Kc?C;8U-R*kT^^R+yv{G6p?U&eE#*9d}OAvHMomD65a&-l|(Yo7o zAL?85n+!i0mKv`#y=po>MomOsFWVu0sXJLePd{J3K)+DGNWU2E@PPgU{T_Xx;U2>Z z<1@x))5E5Hrc2CMnpc=TmPM9_ECtrl)?#a!wcI+{T4DXscCLM^{US}V$am{nhcP(%&bS-i%b}ez;>3WT5y#xD6aOMjOgoVN)VX^Q& zXkBI~H%vBcH4U;1vwUmWW7%ujZ#iK3!uFl5%I>k(+x_;C{XzSq_IPV97JqWGJu)3X zw0>@#W1nYFZkd(Nhn;u2nqA9Xt6ZyHYh91Ko_4Kstta}kcAQ_H*J};K4L+mMY&DDK zTyueWw0VTJ$XafjY^$(U**vy-8@~b+{Mwjd>Sfws>S6xU ze74148EKhrjap|}pSONuHQPqoH@JF9XG*!~iA~b?63cDJ`;Jz)Qusu8Qoq6QreUbL zw_~Wo?PzwKC1yA~JG(nI&YsTR&c05qbD%TZnd20kMyJ*Jn(Jzbm9>K=1A9+lpx_iH z2%Ypjji(v2jXGntImaxRjpmV-F_uY|prxI)v$dn`9@|5<$&Ly~mBZuM=-A}g?AYSi z=J?RD!?Dw`+wrYqk7KW6zoQdq`?T{E*SW4Qh@Mq4jb>UdwrsRyTko(|+CsLK=qumZ zy4#1_bL}74J2{3pa>T2|?#}bjAI<|$-zTw9J8t_rSP%q@kT0AgOcbUGJ|QH`66Oh4 zgF9{)nuQ01wZb#Pdf|0pv+#FehtOT8(e>2z*7enCbpv(Tx*Q$#hVOM5hS`SAhQY?~ zjr&a(TAsJ&*_PSPb}SahIk!52+uV-t*S;xSsei~& zZ+y-8w((NaC+4nJm-TD=ul9D1Ovj0iQyjw_Mn|rr&~dJ#+EMR_I%YX8aop;-)3My~ zfa77uD~>lE?}NS{JO1VP)sZQlAr2ICM4f06OT==qLUfCD;>D0)w~8ypHR2QEv*LPj ztN4-lgUFo6IeR!ya`r)6o1HFazH_v*(&=^1bS`jS@4VHy)YFPz^y ze}W`B-qp)B$Ypi8TqDtPWv)6`gXOdMc5#`A-snk@uBdO@UzejZ8$)8wr)7u?_AwjU5zfPo2|P{ zca?6T?mpcEp#ST-cXXfVzR{hfAFeOZkI`4_r|U0)47*Ezzy3M>%ld8lPa*mK(DyX- zHRud>Ln)+4gQ3xIjp1g)D#N3OR}F6)KIc-rtFfPPkWn*WdA@nI`EM9melRb#+-})mu~>c9$E`c9Us?OuY__$wFKyH8LC5Wm@6q>z;zOd& zX>nfQ+~Vx&DtBGu>Iw>7ENz$A`gW`wq_`OM?V&5xP0(Gb%g{fiKhuz7xWmv4jQ(O+ z2b|t9Y&U#n_|DK`$S|H~yvTT`ah367NQKU(?k0_?r%5!mGj}r2F<)xlV`e+r(a_5# z30b-&x;J&l8BQ=*4BtAQ5=BVaxvnfJM=Fp;OU2S!vZX~*r(N9&4pWx8_RWL<@> zO6Sql>-@S9=(}4VG7K_)rQE~2*<*hkwfj!CY| zT|1FudtRqmf*);S&_7_XnO0(?_}#SGyv4lD{GoY=`Af?r>#4RE?O)pObw1$i>(aUg zV(iLs2`(eJO?2hD3S6UI#o)Md*JP3xuqL27J%PU5Pp8*)(m!SR$gs!I!DunBv<$L- zX?@Lhy1mYRx&3|nRuK_}_G~ho6}s{I%k|e9zcubRYE2iIt}s1jI@z2J* zlII3+W+^E4ny$BgqW)I>vxcn(W}IalXeu^MGc7W$F|9YfVtT{$j%kN!m+5QM52p6! zlgwwK^is3eywq&7+-SMg@|dNob-1 ztH=7JwWlr1cD>C9-QzxchQsd&L8>%4<~W{noFh(go(*n%lK7xXZnu+#M}#MJ&wi=B@+cRPP~ zj&Ow(wM?;uEvqaaS~S*?)^*l19G5%p1-JAO&vL%+>JNQuhqP1LEqyEPk@iCO zIzaW`+@6ocOvn&k5XyA}Ad|k(pJ*@}dqIZWV}2YxVywk)z1Oe0p)tgSYTA*v4X}i<5!nVft5qM@4G>#`6 zIp?<|0PFLste&zS72a8@}z&U&Zc8FEIQP0qE@HSMl3u8*m2?U8X5(BGfay{&WW zM?>ovW!!8$U@SG=YGUSP=3bWJ7Rj>M(%IU{o^M}*5&k__59uN4bHb5z;QG!c;C4WF zx&ANuZwyi6d&aMf2dt;q{I)OcLmi_XS37=jOc8$=11x-eRNEip|8&f7Z3`L6}D zZieAL!(R<=83f}f<7DGf;|AkSO^-r9{neCdKGQtO>@!~in!RfNhdI;I53N#U zxzn;9x}VgP? zKlP^?&Vpn;!8q8M2gw>W-UNBS(YV9-tNC=xILiXdQ>gP9R-^SCYZWxEo2<{<7TRyL zueHy>9Hj$WjO;SeDoc!a8@Gc}8RpLB?q-d-r@6QJc}TjB7BlGfvgHlS&z9dU_gYt5 z*J6}-8r-wqy1}~9y2+Zz)w`|VS{=5VZFkrv+8a=VW_zYX1nr6)WsY(O=~&k|`iiT> zN5sX>4bF|uP0r02QMNgKu0J57UZ=WMb>OmBBa9Uu6n0_6JV9qculvQ~vW~UhW_{NB zj`eHnZ`N$vD%;z(Q|xy8RLo6YahxJPC4M6QEOvG3oim)*K@a!{vyc;9pSUd0xjHcF z4x@C(>8I&K`YwjQn9i|X2t1eD-n5;HF<`3wZF`R64Dod_=sZyxh4KC|={ac&;WJO> zjguiSa|O9~pC>d3+jUd)gA7i?3d}%;7;}x687GLVMYHn_)Mm7_3OZE>u5Z66jMO!P zC+;^rXE!<=4w{uM7MF;3ip}D3@nf;0^qIt(JMj5rXXpr5>pJMI`a1n~0|Q6Ahne9U z&?i5zv$Y+#eAz7gLwHC3gMO&tm6$ebH9uwR2d=KO&$K^me*-;es$;TiuL}$Id<-!Q zj|tlZt!}@r8>C?;<2a+&ILD|r4T3hDXI^Puj+u|gdV%$KThP7+^SUX}eMdtRJIOiC z`JU77ddPK-R4Ij}%Q1p{BtaA0+<|QbX*vkmf&;S74L$vK;bF)Jjp<}lU(*0nwrQAY zy6H}68_$_uHf=O*GHo{fZ0cur^7%$F#;$VnWOIeN%KV^tE&9XL=5^))mZ6r>mQqW( zWwNEhGS{-w@^{M)%TCMggxS%^*zD{v>)X~6^plmg^|qe&PGUFl1hJQRn%Gaw5(kUJ zMT2M)onpQ?N*p7O6(@;Pp;vmvnd0T*>#pstA6*@&jdpb4WAbj@Yx=7U7UP%3m8LrL zDVD94mDZUyi~U)L5i<(WnF~pjL%gu31Gjt3Hs)ZwG8(PWt#XY87)^?eWyW&!`3hr| z(POMP`i&v<#3oRAo^if$fpMX6k#Vtc3ACPO<8tE*m@)il*@xNQnbsWZMC(-a%oCwC zza+MIKI?o9y8BM&JFcFRUK%IeA^im4tZ|P~VTk0I&bz2u(n=uZ3WbI_@ zZacx&*`8_dXW#Ak&hZD@e4^+Bh3*qyz|7z`F~_;Yc^5{+51qT9NB4B~fmT!GieR2Q z7Zm*6rIVb}h0<(kx%7baFwsYoK~rUR7Ut=Bx-q(m(7_sX7wIn5T>}YouWqgGN!=H^ zA25cv^i%YH{dM|f`lt0T>fhA=sviX{)@_(;SZesv(8Ji*IK(KJ?=oL#dCAfqdg)B- z)xi5jNSlwX|K$2x7u!j;Q*8rm)wb6$=laUF*VZ5UoXbAWev|!Ydl$!Pju%TGqs!Hg)87QG{1N?2`u5PkuQDt)Y%uJGJYdEX z(Z~L3+zR>M(_}T}o61bpre&sWrq4{>AX#1Je9XGnn;H1vEz2jC?$%yb$y$I}_>ZVt zmhEiYaN9PFhCkTOM4cu=I#t=PwBKNV#{RthWvsgV(|(#m>&U{ast>q*9JspzGIb4F zZI}4H*xPx!bAWS#^L)&Tu5;dmTDEufcIjNNp_aXQ9iNk4!}$KQ^gGp2$l!f>oNzvB zw@A1}=%nkeJ3+Tfw?_AZZYm_vV*TxCr%&|X=zAMZHw-X5V0akXRJL)baRg?7?;ATn zhtD+GOogUK(`?fv&=Om)wlxIv>?bkb{L#G6e8AktI?WoewxD***2C7zmTy~NdlK^d zYxLbw_G0^3tYlnazXo-D*8YNhgMA1_i%pJq9e=|N{2NCH%=a^$CpyoCE-=A4#rYt3 z@^RB5_;hnIuUe*Cu3L{e)w`VYeukcVx_*e> zpdXEyRi*xI{m1$WLyaM5SZ(;ku+#9Bp~zTb9B=%_xDPt;U`Vkgrn^j!m_CQ*+t=J5 znr{Ss>?`we7!yZZuEm!@`E9JgVF-{Sb%@dMTvZW5P@uZVAm?}&TR`vzf6LkIn5 ztaGCCOUz}jcHQE73adFUx@=ORR44h-_nwe;QQgZju!D;oP+mW^Xv|~<8`YsbKa%< z8q%emArrG=$xvV@F-|dF1=)L}aWraCiMG7Q^d?544@?)EuQERh{(R2-srhSjPfH)m z8J1C&GRthsC6@V?F6fWh_MvtO^Qr(=JnypKYu7q-4!h$Stmia|YsCR*o8O&=9J@FtjS`DN*YlmvIG=aE?Cgh`>0r!HGT0{GKW-9k6YdmRgkJ@wn*-T= zrS4Pojc+iwDb<(j*XSR^>|JB%WjNIkF*HFU|IP3Z!wy4FlNK}_XPRWX&U8EUS)yZi z=!k{pb1)a&ZvMi2lBKuhbjx?p6E{F7{0(#LPPRF=%Yo-hkYsypKVvnT>znhWWixH4RYS!uo1 zj~0nqo2+xJ^Q`l&3#<#Ri!e$pvHsm|#`@PDai4fHdT*Ai5R&&3*Zt5bfpl{Q_GTG- zOn6>+UiXG>vc5uJrT6ITF&>8WQGJts4%V!<=|9x((C^gm#{77Xey=`^Ir8gRGe3Z} z`QspW&owq0XB#gu-fF%ZvULEoqOs5;Zn7@5zG8g?8ghwkJl1n(KtGztCDAI|)y^lK zFQISk#dxrT=1gnlzVsFN^(TQScZO~x)&Q32-%X&`WJ86a%HZLAaD!nvR&&-HUI8Ax zji(!j8-vEVkd(_z|F69>kJG9C`~RiJEY`tbtc`uia`x>y=ej8Sl944lg-mvmt&z2i zY-7n5LTE$`5y~f7D@yC3)C@%u}Eq(6F?>pJK2dB0!V z`#t;BB3eH!vzLV#A8|e(PzhU`UCpWHOt@V-%zZdj{jk;2Qv_c}5quklTif0X&d5(i z?+CZUSxL!!uPu0=$51^NN?~#W73mI$;DXXfZKV!_tB-|y-&gZ%^|g+mohw>dkj@I- z&pWCO$C;Q_dn2qoRsn0aH6O*GvcEct(!E(X2!S3jQ?*wNC=LbJQbNUJ-ygzs>_(SmGpFBCl zR~#+yDItOmR8BJ8;r-F68cM!I?eWD%v6 z(CTW9w5HlnZM61@wnkeI7QC2sFI$=I%|7M^bF;b2EN@k#w$-EScA(>Kwa!=<@o{pX zHhkjWO2-XDJ(vxfIc49nhXmfj#aM(=x)L|xbGlC|?#z+EiNM*w&w(3grcVd+23=fH zCRjD72cHi%2{sSD8tg%L8WfxyoZ+7NQ<=}Eh)_XLgn7bZVUw_0+$8Q4^GL;{ige=_ zq_)y->40=h`cb+gJ&^LsqvQ$lRN1|+$5AscdP-Til2<7N&#k6tiUZS^_gT2y;RE6elvgKG>%T}(VSN_Jc$I!l|5bmW z7xk2gy~a@^)XZr{g29r^FU)PG>%t76d%IrCc20088h01}djFUB0<%F~rO~15qe;Bs z40VP(<4~XX!kNAUGdqFj0(Ao~1jYp>1*YKzEkbMFK|T8p?0h>=B{(HG&wVx-As(kF zC{(AK#c^ha3pwa#QDOv)qLbHyXQGUyDz}v1l!wYy^*4{ViCSl^k2V(tW+@u(O|6t( zUVl~(GjbXEj6z0EGrv{RYG}n;Ev&KDG%LmW3SJnBIv@1c^4E8VxO~~Y-|4pzwKh_6 zr01l%RG`7q66s?Q?KJrVd5^rGS{bfHp)1s(;@nm1!P-XRmW_wAZQ^8IMbnyY;(40W ztb%^WKLw?@HXfvpDc1@7gl6clpYoo&(MgizPvouKkT$$EFsi&CwQV%d}59!717SREQ8bVK;r6{-OSfegOR{8=PP(+DsSos9DTX zt#~lhG5;g|E0tSa+)-T*BqqBzleSCvUbs(ZR#1q0ito`OuAvW>=X-aMhN66KmCi{; z(9=80>y?uDv&+?CS*PWU_B1GGtp2{f2$Zu+|5iT%E6sqvx&EjLa&K*PrW^F4JI5O% zQ9%=p_lysW#m2|Rr|2YK!Bh{>w@;$_T>>H9G=4K4;ySrFxVR~rhUtKZ8-SWx;K03X zzH0U|`T^LADf@@bkCX#z)u<0E$cVyq1DRYo*wlYh~&1vkS*A@9Sge}go`)L zUTJRuo&QS5D(O^nELi0G&Ti*hI!0(9Cr)=kbbq%NKF8B+1a6HFE=6HX#*fVI@#1}S zH5ZTP7e{~!Q^YeKGJJ*F-xFtg4L$!f+Uh;Iv9d^6sT@PYJBLGFO>L`oA~i4x)b%YI z%CjgZ?X_351t>T5^%mgovHAqQ<5j$k7^9rg4IOd0QOq1?PBG7zSGgZCRypextGktx z?>686A$5MEe~bSH?pqaGwQJA^r{J|E!;QbRPty~h#$7Fl-ns*fn;#U`6~6yA)&EeS zChqJ*aCmkP!&Jw?oFUA|;kqDP=XM3TS>K>B<(DRLqc)(k4dLFOmxn9U&?i1s4k?Az z#&oAqpxgQSK0P0pswq;H+>?qPI2-^bK65 z*Eqq(s3NU`L)_ZgH@j~xf7F8YG=uRi1cUDq4#UrTi5tc2Qd2%h66i?;#qQ=zsme%Y zuTn%U3sW5dYB{b}h0TluX`jP&s;N(;BV5!~?(p}907IQ%CYsYZzjL{Vi|Gd|aK+Zr zLpSkRcbJd*!e2f3m7MmAe&2}fo?bUW*e1+WpGNgPp$(_k6tGv@3C;x2#&qXM;54d3 z{-EkP-@VZcv%9ejbhRj{j3mi*L0_}vRMf*CI2@63-$^Q$4xov>5|!fJNP`ggnWXle>TUd9!r8@3XbM^-gsqE2FFaq&pAyO z$?f%!MpA$2U1^$>D($78=Op9M9?Te`?phm2KfU0EU)++p7(b2fn(gp z2jVlJkr`m%&C)JzVkLRBJedxd8y~e1Xk$29-Z&+hi_KkV;Y6KbAg2fpf9f3BK`F zl(-N%hg?98mP_I*RFRv)TT{RSkxEfI_j6R=?#e(I^-Pq^)jWxBl|Ph$d|A3yW(6Ls zkI={AD=*Q%pl7C$jmlwYu$1=3EAW*0Mh0q0ezPo3W++c(0~%g6boicDe@^Ur-g9w( zMSo3y1Ah~G+Hn7P|56awUjI?5RtQOf!S*nFGTDG_=v_DLJ9ZxOC^4i0Z0ALEuWsmG zvz?31EvFl5rK=-+KwT~d9vuxHy%4>-uZuBK9ci+(1<$=IPh%0N=nwt3p3)c;^E$O{eb$M-$C-Xst*O>kn`bFUhryzm zXQLuq`4(E3%aW_3F|4N-9WgE#S3m>hzyTdNDRH=*-D1zmJoU&8~*JZNQ!4xVgif<#=I79Wl@yr*{ecyWU>%nE81RaE<+{3N0MtWmqtXT5mZQv$|Wfy&*F_xPcaWW<|i=$tViz(F{QGyR|8q*V4)$(MOI5A3|K;EqmjXAJEA zbLT7Pd#4DTN)6Twb_jN%lF!G5^EAAS>~7r4*PSFxqOcFYSQU4O*`zn59H*3`FrhZR7@V z8KBkA%v&f^r7Y2Mz*8H+#(S**X_c4!)3|jmf(paQ2{?6}GtMOsH@(amdXE&$!N8c{ zTsnG6a1V-BS{uCt zO!ug<%v^15grV$4VZ3HGwc1<#tYPE;mVsr8`b&fKn^5<=`%Swhm7^&cjzJ{1&f$?| zr|QR{49~$O+XZ?#slt+-kI zMZAq$T3WKC=iv>*q*0_(u1mj3o#a7a{S^6}TwE!qEK~L6{h{le22TH*Jn{F^%uKor;+z-IemmjpftwC0I-mrKIvQ>Ydxoj*?{gQRxaFpQUai zEpZdC>vuK4Y22di(yo)Fx~DA%Ia~yd-7t%T#@c}3yYRLqSf%`x(YjmvJNOrpj`|7g zUIbON4C$wNcB*~d{>?7yR03@-=AIvM9y;LxDWHQh zm)wr4^{kMSvy`58IEHg@h!-oT$?`qJ`K+Y2#?zRI$F~q|{H_|*+Gqo{MELbn)He(K z-49)SvAztatAJ6JcibOd^f8Wch^dfTS;fgch$dJR9ilN$V-7maUh4qfWhehzbiTd* zAN;wl26#whN9!ADkB45&Xt|pEW`~ zT=>Y-7$YWTy~PZ1KYhKC+#5}61xcxN`8Vo$1GsiC&~~U=K^;l`IIYh!;^<8gRzYhz zz4(GZ(hlMvCW4R;po$cxqCK8Q_@~rsLv)b0=r{=|MxT+aI2R-_fL0&s)wnXkvp7nF z=sHV<<)jC02(D&afqwHqtW5Gtk@|sJKbBX?Yte+x$@!JSa3ELlSfH%PI&qJkn0T`q4S2g01&(`){HV4-CA6 zFZv^TSAH^ZEvd|#c$$3PPNAMokWI(|dNPFocjPmnExENx;(9oYtGM436}rg;w8}eD zMY*ah!J>QM>XgN{EI>~={Gg*(UL z=qx2Gov6-H58`GW*1}P^-{gjVryE9HH0%xFkzb5R-oXIWhhZp3Q>=aDBAWYO#~W@6 zPaI*t&G|n@2g%{&b!70?t7HQEqMEv?%hG)M7qXn2&jR0~bma=>3l^d>Ez8utlS6&S zc^{@w8$|lHumPQS9IA6~6qp251wmN@M|_tw)@F6Px(f~OS2c#LVr4YVHslq@q0YOC z&?Pd9H?`tu^ln<9wdbN9@Kl*x8YOUtE5IoSSPQMCoSOaC2{g{U{wPuy zb-h&TTmFyypOW%*T@%-fY6#bF!S{Pe!Z?DYsmxn)(1P;^3I~d3y_YEgKiR|Ho-6eM z8G-}ed*Mr^g!*jo_7GtddHU7dk-cPBo+7C$iW*+{5VVvP;%dB!yD%ENdtgdYazK6_g!8gIQ~r#^Y%T$rg;-wocA z`cb{Wvx?M;k}#?W4tkMfMGtKNOnV$|#sZYIjoO!ZNk`GqB1r`_@v!*{)U4g4E3WFH z;G{^SvN0L=VzzMt)!@9DL=t2xiAWn1-p4 z)|VFQ>4oK}FfG92Z=#wn#c5v;?l{Hyz5=p(AcTr}#G0Nr(YR|1YhhtDUOSeoynL6g>7fW%Hnv+`VpuMbj*WWaD(vR<(66a$jX#2Ev z$z4=+D%6|#_}wu%$zKK2xOL})H-f&5P%rCM4&SjQy>yZ=R$Kx){ZV|19OG~plz=9h zjvsMXD@Pu@uJNkzHdXgavyGgsZBd0T!=PUW!P`3^Q&ZDE3=AH-W zVRPM@>kIR0ZXTgHu6$3DHJgMVnKhZreI6yp!oV{9fP>^`E2wc=Z+>zfIA=DqCMP@I z|E*tj{3PYJI74xaf8cMwA1F&FI^h1@kzpPmj>e;`PL5##nd4-!3|`qBJmF<_w0iCo!Ml)jy$dG>oJ?x8x1jK(U`Y0=&C@b%F!(&ql?h^f z865Y9J(Q%>68i}G@7rYWEawGhuJeVn4UN4}z#@&(A<#JxheNSEumv=C4gC5Ps%Am_ zh|1^zZ-dfLWb)ydFyCdqem@ef=fU5V;ckQRuSQc7-F);el<0IkglM%4*xP`?x$J&` zItJf6U0tnxrG2A`xPUW_V`O!&8qsDcw3b+u#35)c8_n78*jw~${b4EF0J zY-f@noK%OO>6zNppsivrSk)quH;1I(rKja^>P{@)Vh&PGCe>y%uG81(x(}46)G%tz zba3q@kb4QOv{nJE(TDV6A?A>3>kZ)eyN$c1+v|qmv5h6qunz2Y77eVdT{VjZzKeRg z+1^gqKFZy9oe^J+Fb{L*@N}iNLT9{>o=ic^#zQ|LoP}>(7BWyQ?w~9B=x%@Hy0=q5 zFL;>a4(y`@uEmhQ&G^hk$H;U|??|n29^zqL%R#bVQBmvQ7gbdIYlAcyx4(t{to4HR z5)9)VYdrTcmEQdW-gF+n0hfKwf0sT`h&h)!puH8&I_C#+Sr?trO!s^i_=2gL=kdAR zoS=*BJpLLR=Bq*vC?!>vT9Gt-#nV^PQC7~7w8$wJqqg^w=aUjml6T1mQ6#U*zjGD~ zE4uQ$(vl9{3s1U<+CqIz?XA9!wzN>)1Y`PEJ%ozz6PjO~)(d{?x>&BecL`?UrUP;@ z6IPR%nxTItQohXUiPf{5374gRVyq#dafZ{|!fb=0HWqzzia7%gxgB@D2MoR}3C(V# zkKgw%!QtAL^%h&Af6s!Ad}fcv-BqT; zQNbAWIFTFZI)Qyjh^-5L9^49|yh48Pwwo;O6y}bE!JQ|9&SugrGiM|+(?<Z;gbOpv&fBaLCc=bQ>)uHk|4-uwKD9C;xUCk=zK3iR<>yJLU%g2O4c1=M-$3>H7<^a=1oEQ!5;d-| z)tWm$i7NLJOm7!Sm!4#plf1M;JPOqYl-KQnZD^Jc+!{S1%*(kvC4`aTIg!QL?t$%e zaCS{jQem{=MyOFc<$Fq)s-qRu#5pUR<^S%|0=f?;&^Bt4^Nys-HZq%nxUZWJ%&OER z-BXr2;$yzXG?j#^*p2DIe7x0TB;s5jqanV`Df&{eVCi5RG)6vCVwi6Pe;h?0D@Y%H zM!YEA63cPYt4q1zjrC!U&Eyiev#rSvbyi}jpA*n7W~iOCo>@NS5dCd^o&LGLRUe14 zo0(KdMoquWC;Qb*vG!Q^nIQ^A^E?9{D~t>M6aIW@$4x@{7KC}Yx4SfuH1KjM88>PU zwfl(Ylhjaasr8syh(&j6#ZT_UtV^8QQ|(Iyp8(>W4l|!iV&xFE{5V+PEdA@UngLV4 zLzckjrJmhMxJPbrC#{jOj>O@0PnCEJ_4WWxQDyXqSFIj+!fVLlT}4~JW6cE%xq4R{ zyCcc>Rdx)Yww?1b$T$+*=+3J6lEXY*zY}w$0ZwusCK5`c3cZBBuPdJ`+fc(ZVAbW- zb=Ef0eZP|Cd6bxTr_$mcn|T=V*mS`0zs;!qbqe4aGBizsiNWcp!*hcRxP?i~mi|P} zBFvjt!rm=i!VV-vQpOa_CpL3XvxT%R~3A4m> z@wj-3dm@8}BIJvDE41D|=v0?T0<|#bp}L(lbK}+enYC#RA}>gy%0b&4K-ZWB2G2&8 zQM6m}4Ex&e(Cv#l<NLF)a@E%#-FfXfF zB+ElzjMgEcRt*AoyD6+yVnsSI}UJVqkC!c@r;-o)PBdfZdFV%fel)`VzVBN@6o+p4yA;a0w@K)@L)Xlg^}0 z7)gG`aBu{Bq5*tFa{qhgEzdHyRK{%VRi?U3n0^W`I>V=~?|+duG2XwL+P@Y3!Y>gEy4k(m9M zS)8?5Da3g3Z4a?;MCFJghgsKCpa(t1g0h1-YodDW<^6O4g?vat;Cr;gqN+mntT*3o z8U3&Z_jHK%7Acqa$v^Cd$!2!JGE7H1u$u((>}zrIuIe}NL}lt=KNR3MJulw%-a|m+ zt|!xvT*r9W`43c^%=~ONW=X3$uizl`L>+Ol)&;)dFV3?8ckb*#Ae2mYO%l(uV11{U z{|NUobvcB*LXk0uKN3(&nDT+ed;27@C=HPa+z!`2o zjtTd&@Rd*tWl&c?xWOjz4&9|Bd?|`gbB+1r##tE$Uak=C`I|#X@?_@iBBg>-QPNDV zo86Bpy+T@zxAcXSMw0mw4o!E*S0nRhWO{I!Dbxhe83p=ld7T< z#)f;mFt0F}>8r_J;ytfe4{Y^`bVF)|7S;zfY?+roIH8PE7oi%QqMFBWI!4j!-Gt{* zQX5l^YG^d8OochWUN}sHtPLb$G!R}NPY1X{_BH`+Z!h|IsFRb);$_T_UUP~BN(RaV z8fDHgbqe?HUT!qIXQVFZ`Sb9CK7ylPm0Ob1noPcbHYYV3n*F=Xu%)4KyEkwvyuo$E zo+2^aP#=tLG)li`G&UERE6ro(Df66JnlzT1@a_seNJVcc%_KlulIvYPC$K(>#3=6F z1hnj{b}?M8a-7bO@IFg1VgA0S*jMndR=+GyJlwc~cB|9)Za@xH2SLG+hIZ8M4P zT_k_=^KEJ|yHpQeHxgzy-l$@}#~k&3H2Y)BF;=3&cd$B>!w>j-!RLH@`FG9Gon7zA6!rjf zFdleEe-HY~JXDL_r2d|=!4)>6+Q?59YvkJg8~tT0#Se|?9x=+YN!y7=Ml7^@^&NZ`#KuW7*x^s$eb@>E+dDO zkEFRqwyFgg&j(VdEO;r@zN8{t2Jne|o^xUF6en`yQ}nNxv8swk>Lz`=8)HG-k*H~t z0w>Y@!aXcnQg{oG@~ZF_O5eNEB`FV2YL2`GeXoJi+skWzpyXoCKAu_4>#Cbpf0o)Z zgdV?=ELCe}HII>mDQpxsO3@MSPzm#(Z`Wrwt17zs5OzNP;$jQ}ReXy-cp+Cg=Y?vkoD8I85bn zHP!YP#_4EA&u@=fw32D4AMFda7RX07*vRl>b|Tpv`Sms0O_4N1i5A^SZ zS3l*1k#PF$fM1^3OD0O?fcT}Hq}T(vGQdj;Ga zR15IieZ}=mHMNXZ9SqThNxzD^f*$gk-Wvxbjk!;E-lh;(ycG)DMLtnEl%USmDUyW6 z?NWAmu}82nVYH@n*oj57=McahMW+3`#jY%H0J zFTK>oY*hEd+~yzT6uh_lRHRV#fO;~^g?6)oW%WwzocImhPcz)y>@av;W>&l(+Y{dN z(yja4pO+Et<(33=IpeYUx5uYj2XM&bq% z7W<@Iyu(btV597&3&xPfZi%auik|d?mLJW%E6*++W%rt1&?w4uc3C-cm#eO_XTwwX;P)MIqVf9N$^RDI^axJ_nhYY$$1KZSZlTM$ zhkM>cI{xZ4)z67eV=n%<_A}|md(1x;WHz8Yj=jn>m74>cfv>fi6yPyEw~-%&8;@qX zf~4O@ROds?_MV3^^n+W^hF9;Q!e2IXTE(rhoIl-5+;;Pl&>^S{)!^Hs(NTZ*oURyB zSUc@QIOf?%l)9ef>*zfTnKs?W`TUW2*Xt)lVbcJFls1+qq zs?Q8Sob)=FY!2$sUD6=MaNg%|rq-gIg)rGv3hk_;GJ~$Y361`krwc{Xb4@VJ1noVh z7WR|NK7$S#q8DIBqz^d_BqFmM##<$NF~`k_LKFE;oln(Rz8 zV78Lr6GwRdMP77SzvuZ6=JY2pX*3EBG!K1$9a_b4)T#SoUa1_pZk6g^8$2{rdS6;h z)@BWO=$v$gjvGn7rUF|VzV{HsZ8DDGaOdY?&)t>xytLdlT)s=_84po;3*)%Tu>5vR zk!{9dzCsGvX7Zvv)qMphXB$ubD(RBE+!^#Fo%5^^Z$=zXwS5}Ds^Xsw5p zGfWQLR7$Cp)y`}+7z%0{ho7++bngdQyr6eta&Z`X!CL(R_a_Q9wH|X~u~u`d6;<=m zhMYgL*=`2!)So+v9-lbQlOH*b^8TJ*?o=m0s`&eGi(;*4@qK+2h0 zdme4jIYQ3;N4=I0_ z?|jIq*dre1R1}pONX_tkM&ZsaL}_0s`Q#9k*IaTWvyYi`7IAV5J?{$Vqo` zy&pF>Hw@=zJx?x8xq*)w#}iwku0x4GOcwC2S{RqiU=C+CY4Ek6v@4{H>XV~$>*o@0 zKI=>7KhHCplao7WF)z}B$~Dj!OYK@t_F^A(^#++_SJm!~r#S_Dx|*a~DsNwcvA4yq zm<_)TuzA6q-1ukRp(1-+1Y5xkvSE$2?0U>0#j?|-6)EIRaNQkr_seMbwb96CZXJls@5sc1#P=0iO+1f(XNVE8^+R=%I43*D1ocf!7cs zgbTTZNcdMYw=#x}KNU%XzRtaz?=`x$>Ivn3T#F-|eDAAts1jfseQBoc=O-wjS>`vy8i-w3aVH$suWAzTu_pvG1L zdB=Ej4e#JVTqK21m}I1TFNT4{leiP#!D|}w*|yW0j?$IhVWM{@Q-`4>(Cgqdy<;pz zyZMRPh4Q$!uTU}Pn^9JCZwF9>=Vz6tvX7HHn9bR^v)KN|@g+uhdmUDZGvr_7Bgz)^ zVV}#Zq>KAgY2E^5ze~NHfj+#5In|YTfS=RJc5=@SphBHw zs^$`J`d6y(Q&Kqdj#2Q^7(CZ1uycdi<67LrSZ1Btq0swca(d6_U6B4IA-`y`lczO& z#8)Gyx9iBgol&GAi_0;xz~(4JtD*O$y9~x>8{z+-H+E!ryQBW9%SEGX#xR5QPxqzg zKWuTEj{e@)+j}TP z5~yE)*8-Et3vD3jk%BUxN~SFhohhAecj`a)fW+o>SAn?m!f>Sj9uH=^I0InpkC0&^ zCtwx{%LSxvlQ;#*S*}iq7|uJ7{D;j}f7KV8|Hnq3!PM3foPz|mCS|IRb8#{ka~f8_ zbJsEpx``99LrmpN#N!x@VUBD%uE8IvrVZ>QN@0>=58JccnS&6zZ!RU0R9^IBlLo^7 z!-W50s>li@1xGfO+I)!ndz{?g`TA^e?)#dn#l9+^5jxANbgGCK~6G1NdvMB}LnzJ)UVXyv>5OP@x*(jWzeS zFm!^y#OXctzPQVS^>}gvV^G&7Pz|T!@y^v3knl>Pza-PAH|U%6sK*dq6+=K#HoWP{ zN3)TS<2`pBDvtP`>l`)AEG(m&}qQK#tM z7nmE!VA|ylF1Rlk5)4OIFwu80uQdWH)QpvRC|w|1_n>Jk5j|Xdi{$3H!-KT zd&Siod<$}Vy4Mae8;6+KIE&YKlZnX?`nbE1t1QmBBHK(KHzKRr4hOxb>`SJW@Ylq7 zdm;FbjGW%@I=1kPp3~}R;aPhUqg^e6-vAiHAK&AVpP{1vX8ewR9?GtRlDJ9bO<$*6 z9!`q{6Gg)-Vqg{(|6`L?Bld;l^7><{l*TmaDJDrX$OHMn80Uc(;G?Tbrx!?_^Z?RDF;B2p^32+or_@0Uy70@h1Q8slROv@fjiP`O8Jd z<-a?h|C{OF$4~IT=~Vxhv;A-9F@xNj&k6CiBk^VX=Jv88oye08@a7c9cpEx5pe63{ z5b;esDxW_ZoL>dyqej*yo=$YI1ajqby**Pa$n+c{O?x40+u?nGl+E8gBDXiwdWv%s z!?~`9F-($)&-okNUb>CvKFEz zp+&OA)U7XlsUfqd9_gscA#lPfrNWls7(N$~Mok*$8WxI}xINe+9Lo=;{-`kv=TD&uhE}70= zz-WA=Rv^uuxcSrZFBUN2x(NoDPMvV8L`<+Es%kCJTbE!QT6u!!p1ED}tedW-Cgk?? zauujnGP?yfZtIOm)f&P7`a(nkWlF$8!c~#*%D5MSe`!aHS z`)7A(sos-1M3%_s%j4~N65zobNTH_uZ3EaJZ|e@p!p!YoF@M`T*UrPzK7K1{0$e3g zO7yzm+{g00Yq{l_y)gC9Zg~73C-DBcLn>5(djHG)H*UvFR@PDC(Lwj%d)*lVG~^~+@R*@kzG)32B1TZ0mVdbZ%5qA~8NdGgPbC-CG6Jb407p1_kQ c@Z+(Kz>_ENe((2n&bc3EXUV?b-|P4K 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

    #MX_|H&!qtF&1EuT$1nX|ev(Q`T2$ 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

    Ef-ks3J5`p&< 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

    g_46g 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
    Ln}>K z=xro+H@e|(D|N5V3uG(9qYtjSFUS!|+e}4iN83nmm3H9t!nmZ?=1(`8zev<~+r#|b zA0e#7vP*i2pbcWP3DG@Ee34U^U=K*BZiqIRL&WOWe|L2M<%k|<`(JXr_aVda)q7fY jVam}hg{b21B{dzp>{M5&$f3WU)Za~;*AEwI(bE1OmOzon diff --git a/src/main/resources/assets/opencomputers/lib/lua52/native.64.bsd.so b/src/main/resources/assets/opencomputers/lib/lua52/native.64.bsd.so deleted file mode 100644 index 715dccfaba7bc0a7bd512ca35532d4c9a00fcfac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 819437 zcmdqK33wdE)h^s#BNijEi+$J}nZ*c$L11vk%fJ8z2C$3>izLgEC9tKz8W|ao5y%D& zgNPtt1Oy@oi`a+Fh|LV(U=g+nF+^d@5SFx%Lk8jy0mk~@bLv!2Ob|2)9 z5!rGaW8h|E{{_zH;J7c2#d3X591oRqTyj>^KiEzc?(Yg_{kt{`InG3pje{@1`MJjR zU4dT!F2#NX&KHR6Q8*rmeWM{ei1S^*f z6t|x8?Ls+5OT_J~+Qfc%qKP`TaNsu)mG{bL@J%5hv$}>@1ug zjq}q4{}OmM&R-PagK&N__6oU9^dZ=%17C+@1NO6U{wl&*cc(IUDEy z!m$Cz4jiw>{s?y3js1i!{Fcy%8;OF(xqUj#AWkyQhjV%+gb%}hnvtl$ zI3LdG^dW@L0oRQEWaHY|IKRTcwZ=J}9&KR_=jjtU{@TEEjdOi^t(;88@eJ&(`hpy9 z#c`sXA8%Z{5a$nI|BY~+I36qK$I0;m9Ph(^4fZFoABz15>=$9L#!lNq>fkJu!t=((ket7QV-&kjFddG_9B{=>;&KKkOAogbj{|tS^DD3ruQ#gJn=lZY# zClT!b#(sh(ar_VV<=AH!To~uK2p%waI{i}cKj?@⪻!K`&gXsguNZ-^Rd5*{Riy( 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

    O2q?CJr|kMX;}WY>|o=s`csMN!Np=4bzS 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 Di%?6P diff --git a/src/main/resources/assets/opencomputers/lib/lua52/native.64.dll b/src/main/resources/assets/opencomputers/lib/lua52/native.64.dll deleted file mode 100644 index 0b31407532b84c4a8bdc3f460b8db8f602a3f0da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1202688 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 t4CyDm@uf7~wV1xGf1X@yzSIn0& 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

    sHAblqv$DsZNm)r&X#78B=G7 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 z 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