├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle-keystore-gradle.properties ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src └── main ├── java └── io │ └── github │ └── cadiboo │ └── examplemod │ ├── ExampleMod.java │ ├── ForgeEventSubscriber.java │ ├── ModEventSubscriber.java │ ├── ModUtil.java │ ├── block │ ├── ElectricFurnaceBlock.java │ ├── HeatCollectorBlock.java │ ├── MiniModelBlock.java │ └── ModFurnaceBlock.java │ ├── client │ ├── ClientForgeEventSubscriber.java │ ├── ClientModEventSubscriber.java │ ├── gui │ │ ├── ElectricFurnaceScreen.java │ │ ├── HeatCollectorScreen.java │ │ ├── MiniModelScreen.java │ │ └── ModFurnaceScreen.java │ └── render │ │ ├── MiniModel.java │ │ ├── entity │ │ ├── WildBoarRenderer.java │ │ └── layer │ │ │ └── WildBoarSaddleLayer.java │ │ └── tileentity │ │ ├── ElectricFurnaceTileEntityRenderer.java │ │ └── MiniModelTileEntityRenderer.java │ ├── config │ ├── ClientConfig.java │ ├── ConfigHelper.java │ ├── ConfigHolder.java │ ├── ExampleModConfig.java │ └── ServerConfig.java │ ├── container │ ├── ElectricFurnaceContainer.java │ ├── FunctionalIntReferenceHolder.java │ ├── HeatCollectorContainer.java │ └── ModFurnaceContainer.java │ ├── energy │ └── SettableEnergyStorage.java │ ├── entity │ └── WildBoarEntity.java │ ├── init │ ├── ModBlocks.java │ ├── ModContainerTypes.java │ ├── ModEntityTypes.java │ ├── ModItemGroups.java │ ├── ModItems.java │ └── ModTileEntityTypes.java │ ├── item │ └── ModdedSpawnEggItem.java │ └── tileentity │ ├── ElectricFurnaceTileEntity.java │ ├── HeatCollectorTileEntity.java │ ├── MiniModelTileEntity.java │ └── ModFurnaceTileEntity.java └── resources ├── META-INF └── mods.toml ├── assets └── examplemod │ ├── blockstates │ ├── electric_furnace.json │ ├── example_block.json │ ├── example_ore.json │ ├── heat_collector.json │ ├── mini_model.json │ └── mod_furnace.json │ ├── lang │ └── en_us.json │ ├── models │ ├── block │ │ ├── electric_furnace.json │ │ ├── example_block.json │ │ ├── example_ore.json │ │ ├── heat_collector.json │ │ ├── mini_model.json │ │ ├── mod_furnace.json │ │ └── mod_furnace_burning.json │ └── item │ │ ├── electric_furnace.json │ │ ├── example_block.json │ │ ├── example_crystal.json │ │ ├── example_ore.json │ │ ├── heat_collector.json │ │ ├── mini_model.json │ │ ├── mod_furnace.json │ │ └── wild_boar_spawn_egg.json │ └── textures │ ├── block │ ├── electric_furnace_front.png │ ├── example_block.png │ ├── example_ore.png │ └── mini_model.png │ ├── entity │ └── wild_boar │ │ ├── wild_boar.png │ │ └── wild_boar_saddle.png │ ├── gui │ └── container │ │ ├── electric_furnace.png │ │ └── heat_collector.png │ └── item │ └── example_crystal.png ├── data └── examplemod │ ├── loot_tables │ └── blocks │ │ ├── electric_furnace.json │ │ ├── example_block.json │ │ ├── example_ore.json │ │ ├── heat_collector.json │ │ ├── mini_model.json │ │ └── mod_furnace.json │ └── recipes │ ├── example_block_from_example_crystal.json │ ├── example_crystal_from_example_block.json │ ├── example_crystal_from_smelting.json │ ├── heat_collector.json │ └── mini_model.json ├── examplemod.png └── pack.mcmeta /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | eclipse 4 | *.launch 5 | .settings 6 | .metadata 7 | .classpath 8 | .project 9 | 10 | # idea 11 | out 12 | production 13 | classes 14 | *.ipr 15 | *.iws 16 | *.iml 17 | .idea 18 | 19 | # gradle 20 | build 21 | .gradle 22 | 23 | # other 24 | run 25 | !LICENSE 26 | !README.* 27 | !.gitignore 28 | *.DS_Store* 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Example Mod](https://github.com/Cadiboo/Example-Mod) 2 | ### An example mod created by [Cadiboo](https://github.com/Cadiboo) to try and get new modders to use good code practices 3 | ##### View the tuorials for this at [https://cadiboo.github.io/tutorials/](https://cadiboo.github.io/tutorials/) 4 | This contains the basic setup for a normal mod, and the included files use good code practices. 5 | ### All credits for Forge, FML etc go to their respective owners. 6 | Any code written by me is free for any use at all. Some credit would be nice though :) 7 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Forge buildscript 2 | buildscript { 3 | repositories { 4 | // Forge Maven 5 | maven { url = 'https://files.minecraftforge.net/maven' } 6 | // Other useful Mavens 7 | jcenter() 8 | mavenCentral() 9 | } 10 | // Buildscript dependency on Forge Gradle 11 | dependencies { 12 | classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true 13 | } 14 | } 15 | apply plugin: 'net.minecraftforge.gradle' 16 | // Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. 17 | apply plugin: 'eclipse' 18 | apply plugin: 'maven-publish' 19 | 20 | // We use the version format MCVERSION-MAJOR.MINOR.PATCH 21 | version = modMinecraftVersion + "-" + modVersion 22 | group = modGroup 23 | archivesBaseName = modFileName 24 | 25 | sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. 26 | 27 | repositories { 28 | // Put remote maven repositories here 29 | } 30 | 31 | minecraft { 32 | // The mappings can be changed at any time, and must be in the following format. 33 | // snapshot_YYYYMMDD Snapshot are built nightly. 34 | // stable_# Stables are built at the discretion of the MCP team. 35 | // Use non-default mappings at your own risk. they may not always work. 36 | // Simply re-run your setup task after changing the mappings to update your workspace. 37 | mappings channel: modMcpMappingsChannel, version: modMcpMappingsVersion 38 | 39 | // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. 40 | 41 | // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') 42 | 43 | // Default run configurations. 44 | // These can be tweaked, removed, or duplicated as needed. 45 | runs { 46 | client { 47 | workingDirectory project.file('run') 48 | 49 | // Recommended logging data for a userdev environment 50 | property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP' 51 | 52 | // Recommended logging level for the console 53 | property 'forge.logging.console.level', 'debug' 54 | 55 | mods { 56 | examplemod { 57 | // Main sources (everything that goes in your final compiled mod. From /src/main/) 58 | source sourceSets.main 59 | // // Test sources (debugging, unit tests. From /src/test/) 60 | // source sourceSets.test 61 | } 62 | } 63 | } 64 | 65 | server { 66 | workingDirectory project.file('run') 67 | 68 | // Recommended logging data for a userdev environment 69 | property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP' 70 | 71 | // Recommended logging level for the console 72 | property 'forge.logging.console.level', 'debug' 73 | 74 | mods { 75 | examplemod { 76 | // Main sources (everything that goes in your final compiled mod. From /src/main/) 77 | source sourceSets.main 78 | // // Test sources (debugging, unit tests. From /src/test/) 79 | // source sourceSets.test 80 | } 81 | } 82 | } 83 | 84 | data { 85 | workingDirectory project.file('run') 86 | 87 | // Recommended logging data for a userdev environment 88 | property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP' 89 | 90 | // Recommended logging level for the console 91 | property 'forge.logging.console.level', 'debug' 92 | 93 | args '--mod', modId, '--all', '--output', file('src/generated/resources/') 94 | 95 | mods { 96 | examplemod { 97 | // Main sources (everything that goes in your final compiled mod. From /src/main/) 98 | source sourceSets.main 99 | // // Test sources (debugging, unit tests. From /src/test/) 100 | // source sourceSets.test 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | dependencies { 108 | // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed 109 | // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. 110 | // The userdev artifact is a special name and will get all sorts of transformations applied to it. 111 | minecraft "net.minecraftforge:forge:" + modMinecraftVersion + "-" + modForgeVersion 112 | 113 | // You may put jars on which you depend on in ./libs or you may define them like so.. 114 | // compile "some.group:artifact:version:classifier" 115 | // compile "some.group:artifact:version" 116 | 117 | // Real examples 118 | // compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env 119 | // compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env 120 | 121 | // The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. 122 | // provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' 123 | 124 | // These dependencies get remapped to your current MCP mappings 125 | // deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev' 126 | 127 | // For more info... 128 | // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html 129 | // http://www.gradle.org/docs/current/userguide/dependency_management.html 130 | 131 | } 132 | 133 | jar { 134 | manifest { 135 | attributes([ 136 | "Specification-Title" : modId, 137 | "Specification-Vendor" : modGroup, 138 | "Specification-Version" : "1.0", // We are version 1 of ourselves 139 | "Implementation-Title" : project.name, 140 | "Implementation-Version" : "${version}", 141 | "Implementation-Vendor" : modGroup, 142 | "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), 143 | "Signing-Fingerprint" : project.hasProperty('signSHA1') ? project.findProperty('signSHA1') : "unsigned" 144 | ]) 145 | } 146 | } 147 | 148 | // Example configuration to allow publishing using the maven-publish task 149 | // we define a custom artifact that is sourced from the reobfJar output task 150 | // and then declare that to be published 151 | // Note you'll need to add a repository here 152 | def reobfFile = file("$buildDir/reobfJar/output.jar") 153 | def reobfArtifact = artifacts.add('default', reobfFile) { 154 | type 'jar' 155 | builtBy 'reobfJar' 156 | } 157 | publishing { 158 | publications { 159 | mavenJava(MavenPublication) { 160 | artifact reobfArtifact 161 | } 162 | } 163 | repositories { 164 | maven { 165 | url "file:///${project.projectDir}/mcmodsrepo" 166 | } 167 | } 168 | } 169 | 170 | // Jar signing 171 | import net.minecraftforge.gradle.common.task.SignJar 172 | 173 | task signJar(type: SignJar, dependsOn: jar) { 174 | // Skips if the keyStore property is missing. 175 | onlyIf { 176 | project.hasProperty('keyStore') 177 | } 178 | 179 | // findProperty allows us to reference the property without it existing. 180 | // Using project.propName would cause the script to fail validation if the property did not exist. 181 | keyStore = project.findProperty('keyStore') 182 | alias = project.findProperty('keyStoreAlias') 183 | storePass = project.findProperty('keyStorePass') 184 | keyPass = project.findProperty('keyStoreKeyPass') 185 | inputFile = jar.archivePath 186 | outputFile = jar.archivePath 187 | } 188 | 189 | // Runs the signJar task automatically when build is run. 190 | build.dependsOn signJar 191 | 192 | // Process resources on build 193 | processResources { 194 | // This will ensure that this task is redone when the versions change. 195 | inputs.property 'version', project.version 196 | 197 | // Replace stuff in mods.toml, nothing else 198 | from(sourceSets.main.resources.srcDirs) { 199 | include 'META-INF/mods.toml' 200 | 201 | // Replace version 202 | expand 'version': project.version 203 | } 204 | 205 | // Copy everything else except the mods.toml 206 | from(sourceSets.main.resources.srcDirs) { 207 | exclude 'META-INF/mods.toml' 208 | } 209 | } 210 | 211 | // Create Sources Jar 212 | task sourcesJar(type: Jar, dependsOn: classes) { 213 | classifier = 'sources' 214 | from sourceSets.main.allSource 215 | } 216 | build.dependsOn sourcesJar 217 | 218 | //// Create Javadoc Jar 219 | //task javadocJar(type: Jar, dependsOn: javadoc) { 220 | // classifier = 'javadoc' 221 | // from javadoc.destinationDir 222 | //} 223 | //build.dependsOn javadocJar 224 | 225 | artifacts { 226 | archives sourcesJar 227 | // archives javadocJar 228 | } 229 | -------------------------------------------------------------------------------- /gradle-keystore-gradle.properties: -------------------------------------------------------------------------------- 1 | # This file should go in your global gradle cache named "gradle.properties" 2 | # My file is located at /Users/Cadiboo/.gradle/gradle.properties 3 | 4 | # Take a look at 5 | # https://tutorials.darkhax.net/tutorials/jar_signing/ 6 | # and 7 | # https://mcforge.readthedocs.io/en/latest/concepts/jarsigning/ 8 | 9 | # The full path to your keystore 10 | # Mine is /Users/Cadiboo/Modding/keystore.jks 11 | keyStore=/Full/Path/To/keystore.jks 12 | # The Alias of your keystore 13 | # Mine is Cadiboo 14 | keyStoreAlias= 15 | # The Passphrase of your keystore 16 | keyStorePass= 17 | # The Passphrase of your key 18 | keyStoreKeyPass= 19 | # The SHA-1 hash of the key, without colons, in lowercase 20 | # Mine is 1500c8bdd9178003afd944bc165f1984f9515082 21 | signSHA1= 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | 5 | # No deamon 6 | org.gradle.daemon=false 7 | 8 | # Taken from https://semver.org 9 | # Given a version number MAJOR.MINOR.PATCH, increment the: 10 | # MAJOR version when you make incompatible API changes, 11 | # MINOR version when you add functionality in a backwards-compatible manner, and 12 | # PATCH version when you make backwards-compatible bug fixes. 13 | # Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. 14 | # The initial alpha release is 0.1.0 and the initial public stable release should be 1.0.0 15 | modVersion = 0.1.0 16 | 17 | # The modid of our mod 18 | modId = examplemod 19 | 20 | # The maven group of your mod 21 | # See "http://maven.apache.org/guides/mini/guide-naming-conventions.html" 22 | # modGroup = your.website.modid 23 | # modGroup = mod.yourname.modid 24 | modGroup = io.github.cadiboo.examplemod 25 | 26 | # The name of the mod file (excluding the version) 27 | # Maven and Gradle don't like spaces in filenames, so use a different separator character like '-' 28 | modFileName = ModName 29 | # modFileName = Mod-Name 30 | 31 | # The version of Minecraft we are modding for 32 | modMinecraftVersion = 1.15.2 33 | 34 | # The MCP Mappings the source code of the mod will be built against 35 | # MCP Mappings are in the format 36 | # snapshot_YYYYMMDD 37 | # stable_# 38 | # Snapshot are built nightly and stables are built at the discretion of the MCP team 39 | # To change your mappings change the modMcpMappingsVersion property 40 | # (and the modMcpMappingsChannel property if you're switching in between stable and snapshot) 41 | # then refresh the gradle project in your IDE 42 | # and run the gredle task to generate the run configs for your IDE (genEclipseRuns or genIntellijRuns) 43 | modMcpMappingsChannel = snapshot 44 | modMcpMappingsVersion = 20200131-1.15.1 45 | 46 | # The Forge version the mod is being made for 47 | modForgeVersion = 31.0.14 48 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/ExampleMod.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod; 2 | 3 | import io.github.cadiboo.examplemod.config.ConfigHolder; 4 | import io.github.cadiboo.examplemod.init.ModBlocks; 5 | import io.github.cadiboo.examplemod.init.ModContainerTypes; 6 | import io.github.cadiboo.examplemod.init.ModEntityTypes; 7 | import io.github.cadiboo.examplemod.init.ModItems; 8 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 9 | import net.minecraftforge.eventbus.api.IEventBus; 10 | import net.minecraftforge.fml.ModLoadingContext; 11 | import net.minecraftforge.fml.common.Mod; 12 | import net.minecraftforge.fml.config.ModConfig; 13 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 14 | import org.apache.logging.log4j.LogManager; 15 | import org.apache.logging.log4j.Logger; 16 | 17 | /** 18 | * @author Cadiboo 19 | */ 20 | @Mod(ExampleMod.MODID) 21 | public final class ExampleMod { 22 | 23 | public static final String MODID = "examplemod"; 24 | 25 | public static final Logger LOGGER = LogManager.getLogger(MODID); 26 | 27 | public ExampleMod() { 28 | LOGGER.debug("Hello from Example Mod!"); 29 | 30 | final ModLoadingContext modLoadingContext = ModLoadingContext.get(); 31 | final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); 32 | 33 | // Register Deferred Registers (Does not need to be before Configs) 34 | ModBlocks.BLOCKS.register(modEventBus); 35 | ModItems.ITEMS.register(modEventBus); 36 | ModContainerTypes.CONTAINER_TYPES.register(modEventBus); 37 | ModEntityTypes.ENTITY_TYPES.register(modEventBus); 38 | ModTileEntityTypes.TILE_ENTITY_TYPES.register(modEventBus); 39 | // Register Configs (Does not need to be after Deferred Registers) 40 | modLoadingContext.registerConfig(ModConfig.Type.CLIENT, ConfigHolder.CLIENT_SPEC); 41 | modLoadingContext.registerConfig(ModConfig.Type.SERVER, ConfigHolder.SERVER_SPEC); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/ForgeEventSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod; 2 | 3 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 4 | 5 | /** 6 | * Subscribe to events from the FORGE EventBus that should be handled on both PHYSICAL sides in this class 7 | * 8 | * @author Cadiboo 9 | */ 10 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.FORGE) 11 | public final class ForgeEventSubscriber { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/ModEventSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod; 2 | 3 | import io.github.cadiboo.examplemod.config.ConfigHelper; 4 | import io.github.cadiboo.examplemod.config.ConfigHolder; 5 | import io.github.cadiboo.examplemod.init.ModBlocks; 6 | import io.github.cadiboo.examplemod.init.ModItemGroups; 7 | import io.github.cadiboo.examplemod.item.ModdedSpawnEggItem; 8 | import net.minecraft.entity.EntityType; 9 | import net.minecraft.item.BlockItem; 10 | import net.minecraft.item.Item; 11 | import net.minecraftforge.event.RegistryEvent; 12 | import net.minecraftforge.eventbus.api.EventPriority; 13 | import net.minecraftforge.eventbus.api.SubscribeEvent; 14 | import net.minecraftforge.fml.RegistryObject; 15 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 16 | import net.minecraftforge.fml.config.ModConfig; 17 | import net.minecraftforge.registries.IForgeRegistry; 18 | import org.apache.logging.log4j.LogManager; 19 | import org.apache.logging.log4j.Logger; 20 | 21 | /** 22 | * Subscribe to events from the MOD EventBus that should be handled on both PHYSICAL sides in this class 23 | * 24 | * @author Cadiboo 25 | */ 26 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD) 27 | public final class ModEventSubscriber { 28 | 29 | private static final Logger LOGGER = LogManager.getLogger(ExampleMod.MODID + " Mod Event Subscriber"); 30 | 31 | /** 32 | * This method will be called by Forge when it is time for the mod to register its Items. 33 | * This method will always be called after the Block registry method. 34 | */ 35 | @SubscribeEvent 36 | public static void onRegisterItems(final RegistryEvent.Register event) { 37 | final IForgeRegistry registry = event.getRegistry(); 38 | // Automatically register BlockItems for all our Blocks 39 | ModBlocks.BLOCKS.getEntries().stream() 40 | .map(RegistryObject::get) 41 | // You can do extra filtering here if you don't want some blocks to have an BlockItem automatically registered for them 42 | // .filter(block -> needsItemBlock(block)) 43 | // Register the BlockItem for the block 44 | .forEach(block -> { 45 | // Make the properties, and make it so that the item will be on our ItemGroup (CreativeTab) 46 | final Item.Properties properties = new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP); 47 | // Create the new BlockItem with the block and it's properties 48 | final BlockItem blockItem = new BlockItem(block, properties); 49 | // Set the new BlockItem's registry name to the block's registry name 50 | blockItem.setRegistryName(block.getRegistryName()); 51 | // Register the BlockItem 52 | registry.register(blockItem); 53 | }); 54 | LOGGER.debug("Registered BlockItems"); 55 | } 56 | 57 | /** 58 | * This method will be called by Forge when a config changes. 59 | */ 60 | @SubscribeEvent 61 | public static void onModConfigEvent(final ModConfig.ModConfigEvent event) { 62 | final ModConfig config = event.getConfig(); 63 | // Rebake the configs when they change 64 | if (config.getSpec() == ConfigHolder.CLIENT_SPEC) { 65 | ConfigHelper.bakeClient(config); 66 | LOGGER.debug("Baked client config"); 67 | } else if (config.getSpec() == ConfigHolder.SERVER_SPEC) { 68 | ConfigHelper.bakeServer(config); 69 | LOGGER.debug("Baked server config"); 70 | } 71 | } 72 | 73 | /** 74 | * Exists to work around a limitation with Spawn Eggs: 75 | * Spawn Eggs require an EntityType, but EntityTypes are created AFTER Items. 76 | * Therefore it is "impossible" for modded spawn eggs to exist. 77 | * To get around this we have our own custom SpawnEggItem, but it needs 78 | * some extra setup after Item and EntityType registration completes. 79 | */ 80 | @SubscribeEvent(priority = EventPriority.LOWEST) 81 | public static void onPostRegisterEntities(final RegistryEvent.Register> event) { 82 | ModdedSpawnEggItem.initUnaddedEggs(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/ModUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod; 2 | 3 | import net.minecraft.util.Direction; 4 | import net.minecraftforge.energy.EnergyStorage; 5 | 6 | /** 7 | * Assorted common utility code 8 | * 9 | * @author Cadiboo 10 | */ 11 | public final class ModUtil { 12 | 13 | /** 14 | * Cache all the directions instead of calling Direction.values() 15 | * each time (because each call creates a new Direction[] which is wasteful) 16 | * TODO: change to Direction.VALUES once it's ATed 17 | */ 18 | public static final Direction[] DIRECTIONS = Direction.values(); 19 | 20 | /** 21 | * This method calculates a comparator output for how "full" the energy storage is. 22 | * 23 | * @param energy The energy storage to test. 24 | * @return A redstone value in the range [0,15] representing how "full" this energy storage is. 25 | */ 26 | public static int calcRedstoneFromEnergyStorage(final EnergyStorage energy) { 27 | if (energy == null) 28 | return 0; 29 | return Math.round(energy.getEnergyStored() / ((float) energy.getMaxEnergyStored()) * 15F); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/block/ElectricFurnaceBlock.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.block; 2 | 3 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 4 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity; 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.block.HorizontalBlock; 8 | import net.minecraft.entity.player.PlayerEntity; 9 | import net.minecraft.entity.player.ServerPlayerEntity; 10 | import net.minecraft.inventory.InventoryHelper; 11 | import net.minecraft.item.BlockItemUseContext; 12 | import net.minecraft.state.StateContainer; 13 | import net.minecraft.tileentity.TileEntity; 14 | import net.minecraft.util.ActionResultType; 15 | import net.minecraft.util.Direction; 16 | import net.minecraft.util.Hand; 17 | import net.minecraft.util.Mirror; 18 | import net.minecraft.util.Rotation; 19 | import net.minecraft.util.math.BlockPos; 20 | import net.minecraft.util.math.BlockRayTraceResult; 21 | import net.minecraft.world.IBlockReader; 22 | import net.minecraft.world.World; 23 | import net.minecraftforge.fml.network.NetworkHooks; 24 | import net.minecraftforge.items.ItemHandlerHelper; 25 | import net.minecraftforge.items.ItemStackHandler; 26 | 27 | import javax.annotation.Nullable; 28 | 29 | /** 30 | * @author Cadiboo 31 | */ 32 | public class ElectricFurnaceBlock extends HorizontalBlock { 33 | 34 | public ElectricFurnaceBlock(final Properties properties) { 35 | super(properties); 36 | // Set the default values for our blockstate properties 37 | this.setDefaultState(this.getDefaultState() 38 | .with(HORIZONTAL_FACING, Direction.NORTH) 39 | ); 40 | } 41 | 42 | @Override 43 | public boolean hasTileEntity(final BlockState state) { 44 | return true; 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public TileEntity createTileEntity(final BlockState state, final IBlockReader world) { 50 | // Always use TileEntityType#create to allow registry overrides to work. 51 | return ModTileEntityTypes.ELECTRIC_FURNACE.get().create(); 52 | } 53 | 54 | /** 55 | * Called on the logical server when a BlockState with a TileEntity is replaced by another BlockState. 56 | * We use this method to drop all the items from our tile entity's inventory and update comparators near our block. 57 | * 58 | * @deprecated Call via {@link BlockState#onReplaced(World, BlockPos, BlockState, boolean)} 59 | * Implementing/overriding is fine. 60 | */ 61 | @Override 62 | public void onReplaced(BlockState oldState, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { 63 | if (oldState.getBlock() != newState.getBlock()) { 64 | TileEntity tileEntity = worldIn.getTileEntity(pos); 65 | if (tileEntity instanceof ElectricFurnaceTileEntity) { 66 | final ItemStackHandler inventory = ((ElectricFurnaceTileEntity) tileEntity).inventory; 67 | for (int slot = 0; slot < inventory.getSlots(); ++slot) 68 | InventoryHelper.spawnItemStack(worldIn, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot)); 69 | } 70 | } 71 | super.onReplaced(oldState, worldIn, pos, newState, isMoving); 72 | } 73 | 74 | /** 75 | * Called when a player right clicks our block. 76 | * We use this method to open our gui. 77 | * 78 | * @deprecated Call via {@link BlockState#onBlockActivated(World, PlayerEntity, Hand, BlockRayTraceResult)} whenever possible. 79 | * Implementing/overriding is fine. 80 | */ 81 | @Override 82 | public ActionResultType onBlockActivated(final BlockState state, final World worldIn, final BlockPos pos, final PlayerEntity player, final Hand handIn, final BlockRayTraceResult hit) { 83 | if (!worldIn.isRemote) { 84 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 85 | if (tileEntity instanceof ElectricFurnaceTileEntity) 86 | NetworkHooks.openGui((ServerPlayerEntity) player, (ElectricFurnaceTileEntity) tileEntity, pos); 87 | } 88 | return ActionResultType.SUCCESS; 89 | } 90 | 91 | /** 92 | * Makes the block face the player when placed 93 | */ 94 | @Override 95 | public BlockState getStateForPlacement(BlockItemUseContext context) { 96 | return this.getDefaultState().with(HORIZONTAL_FACING, context.getPlacementHorizontalFacing().getOpposite()); 97 | } 98 | 99 | /** 100 | * We return the redstone calculated from our inventory 101 | * 102 | * @deprecated call via {@link BlockState#getComparatorInputOverride(World, BlockPos)} whenever possible. 103 | * Implementing/overriding is fine. 104 | */ 105 | @Override 106 | public int getComparatorInputOverride(BlockState blockState, World worldIn, BlockPos pos) { 107 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 108 | if (tileEntity instanceof ElectricFurnaceTileEntity) 109 | return ItemHandlerHelper.calcRedstoneFromInventory(((ElectricFurnaceTileEntity) tileEntity).inventory); 110 | return super.getComparatorInputOverride(blockState, worldIn, pos); 111 | } 112 | 113 | /** 114 | * Called from inside the constructor {@link Block#Block(Properties)} to add all the properties to our blockstate 115 | */ 116 | @Override 117 | protected void fillStateContainer(final StateContainer.Builder builder) { 118 | super.fillStateContainer(builder); 119 | builder.add(HORIZONTAL_FACING); 120 | } 121 | 122 | /** 123 | * Returns the blockstate with the given rotation from the passed blockstate. 124 | * If inapplicable, returns the passed blockstate. 125 | * 126 | * @deprecated call via {@link BlockState#rotate(Rotation)} whenever possible. Implementing/overriding is fine. 127 | */ 128 | @Override 129 | public BlockState rotate(BlockState state, Rotation rot) { 130 | return state.with(HORIZONTAL_FACING, rot.rotate(state.get(HORIZONTAL_FACING))); 131 | } 132 | 133 | /** 134 | * Returns the blockstate with the given mirror of the passed blockstate. 135 | * If inapplicable, returns the passed blockstate. 136 | * 137 | * @deprecated call via {@link BlockState#mirror(Mirror)} whenever possible. Implementing/overriding is fine. 138 | */ 139 | @Override 140 | public BlockState mirror(BlockState state, Mirror mirrorIn) { 141 | return state.rotate(mirrorIn.toRotation(state.get(HORIZONTAL_FACING))); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/block/HeatCollectorBlock.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.block; 2 | 3 | import io.github.cadiboo.examplemod.ModUtil; 4 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 5 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity; 6 | import net.minecraft.block.Block; 7 | import net.minecraft.block.BlockState; 8 | import net.minecraft.entity.player.PlayerEntity; 9 | import net.minecraft.entity.player.ServerPlayerEntity; 10 | import net.minecraft.inventory.InventoryHelper; 11 | import net.minecraft.tileentity.TileEntity; 12 | import net.minecraft.util.ActionResultType; 13 | import net.minecraft.util.Hand; 14 | import net.minecraft.util.math.BlockPos; 15 | import net.minecraft.util.math.BlockRayTraceResult; 16 | import net.minecraft.util.math.shapes.IBooleanFunction; 17 | import net.minecraft.util.math.shapes.ISelectionContext; 18 | import net.minecraft.util.math.shapes.VoxelShape; 19 | import net.minecraft.util.math.shapes.VoxelShapes; 20 | import net.minecraft.world.IBlockReader; 21 | import net.minecraft.world.World; 22 | import net.minecraftforge.fml.network.NetworkHooks; 23 | import net.minecraftforge.items.ItemStackHandler; 24 | 25 | import javax.annotation.Nonnull; 26 | import javax.annotation.Nullable; 27 | import java.util.stream.Stream; 28 | 29 | /** 30 | * @author Cadiboo 31 | */ 32 | public class HeatCollectorBlock extends Block { 33 | 34 | private static final VoxelShape SHAPE = Stream.of( 35 | Block.makeCuboidShape(11, 0, 0, 16, 16, 5), 36 | Block.makeCuboidShape(11, 0, 11, 16, 16, 16), 37 | Block.makeCuboidShape(0, 0, 11, 5, 16, 16), 38 | Block.makeCuboidShape(0, 0, 0, 5, 16, 5), 39 | Block.makeCuboidShape(5, 5, 5, 11, 11, 11) 40 | ) 41 | .reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, IBooleanFunction.OR)) 42 | .get(); 43 | 44 | public HeatCollectorBlock(final Properties properties) { 45 | super(properties); 46 | } 47 | 48 | @Override 49 | public boolean hasTileEntity(final BlockState state) { 50 | return true; 51 | } 52 | 53 | @Nullable 54 | @Override 55 | public TileEntity createTileEntity(final BlockState state, final IBlockReader world) { 56 | // Always use TileEntityType#create to allow registry overrides to work. 57 | return ModTileEntityTypes.HEAT_COLLECTOR.get().create(); 58 | } 59 | 60 | /** 61 | * @deprecated Call via {@link BlockState#getShape(IBlockReader, BlockPos, ISelectionContext)} 62 | * Implementing/overriding is fine. 63 | */ 64 | @Nonnull 65 | @Override 66 | public VoxelShape getShape(final BlockState state, final IBlockReader worldIn, final BlockPos pos, final ISelectionContext context) { 67 | return SHAPE; 68 | } 69 | 70 | /** 71 | * Called on the logical server when a BlockState with a TileEntity is replaced by another BlockState. 72 | * We use this method to drop all the items from our tile entity's inventory and update comparators near our block. 73 | * 74 | * @deprecated Call via {@link BlockState#onReplaced(World, BlockPos, BlockState, boolean)} 75 | * Implementing/overriding is fine. 76 | */ 77 | @Override 78 | public void onReplaced(BlockState oldState, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { 79 | if (oldState.getBlock() != newState.getBlock()) { 80 | TileEntity tileEntity = worldIn.getTileEntity(pos); 81 | if (tileEntity instanceof HeatCollectorTileEntity) { 82 | final ItemStackHandler inventory = ((HeatCollectorTileEntity) tileEntity).inventory; 83 | for (int slot = 0; slot < inventory.getSlots(); ++slot) 84 | InventoryHelper.spawnItemStack(worldIn, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot)); 85 | } 86 | } 87 | super.onReplaced(oldState, worldIn, pos, newState, isMoving); 88 | } 89 | 90 | /** 91 | * Called when a player right clicks our block. 92 | * We use this method to open our gui. 93 | * 94 | * @deprecated Call via {@link BlockState#onBlockActivated(World, PlayerEntity, Hand, BlockRayTraceResult)} whenever possible. 95 | * Implementing/overriding is fine. 96 | */ 97 | @Override 98 | public ActionResultType onBlockActivated(final BlockState state, final World worldIn, final BlockPos pos, final PlayerEntity player, final Hand handIn, final BlockRayTraceResult hit) { 99 | if (!worldIn.isRemote) { 100 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 101 | if (tileEntity instanceof HeatCollectorTileEntity) 102 | NetworkHooks.openGui((ServerPlayerEntity) player, (HeatCollectorTileEntity) tileEntity, pos); 103 | } 104 | return ActionResultType.SUCCESS; 105 | } 106 | 107 | /** 108 | * We return the redstone calculated from our energy 109 | * 110 | * @deprecated call via {@link BlockState#getComparatorInputOverride(World, BlockPos)} whenever possible. 111 | * Implementing/overriding is fine. 112 | */ 113 | @Override 114 | public int getComparatorInputOverride(BlockState blockState, World worldIn, BlockPos pos) { 115 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 116 | if (tileEntity instanceof HeatCollectorTileEntity) 117 | return ModUtil.calcRedstoneFromEnergyStorage(((HeatCollectorTileEntity) tileEntity).energy); 118 | return super.getComparatorInputOverride(blockState, worldIn, pos); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/block/MiniModelBlock.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.block; 2 | 3 | import io.github.cadiboo.examplemod.client.gui.MiniModelScreen; 4 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 5 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity; 6 | import net.minecraft.block.Block; 7 | import net.minecraft.block.BlockState; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.entity.player.PlayerEntity; 10 | import net.minecraft.tileentity.TileEntity; 11 | import net.minecraft.util.ActionResultType; 12 | import net.minecraft.util.Hand; 13 | import net.minecraft.util.math.BlockPos; 14 | import net.minecraft.util.math.BlockRayTraceResult; 15 | import net.minecraft.world.IBlockReader; 16 | import net.minecraft.world.World; 17 | import net.minecraftforge.api.distmarker.Dist; 18 | import net.minecraftforge.api.distmarker.OnlyIn; 19 | import net.minecraftforge.fml.DistExecutor; 20 | 21 | import javax.annotation.Nullable; 22 | 23 | /** 24 | * @author Cadiboo 25 | */ 26 | public class MiniModelBlock extends Block { 27 | 28 | public MiniModelBlock(final Properties properties) { 29 | super(properties); 30 | } 31 | 32 | @Override 33 | public boolean hasTileEntity(final BlockState state) { 34 | return true; 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public TileEntity createTileEntity(final BlockState state, final IBlockReader world) { 40 | // Always use TileEntityType#create to allow registry overrides to work. 41 | return ModTileEntityTypes.MINI_MODEL.get().create(); 42 | } 43 | 44 | /** 45 | * Called when a player right clicks our block. 46 | * We use this method to open our gui. 47 | * 48 | * @deprecated Call via {@link BlockState#onBlockActivated(World, PlayerEntity, Hand, BlockRayTraceResult)} whenever possible. 49 | * Implementing/overriding is fine. 50 | */ 51 | @Override 52 | public ActionResultType onBlockActivated(final BlockState state, final World worldIn, final BlockPos pos, final PlayerEntity player, final Hand handIn, final BlockRayTraceResult hit) { 53 | // Only open the gui on the physical client 54 | DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> openGui(worldIn, pos)); 55 | return ActionResultType.SUCCESS; 56 | } 57 | 58 | // @OnlyIn(Dist.CLIENT) Makes it so this method will be removed from the class on the PHYSICAL SERVER 59 | // This is because we only want to handle opening the GUI on the physical client. 60 | @OnlyIn(Dist.CLIENT) 61 | private void openGui(final World worldIn, final BlockPos pos) { 62 | // Only handle opening the Gui screen on the logical client 63 | if (worldIn.isRemote) { 64 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 65 | if (tileEntity instanceof MiniModelTileEntity) { 66 | Minecraft.getInstance().displayGuiScreen(new MiniModelScreen(((MiniModelTileEntity) tileEntity))); 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/block/ModFurnaceBlock.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.block; 2 | 3 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 4 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity; 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.block.HorizontalBlock; 8 | import net.minecraft.entity.player.PlayerEntity; 9 | import net.minecraft.entity.player.ServerPlayerEntity; 10 | import net.minecraft.inventory.InventoryHelper; 11 | import net.minecraft.item.BlockItemUseContext; 12 | import net.minecraft.state.BooleanProperty; 13 | import net.minecraft.state.StateContainer; 14 | import net.minecraft.tileentity.TileEntity; 15 | import net.minecraft.util.ActionResultType; 16 | import net.minecraft.util.Direction; 17 | import net.minecraft.util.Hand; 18 | import net.minecraft.util.Mirror; 19 | import net.minecraft.util.Rotation; 20 | import net.minecraft.util.math.BlockPos; 21 | import net.minecraft.util.math.BlockRayTraceResult; 22 | import net.minecraft.world.IBlockReader; 23 | import net.minecraft.world.World; 24 | import net.minecraftforge.fml.network.NetworkHooks; 25 | import net.minecraftforge.items.ItemHandlerHelper; 26 | import net.minecraftforge.items.ItemStackHandler; 27 | 28 | import javax.annotation.Nullable; 29 | 30 | /** 31 | * @author Cadiboo 32 | */ 33 | public class ModFurnaceBlock extends HorizontalBlock { 34 | 35 | public static final BooleanProperty BURNING = BooleanProperty.create("burning"); 36 | 37 | public ModFurnaceBlock(final Properties properties) { 38 | super(properties); 39 | // Set the default values for our blockstate properties 40 | this.setDefaultState(this.getDefaultState() 41 | .with(HORIZONTAL_FACING, Direction.NORTH) 42 | .with(BURNING, false) 43 | ); 44 | } 45 | 46 | @Override 47 | public boolean hasTileEntity(final BlockState state) { 48 | return true; 49 | } 50 | 51 | @Nullable 52 | @Override 53 | public TileEntity createTileEntity(final BlockState state, final IBlockReader world) { 54 | // Always use TileEntityType#create to allow registry overrides to work. 55 | return ModTileEntityTypes.MOD_FURNACE.get().create(); 56 | } 57 | 58 | /** 59 | * Amount of light emitted 60 | * 61 | * @deprecated Call via {@link BlockState#getLightValue())} 62 | * Implementing/overriding is fine. 63 | */ 64 | public int getLightValue(BlockState state) { 65 | return state.get(BURNING) ? super.getLightValue(state) : 0; 66 | } 67 | 68 | /** 69 | * Called on the logical server when a BlockState with a TileEntity is replaced by another BlockState. 70 | * We use this method to drop all the items from our tile entity's inventory and update comparators near our block. 71 | * 72 | * @deprecated Call via {@link BlockState#onReplaced(World, BlockPos, BlockState, boolean)} 73 | * Implementing/overriding is fine. 74 | */ 75 | @Override 76 | public void onReplaced(BlockState oldState, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { 77 | if (oldState.getBlock() != newState.getBlock()) { 78 | TileEntity tileEntity = worldIn.getTileEntity(pos); 79 | if (tileEntity instanceof ModFurnaceTileEntity) { 80 | final ItemStackHandler inventory = ((ModFurnaceTileEntity) tileEntity).inventory; 81 | for (int slot = 0; slot < inventory.getSlots(); ++slot) 82 | InventoryHelper.spawnItemStack(worldIn, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot)); 83 | } 84 | } 85 | super.onReplaced(oldState, worldIn, pos, newState, isMoving); 86 | } 87 | 88 | /** 89 | * Called when a player right clicks our block. 90 | * We use this method to open our gui. 91 | * 92 | * @deprecated Call via {@link BlockState#onBlockActivated(World, PlayerEntity, Hand, BlockRayTraceResult)} whenever possible. 93 | * Implementing/overriding is fine. 94 | */ 95 | @Override 96 | public ActionResultType onBlockActivated(final BlockState state, final World worldIn, final BlockPos pos, final PlayerEntity player, final Hand handIn, final BlockRayTraceResult hit) { 97 | if (!worldIn.isRemote) { 98 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 99 | if (tileEntity instanceof ModFurnaceTileEntity) 100 | NetworkHooks.openGui((ServerPlayerEntity) player, (ModFurnaceTileEntity) tileEntity, pos); 101 | } 102 | return ActionResultType.SUCCESS; 103 | } 104 | 105 | /** 106 | * Makes the block face the player when placed 107 | */ 108 | @Override 109 | public BlockState getStateForPlacement(BlockItemUseContext context) { 110 | return this.getDefaultState().with(HORIZONTAL_FACING, context.getPlacementHorizontalFacing().getOpposite()); 111 | } 112 | 113 | /** 114 | * We return the redstone calculated from our inventory 115 | * 116 | * @deprecated call via {@link BlockState#getComparatorInputOverride(World, BlockPos)} whenever possible. 117 | * Implementing/overriding is fine. 118 | */ 119 | @Override 120 | public int getComparatorInputOverride(BlockState blockState, World worldIn, BlockPos pos) { 121 | final TileEntity tileEntity = worldIn.getTileEntity(pos); 122 | if (tileEntity instanceof ModFurnaceTileEntity) 123 | return ItemHandlerHelper.calcRedstoneFromInventory(((ModFurnaceTileEntity) tileEntity).inventory); 124 | return super.getComparatorInputOverride(blockState, worldIn, pos); 125 | } 126 | 127 | /** 128 | * Called from inside the constructor {@link Block#Block(Properties)} to add all the properties to our blockstate 129 | */ 130 | @Override 131 | protected void fillStateContainer(final StateContainer.Builder builder) { 132 | super.fillStateContainer(builder); 133 | builder.add(HORIZONTAL_FACING); 134 | builder.add(BURNING); 135 | } 136 | 137 | /** 138 | * Returns the blockstate with the given rotation from the passed blockstate. 139 | * If inapplicable, returns the passed blockstate. 140 | * 141 | * @deprecated call via {@link BlockState#rotate(Rotation)} whenever possible. Implementing/overriding is fine. 142 | */ 143 | @Override 144 | public BlockState rotate(BlockState state, Rotation rot) { 145 | return state.with(HORIZONTAL_FACING, rot.rotate(state.get(HORIZONTAL_FACING))); 146 | } 147 | 148 | /** 149 | * Returns the blockstate with the given mirror of the passed blockstate. 150 | * If inapplicable, returns the passed blockstate. 151 | * 152 | * @deprecated call via {@link BlockState#mirror(Mirror)} whenever possible. Implementing/overriding is fine. 153 | */ 154 | @Override 155 | public BlockState mirror(BlockState state, Mirror mirrorIn) { 156 | return state.rotate(mirrorIn.toRotation(state.get(HORIZONTAL_FACING))); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/ClientForgeEventSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 6 | 7 | /** 8 | * Subscribe to events from the FORGE EventBus that should be handled on the PHYSICAL CLIENT side in this class 9 | * 10 | * @author Cadiboo 11 | */ 12 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT) 13 | public final class ClientForgeEventSubscriber { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/ClientModEventSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.client.gui.ElectricFurnaceScreen; 5 | import io.github.cadiboo.examplemod.client.gui.HeatCollectorScreen; 6 | import io.github.cadiboo.examplemod.client.gui.ModFurnaceScreen; 7 | import io.github.cadiboo.examplemod.client.render.entity.WildBoarRenderer; 8 | import io.github.cadiboo.examplemod.client.render.tileentity.ElectricFurnaceTileEntityRenderer; 9 | import io.github.cadiboo.examplemod.client.render.tileentity.MiniModelTileEntityRenderer; 10 | import io.github.cadiboo.examplemod.init.ModContainerTypes; 11 | import io.github.cadiboo.examplemod.init.ModEntityTypes; 12 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 13 | import net.minecraft.client.gui.ScreenManager; 14 | import net.minecraftforge.api.distmarker.Dist; 15 | import net.minecraftforge.eventbus.api.SubscribeEvent; 16 | import net.minecraftforge.fml.DeferredWorkQueue; 17 | import net.minecraftforge.fml.client.registry.ClientRegistry; 18 | import net.minecraftforge.fml.client.registry.RenderingRegistry; 19 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 20 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 21 | import org.apache.logging.log4j.LogManager; 22 | import org.apache.logging.log4j.Logger; 23 | 24 | /** 25 | * Subscribe to events from the MOD EventBus that should be handled on the PHYSICAL CLIENT side in this class 26 | * 27 | * @author Cadiboo 28 | */ 29 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) 30 | public final class ClientModEventSubscriber { 31 | 32 | private static final Logger LOGGER = LogManager.getLogger(ExampleMod.MODID + " Client Mod Event Subscriber"); 33 | 34 | /** 35 | * We need to register our renderers on the client because rendering code does not exist on the server 36 | * and trying to use it on a dedicated server will crash the game. 37 | *

38 | * This method will be called by Forge when it is time for the mod to do its client-side setup 39 | * This method will always be called after the Registry events. 40 | * This means that all Blocks, Items, TileEntityTypes, etc. will all have been registered already 41 | */ 42 | @SubscribeEvent 43 | public static void onFMLClientSetupEvent(final FMLClientSetupEvent event) { 44 | 45 | // Register TileEntity Renderers 46 | ClientRegistry.bindTileEntityRenderer(ModTileEntityTypes.MINI_MODEL.get(), MiniModelTileEntityRenderer::new); 47 | ClientRegistry.bindTileEntityRenderer(ModTileEntityTypes.ELECTRIC_FURNACE.get(), ElectricFurnaceTileEntityRenderer::new); 48 | LOGGER.debug("Registered TileEntity Renderers"); 49 | 50 | // Register Entity Renderers 51 | RenderingRegistry.registerEntityRenderingHandler(ModEntityTypes.WILD_BOAR.get(), WildBoarRenderer::new); 52 | LOGGER.debug("Registered Entity Renderers"); 53 | 54 | // Register ContainerType Screens 55 | // ScreenManager.registerFactory is not safe to call during parallel mod loading so we queue it to run later 56 | DeferredWorkQueue.runLater(() -> { 57 | ScreenManager.registerFactory(ModContainerTypes.HEAT_COLLECTOR.get(), HeatCollectorScreen::new); 58 | ScreenManager.registerFactory(ModContainerTypes.ELECTRIC_FURNACE.get(), ElectricFurnaceScreen::new); 59 | ScreenManager.registerFactory(ModContainerTypes.MOD_FURNACE.get(), ModFurnaceScreen::new); 60 | LOGGER.debug("Registered ContainerType Screens"); 61 | }); 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/gui/ElectricFurnaceScreen.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.gui; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import io.github.cadiboo.examplemod.ExampleMod; 5 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer; 6 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity; 7 | import net.minecraft.client.gui.screen.inventory.ContainerScreen; 8 | import net.minecraft.entity.player.PlayerInventory; 9 | import net.minecraft.util.ResourceLocation; 10 | import net.minecraft.util.text.ITextComponent; 11 | import net.minecraft.util.text.TranslationTextComponent; 12 | import net.minecraftforge.energy.EnergyStorage; 13 | 14 | /** 15 | * @author Cadiboo 16 | */ 17 | public class ElectricFurnaceScreen extends ContainerScreen { 18 | 19 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/gui/container/electric_furnace.png"); 20 | 21 | public ElectricFurnaceScreen(final ElectricFurnaceContainer container, final PlayerInventory inventory, final ITextComponent title) { 22 | super(container, inventory, title); 23 | } 24 | 25 | @Override 26 | public void render(final int mouseX, final int mouseY, final float partialTicks) { 27 | this.renderBackground(); 28 | super.render(mouseX, mouseY, partialTicks); 29 | this.renderHoveredToolTip(mouseX, mouseY); 30 | 31 | int relMouseX = mouseX - this.guiLeft; 32 | int relMouseY = mouseY - this.guiTop; 33 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity; 34 | boolean energyBarHovered = relMouseX > 151 && relMouseX < 166 && relMouseY > 10 && relMouseY < 76; 35 | if (energyBarHovered) { 36 | String tooltip = new TranslationTextComponent( 37 | "gui." + ExampleMod.MODID + ".energy", 38 | tileEntity.energy.getEnergyStored() 39 | ).getFormattedText(); 40 | this.renderTooltip(tooltip, mouseX, mouseY); 41 | } 42 | boolean arrowHovered = relMouseX > 79 && relMouseX < 104 && relMouseY > 34 && relMouseY < 50; 43 | if (arrowHovered && tileEntity.maxSmeltTime > 0) { 44 | String tooltip = new TranslationTextComponent( 45 | "gui." + ExampleMod.MODID + ".smeltTimeProgress", 46 | tileEntity.smeltTimeLeft, tileEntity.maxSmeltTime 47 | ).getFormattedText(); 48 | this.renderTooltip(tooltip, mouseX, mouseY); 49 | } 50 | } 51 | 52 | @Override 53 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) { 54 | super.drawGuiContainerForegroundLayer(mouseX, mouseY); 55 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer 56 | String s = this.title.getFormattedText(); 57 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040); 58 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040); 59 | } 60 | 61 | @Override 62 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) { 63 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); 64 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE); 65 | int startX = this.guiLeft; 66 | int startY = this.guiTop; 67 | 68 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen 69 | // The parameters are (x, y, u, v, width, height) 70 | 71 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize); 72 | 73 | final ElectricFurnaceTileEntity tileEntity = container.tileEntity; 74 | if (tileEntity.energy.getEnergyStored() > 0) { // Draw energy bar 75 | int energyProgress = getEnergyProgressScaled(); 76 | this.blit( 77 | startX + 152, startY + 10 + 65 - energyProgress, 78 | 176, 16, 79 | 14, energyProgress 80 | ); 81 | } 82 | if (tileEntity.smeltTimeLeft > 0) { 83 | // Draw progress arrow 84 | int arrowWidth = getSmeltTimeScaled(); 85 | this.blit( 86 | startX + 79, startY + 34, 87 | 176, 0, 88 | arrowWidth, 16 89 | ); 90 | } 91 | } 92 | 93 | private int getEnergyProgressScaled() { 94 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity; 95 | final EnergyStorage energy = tileEntity.energy; 96 | final int energyStored = energy.getEnergyStored(); 97 | final int maxEnergyStored = energy.getMaxEnergyStored(); 98 | return Math.round((float) energyStored / maxEnergyStored * 65); // 65 is the height of the arrow 99 | } 100 | 101 | private int getSmeltTimeScaled() { 102 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity; 103 | final short smeltTimeLeft = tileEntity.smeltTimeLeft; 104 | final short maxSmeltTime = tileEntity.maxSmeltTime; 105 | if (smeltTimeLeft <= 0 || maxSmeltTime <= 0) 106 | return 0; 107 | return (maxSmeltTime - smeltTimeLeft) * 24 / maxSmeltTime; // 24 is the width of the arrow 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/gui/HeatCollectorScreen.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.gui; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import io.github.cadiboo.examplemod.ExampleMod; 5 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer; 6 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage; 7 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity; 8 | import net.minecraft.client.gui.screen.inventory.ContainerScreen; 9 | import net.minecraft.entity.player.PlayerInventory; 10 | import net.minecraft.util.ResourceLocation; 11 | import net.minecraft.util.text.ITextComponent; 12 | import net.minecraft.util.text.TranslationTextComponent; 13 | 14 | /** 15 | * @author Cadiboo 16 | */ 17 | public class HeatCollectorScreen extends ContainerScreen { 18 | 19 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/gui/container/heat_collector.png"); 20 | 21 | public HeatCollectorScreen(final HeatCollectorContainer container, final PlayerInventory inventory, final ITextComponent title) { 22 | super(container, inventory, title); 23 | } 24 | 25 | @Override 26 | public void render(final int mouseX, final int mouseY, final float partialTicks) { 27 | this.renderBackground(); 28 | super.render(mouseX, mouseY, partialTicks); 29 | this.renderHoveredToolTip(mouseX, mouseY); 30 | 31 | int relMouseX = mouseX - this.guiLeft; 32 | int relMouseY = mouseY - this.guiTop; 33 | boolean energyBarHovered = relMouseX > 151 && relMouseX < 166 && relMouseY > 10 && relMouseY < 76; 34 | if (energyBarHovered) { 35 | String tooltip = new TranslationTextComponent( 36 | "gui." + ExampleMod.MODID + ".energy", 37 | this.container.tileEntity.energy.getEnergyStored() 38 | ).getFormattedText(); 39 | this.renderTooltip(tooltip, mouseX, mouseY); 40 | } 41 | } 42 | 43 | @Override 44 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) { 45 | super.drawGuiContainerForegroundLayer(mouseX, mouseY); 46 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer 47 | String s = this.title.getFormattedText(); 48 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040); 49 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040); 50 | } 51 | 52 | @Override 53 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) { 54 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); 55 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE); 56 | int startX = this.guiLeft; 57 | int startY = this.guiTop; 58 | 59 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen 60 | // The parameters are (x, y, u, v, width, height) 61 | 62 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize); 63 | 64 | final HeatCollectorTileEntity tileEntity = container.tileEntity; 65 | 66 | final SettableEnergyStorage energy = tileEntity.energy; 67 | final int energyStored = energy.getEnergyStored(); 68 | if (energyStored > 0) { // Draw energy bar 69 | final int energyProgress = Math.round((float) energyStored / energy.getMaxEnergyStored() * 65); 70 | this.blit( 71 | startX + 152, startY + 10 + 65 - energyProgress, 72 | 176, 14, 73 | 14, energyProgress 74 | ); 75 | } 76 | 77 | if (!tileEntity.inventory.getStackInSlot(HeatCollectorTileEntity.FUEL_SLOT).isEmpty()) // Draw flames 78 | this.blit( 79 | startX + 81, startY + 58, 80 | 176, 0, 81 | 14, 14 82 | ); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/gui/MiniModelScreen.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.gui; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.client.render.MiniModel; 5 | import io.github.cadiboo.examplemod.init.ModBlocks; 6 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import net.minecraft.client.resources.I18n; 9 | import net.minecraftforge.fml.client.gui.widget.ExtendedButton; 10 | 11 | /** 12 | * A Screen for refreshing our MiniMode. 13 | * It contains two buttons "Refresh Mini Model" and "Done" 14 | * 15 | * @author Cadiboo 16 | */ 17 | public class MiniModelScreen extends Screen { 18 | 19 | private final MiniModelTileEntity tileEntity; 20 | 21 | public MiniModelScreen(final MiniModelTileEntity tileEntity) { 22 | super(ModBlocks.MINI_MODEL.get().getNameTextComponent()); 23 | this.tileEntity = tileEntity; 24 | } 25 | 26 | @Override 27 | public void render(final int mouseX, final int mouseY, final float partialTicks) { 28 | this.renderBackground(); 29 | super.render(mouseX, mouseY, partialTicks); 30 | } 31 | 32 | @Override 33 | protected void init() { 34 | final int halfW = this.width / 2; 35 | final int halfH = this.height / 2; 36 | // "Refresh Mini Model" button rebuilds the tile's MiniModel 37 | this.addButton(new ExtendedButton(halfW - 150, halfH, 150, 20, I18n.format("gui." + ExampleMod.MODID + ".refresh_mini_model"), 38 | $ -> { 39 | final MiniModel miniModel = this.tileEntity.miniModel; 40 | if (miniModel != null) 41 | miniModel.compile(); 42 | } 43 | )); 44 | // "Done" button exits the GUI 45 | this.addButton(new ExtendedButton(halfW, halfH, 150, 20, I18n.format("gui.done"), 46 | $ -> this.minecraft.displayGuiScreen(null) 47 | )); 48 | super.init(); 49 | } 50 | 51 | @Override 52 | public boolean isPauseScreen() { 53 | return false; // Don't pause the game when this screen is open 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/gui/ModFurnaceScreen.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.gui; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import io.github.cadiboo.examplemod.ExampleMod; 5 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer; 6 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity; 7 | import net.minecraft.client.gui.screen.inventory.ContainerScreen; 8 | import net.minecraft.entity.player.PlayerInventory; 9 | import net.minecraft.util.ResourceLocation; 10 | import net.minecraft.util.text.ITextComponent; 11 | import net.minecraft.util.text.TranslationTextComponent; 12 | 13 | /** 14 | * @author Cadiboo 15 | */ 16 | public class ModFurnaceScreen extends ContainerScreen { 17 | 18 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation("minecraft", "textures/gui/container/furnace.png"); 19 | 20 | public ModFurnaceScreen(final ModFurnaceContainer container, final PlayerInventory inventory, final ITextComponent title) { 21 | super(container, inventory, title); 22 | } 23 | 24 | @Override 25 | public void render(final int mouseX, final int mouseY, final float partialTicks) { 26 | this.renderBackground(); 27 | super.render(mouseX, mouseY, partialTicks); 28 | this.renderHoveredToolTip(mouseX, mouseY); 29 | 30 | int relMouseX = mouseX - this.guiLeft; 31 | int relMouseY = mouseY - this.guiTop; 32 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity; 33 | boolean arrowHovered = relMouseX > 79 && relMouseX < 104 && relMouseY > 34 && relMouseY < 50; 34 | if (arrowHovered && tileEntity.maxSmeltTime > 0) { 35 | String tooltip = new TranslationTextComponent( 36 | "gui." + ExampleMod.MODID + ".smeltTimeProgress", 37 | tileEntity.smeltTimeLeft, tileEntity.maxSmeltTime 38 | ).getFormattedText(); 39 | this.renderTooltip(tooltip, mouseX, mouseY); 40 | } 41 | boolean fireHovered = relMouseX > 56 && relMouseX < 70 && relMouseY > 36 && relMouseY < 50; 42 | if (fireHovered && tileEntity.maxFuelBurnTime > 0) { 43 | String tooltip = new TranslationTextComponent( 44 | "gui." + ExampleMod.MODID + ".fuelBurnTimeProgress", 45 | tileEntity.fuelBurnTimeLeft, tileEntity.maxFuelBurnTime 46 | ).getFormattedText(); 47 | this.renderTooltip(tooltip, mouseX, mouseY); 48 | } 49 | } 50 | 51 | @Override 52 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) { 53 | super.drawGuiContainerForegroundLayer(mouseX, mouseY); 54 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer 55 | String s = this.title.getFormattedText(); 56 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040); 57 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040); 58 | 59 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity; 60 | if (tileEntity.smeltTimeLeft > 0) 61 | this.font.drawString(tileEntity.smeltTimeLeft + " / " + tileEntity.maxSmeltTime, 8.0F, this.ySize, 0x404040); 62 | this.font.drawString(tileEntity.fuelBurnTimeLeft + " / " + tileEntity.maxFuelBurnTime, 8.0F, this.ySize + 14, 0x404040); 63 | } 64 | 65 | @Override 66 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) { 67 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); 68 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE); 69 | int startX = this.guiLeft; 70 | int startY = this.guiTop; 71 | 72 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen 73 | // The parameters are (x, y, u, v, width, height) 74 | 75 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize); 76 | 77 | final ModFurnaceTileEntity tileEntity = container.tileEntity; 78 | if (tileEntity.smeltTimeLeft > 0) { 79 | // Draw progress arrow 80 | int arrowWidth = getSmeltTimeScaled(); 81 | this.blit( 82 | startX + 79, startY + 34, 83 | 176, 14, 84 | arrowWidth, 14 85 | ); 86 | } 87 | if (tileEntity.isBurning()) { 88 | // Draw flames 89 | int flameHeight = getFuelBurnTimeScaled(); 90 | this.blit( 91 | startX + 56, startY + 50 - flameHeight, 92 | 176, 14 - flameHeight, 93 | 14, flameHeight 94 | ); 95 | } 96 | } 97 | 98 | private int getSmeltTimeScaled() { 99 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity; 100 | final short smeltTimeLeft = tileEntity.smeltTimeLeft; 101 | final short maxSmeltTime = tileEntity.maxSmeltTime; 102 | if (smeltTimeLeft <= 0 || maxSmeltTime <= 0) 103 | return 0; 104 | return (maxSmeltTime - smeltTimeLeft) * 24 / maxSmeltTime; // 24 is the width of the arrow 105 | } 106 | 107 | private int getFuelBurnTimeScaled() { 108 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity; 109 | if (tileEntity.maxFuelBurnTime <= 0) 110 | return 0; 111 | return tileEntity.fuelBurnTimeLeft * 16 / tileEntity.maxFuelBurnTime; // 14 is the height of the flames 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/render/MiniModel.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.render; 2 | 3 | import net.minecraft.tileentity.TileEntity; 4 | 5 | /** 6 | * A wrapper around ChunkRender. 7 | * Stores the a render of the chunk (16x16x16) surrounding a TileEntity 8 | * TODO: Make this work on 1.15 9 | * 10 | * @author Cadiboo 11 | */ 12 | public class MiniModel { 13 | 14 | // // We only create one of these per cache, we reset it each time we rebuild 15 | // public final RegionRenderCacheBuilder regionRenderCacheBuilder; 16 | // private final ChunkRender chunkRender; 17 | // public ChunkRenderTask generator; 18 | private boolean isCompiled = false; 19 | 20 | // private MiniModel(final ChunkRender chunkRender, final RegionRenderCacheBuilder regionRenderCacheBuilder) { 21 | // this.chunkRender = chunkRender; 22 | // this.regionRenderCacheBuilder = regionRenderCacheBuilder; 23 | // } 24 | 25 | public static MiniModel forTileEntity(final TileEntity tileEntity) { 26 | // final ChunkRender chunkRender = new ChunkRender(tileEntity.getWorld(), Minecraft.getInstance().worldRenderer); 27 | // final BlockPos pos = tileEntity.getPos(); 28 | // 29 | // // We want to render everything in a 16x16x16 radius, with the centre being the TileEntity 30 | // chunkRender.setPosition(pos.getX() - 8, pos.getY() - 8, pos.getZ() - 8); 31 | // 32 | // return new MiniModel(chunkRender, new RegionRenderCacheBuilder()); 33 | return null; 34 | } 35 | 36 | /** 37 | * (re)build the render 38 | */ 39 | public void compile() { 40 | // final ChunkRender chunkRender = this.chunkRender; 41 | // final RegionRenderCacheBuilder buffers = this.regionRenderCacheBuilder; 42 | // 43 | // final ChunkRenderTask generator = chunkRender.makeCompileTaskChunk(); 44 | // this.generator = generator; 45 | // 46 | // // Setup generator 47 | // generator.setStatus(ChunkRenderTask.Status.COMPILING); 48 | // generator.setRegionRenderCacheBuilder(buffers); 49 | // 50 | // final Vec3d vec3d = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView(); 51 | // 52 | // // Rebuild the ChunkRender. 53 | // // This resets all the buffers it uses and renders every block in the chunk to the buffers 54 | // chunkRender.rebuildChunk((float) vec3d.x, (float) vec3d.y, (float) vec3d.z, generator); 55 | // 56 | // // ChunkRender#rebuildChunk increments this, we don't want it incremented so we decrement it. 57 | // --ChunkRender.renderChunksUpdated; 58 | // 59 | // // Set the translation of each buffer back to 0 60 | // final int length = BLOCK_RENDER_LAYERS.length; 61 | // for (int ordinal = 0; ordinal < length; ++ordinal) { 62 | // buffers.getBuilder(ordinal).setTranslation(0, 0, 0); 63 | // } 64 | // this.isBuilt = true; 65 | } 66 | 67 | public boolean isCompiled() { 68 | return isCompiled; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/render/entity/WildBoarRenderer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.render.entity; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.client.render.entity.layer.WildBoarSaddleLayer; 5 | import io.github.cadiboo.examplemod.entity.WildBoarEntity; 6 | import net.minecraft.client.renderer.entity.EntityRendererManager; 7 | import net.minecraft.client.renderer.entity.MobRenderer; 8 | import net.minecraft.client.renderer.entity.model.PigModel; 9 | import net.minecraft.util.ResourceLocation; 10 | 11 | /** 12 | * Handles rendering all WildBoar Entities. 13 | * The render method is called once each frame for every visible WildBoar. 14 | *

15 | * We use a PigModel in our renderer and simply change it's texture. 16 | * 17 | * @author Cadiboo 18 | */ 19 | public class WildBoarRenderer extends MobRenderer> { 20 | 21 | private static final ResourceLocation WILD_BOAR_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/entity/wild_boar/wild_boar.png"); 22 | 23 | public WildBoarRenderer(final EntityRendererManager manager) { 24 | super(manager, new PigModel<>(), 0.7F); 25 | this.addLayer(new WildBoarSaddleLayer(this)); 26 | } 27 | 28 | @Override 29 | public ResourceLocation getEntityTexture(final WildBoarEntity entity) { 30 | return WILD_BOAR_TEXTURE; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/render/entity/layer/WildBoarSaddleLayer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.render.entity.layer; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | import com.mojang.blaze3d.vertex.IVertexBuilder; 5 | import io.github.cadiboo.examplemod.ExampleMod; 6 | import io.github.cadiboo.examplemod.entity.WildBoarEntity; 7 | import net.minecraft.client.renderer.IRenderTypeBuffer; 8 | import net.minecraft.client.renderer.RenderType; 9 | import net.minecraft.client.renderer.entity.IEntityRenderer; 10 | import net.minecraft.client.renderer.entity.layers.LayerRenderer; 11 | import net.minecraft.client.renderer.entity.layers.SaddleLayer; 12 | import net.minecraft.client.renderer.entity.model.PigModel; 13 | import net.minecraft.client.renderer.texture.OverlayTexture; 14 | import net.minecraft.util.ResourceLocation; 15 | 16 | /** 17 | * Copy of {@link SaddleLayer} with tweaks to make it work for WildBoarEntity. 18 | * 19 | * @author Cadiboo 20 | */ 21 | public class WildBoarSaddleLayer extends LayerRenderer> { 22 | 23 | private static final ResourceLocation TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/entity/wild_boar/wild_boar_saddle.png"); 24 | private final PigModel pigModel = new PigModel<>(0.5F); 25 | 26 | public WildBoarSaddleLayer(IEntityRenderer> p_i50927_1_) { 27 | super(p_i50927_1_); 28 | } 29 | 30 | @Override 31 | public void render(MatrixStack matrixStack, IRenderTypeBuffer renderTypeBuffer, int light, WildBoarEntity entity, float p_225628_5_, float p_225628_6_, float p_225628_7_, float p_225628_8_, float p_225628_9_, float p_225628_10_) { 32 | if (entity.getSaddled()) { 33 | this.getEntityModel().setModelAttributes(this.pigModel); 34 | this.pigModel.setLivingAnimations(entity, p_225628_5_, p_225628_6_, p_225628_7_); 35 | this.pigModel.render(entity, p_225628_5_, p_225628_6_, p_225628_8_, p_225628_9_, p_225628_10_); 36 | IVertexBuilder buffer = renderTypeBuffer.getBuffer(RenderType.entityCutoutNoCull(TEXTURE)); 37 | this.pigModel.render(matrixStack, buffer, light, OverlayTexture.DEFAULT_LIGHT, 1.0F, 1.0F, 1.0F, 1.0F); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/render/tileentity/ElectricFurnaceTileEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.render.tileentity; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | import io.github.cadiboo.examplemod.config.ExampleModConfig; 5 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.block.Blocks; 8 | import net.minecraft.block.RedstoneTorchBlock; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.renderer.IRenderTypeBuffer; 11 | import net.minecraft.client.renderer.tileentity.TileEntityRenderer; 12 | import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; 13 | 14 | /** 15 | * Handles rendering all ElectricFurnace TileEntities. 16 | * The render method is called once each frame for every visible ElectricFurnace. 17 | * 18 | * @author Cadiboo 19 | */ 20 | public class ElectricFurnaceTileEntityRenderer extends TileEntityRenderer { 21 | 22 | public ElectricFurnaceTileEntityRenderer(final TileEntityRendererDispatcher tileEntityRendererDispatcher) { 23 | super(tileEntityRendererDispatcher); 24 | } 25 | 26 | /** 27 | * Render our TileEntity 28 | */ 29 | @Override 30 | public void render(final ElectricFurnaceTileEntity tileEntityIn, final float partialTicks, final MatrixStack matrixStack, final IRenderTypeBuffer renderTypeBuffer, final int packedLight, final int backupPackedLight) { 31 | // TODO: Fix this up to actually do the rendering I want 32 | 33 | final boolean hasEnergy = tileEntityIn.energy.getEnergyStored() >= ExampleModConfig.electricFurnaceEnergySmeltCostPerTick; 34 | final BlockState renderState = Blocks.REDSTONE_TORCH.getDefaultState() 35 | .with(RedstoneTorchBlock.LIT, hasEnergy); 36 | // Render the torch (We use the depreciated method because we don't have an IModelData instance and want to use the default one) 37 | Minecraft.getInstance().getBlockRendererDispatcher().renderBlock(renderState, matrixStack, renderTypeBuffer, packedLight, backupPackedLight); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/client/render/tileentity/MiniModelTileEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.client.render.tileentity; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | import com.mojang.blaze3d.systems.RenderSystem; 5 | import io.github.cadiboo.examplemod.client.render.MiniModel; 6 | import io.github.cadiboo.examplemod.config.ExampleModConfig; 7 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.renderer.BufferBuilder; 10 | import net.minecraft.client.renderer.IRenderTypeBuffer; 11 | import net.minecraft.client.renderer.RenderHelper; 12 | import net.minecraft.client.renderer.WorldVertexBufferUploader; 13 | import net.minecraft.client.renderer.tileentity.TileEntityRenderer; 14 | import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; 15 | import org.lwjgl.opengl.GL11; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Handles rendering all MiniModel TileEntities. 21 | * The render method is called once each frame for every visible MiniModel. 22 | *

23 | * Renders a model of the surrounding blocks. 24 | * This should really probably not be in an examplemod for beginners, 25 | * but I added comments to it so its all good 26 | * 27 | * TODO: Update this to 1.15 28 | * 29 | * @author Cadiboo 30 | */ 31 | public class MiniModelTileEntityRenderer extends TileEntityRenderer { 32 | 33 | public MiniModelTileEntityRenderer(final TileEntityRendererDispatcher tileEntityRendererDispatcher) { 34 | super(tileEntityRendererDispatcher); 35 | } 36 | 37 | /** 38 | * Render our TileEntity 39 | */ 40 | @Override 41 | public void render(final MiniModelTileEntity tileEntityIn, final float partialTicks, final MatrixStack matrixStack, final IRenderTypeBuffer renderTypeBuffer, final int packedLight, final int backupPackedLight) { 42 | 43 | // final MiniModel miniModel = tileEntityIn.miniModel; 44 | // 45 | // if (miniModel == null) 46 | // return; 47 | // 48 | // if (!miniModel.isBuilt()) 49 | // miniModel.rebuild(); 50 | // 51 | // // Setup correct GL state 52 | //// this.field_228858_b_.textureManager.bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE); 53 | // RenderHelper.disableStandardItemLighting(); 54 | // // Translucency 55 | // if (ExampleModConfig.modelTranslucency) { 56 | // RenderSystem.blendFunc(GL11.GL_ONE, GL11.GL_ONE); 57 | // } else { 58 | // RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 59 | // } 60 | // RenderSystem.enableBlend(); 61 | // 62 | // if (Minecraft.isAmbientOcclusionEnabled()) { 63 | // RenderSystem.shadeModel(GL11.GL_SMOOTH); 64 | // } else { 65 | // RenderSystem.shadeModel(GL11.GL_FLAT); 66 | // } 67 | // 68 | // GlStateManager.pushMatrix(); 69 | // 70 | // // Translate to render pos. The 0.5 is to translate into the centre of the block, rather than to the corner of it 71 | // GlStateManager.translated(x + 0.5, y + 0.5, z + 0.5); 72 | // 73 | // final double scale = ExampleModConfig.modelScale; 74 | // GlStateManager.scaled(scale, scale, scale); 75 | // 76 | // // Translate to start of render (our TileEntity is at its centre) 77 | // GlStateManager.translated(-8, -8, -8); 78 | // 79 | // // Render the buffers 80 | // renderChunkBuffers(miniModel.regionRenderCacheBuilder, miniModel.generator.getCompiledChunk()); 81 | // 82 | // GlStateManager.popMatrix(); 83 | // 84 | // // Clean up GL state 85 | // RenderHelper.enableStandardItemLighting(); 86 | 87 | } 88 | 89 | /** 90 | * This renderer is a global renderer. 91 | * This means that it will always render, even if the player is not able to see it's block. 92 | * This is useful for rendering larger models or dynamically sized models. 93 | * The Beacon's beam is also a global renderer 94 | */ 95 | @Override 96 | public boolean isGlobalRenderer(final MiniModelTileEntity te) { 97 | return true; 98 | } 99 | 100 | // /** 101 | // * Loops through every non-empty {@link BufferBuilder} in buffers and renders the buffer without resetting it 102 | // * 103 | // * @param buffers The {@link RegionRenderCacheBuilder} to get {@link BufferBuilder}s from 104 | // * @param compiledChunk The {@link CompiledChunk} to use to check if a layer has any rendered blocks 105 | // */ 106 | // private void renderChunkBuffers(final RegionRenderCacheBuilder buffers, final CompiledChunk compiledChunk) { 107 | // final int length = BLOCK_RENDER_LAYERS.length; 108 | // // Render each buffer that has been used 109 | // for (int layerOrdinal = 0; layerOrdinal < length; ++layerOrdinal) { 110 | // if (!compiledChunk.isLayerEmpty(BLOCK_RENDER_LAYERS[layerOrdinal])) { 111 | // drawBufferWithoutResetting(buffers.getBuilder(layerOrdinal)); 112 | // } 113 | // } 114 | // } 115 | // 116 | // /** 117 | // * This should work. 118 | // * Draws a BufferBuilder without resetting its internal data. 119 | // * 120 | // * @param bufferBuilder The BufferBuilder to draw (but not reset) 121 | // */ 122 | // private void drawBufferWithoutResetting(final BufferBuilder bufferBuilder) { 123 | // // Get the internal data from the BufferBuilder (This resets the BufferBuilder's own copy of this data) 124 | // final ByteBuffer byteBuffer = bufferBuilder.getAndResetData().getSecond(); 125 | // // Set the BufferBuilder's internal data to the original data 126 | // bufferBuilder.putBulkData(byteBuffer); 127 | // // Draw the BufferBuilder (This resets the BufferBuilder's data) 128 | // WorldVertexBufferUploader.draw(bufferBuilder); 129 | // // Set the BufferBuilder's internal data back to the original data 130 | // bufferBuilder.putBulkData(byteBuffer); 131 | // } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/config/ClientConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.config; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import net.minecraft.item.DyeColor; 5 | import net.minecraftforge.common.ForgeConfigSpec; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * For configuration settings that change the behaviour of code on the LOGICAL CLIENT. 12 | * This can be moved to an inner class of ExampleModConfig, but is separate because of personal preference and to keep the code organised 13 | * 14 | * @author Cadiboo 15 | */ 16 | final class ClientConfig { 17 | 18 | final ForgeConfigSpec.BooleanValue clientBoolean; 19 | final ForgeConfigSpec.ConfigValue> clientStringList; 20 | final ForgeConfigSpec.EnumValue clientDyeColorEnum; 21 | 22 | final ForgeConfigSpec.BooleanValue modelTranslucency; 23 | final ForgeConfigSpec.DoubleValue modelScale; 24 | 25 | ClientConfig(final ForgeConfigSpec.Builder builder) { 26 | builder.push("general"); 27 | clientBoolean = builder 28 | .comment("An example boolean in the client config") 29 | .translation(ExampleMod.MODID + ".config.clientBoolean") 30 | .define("clientBoolean", true); 31 | clientStringList = builder 32 | .comment("An example list of Strings in the client config") 33 | .translation(ExampleMod.MODID + ".config.clientStringList") 34 | .define("clientStringList", new ArrayList<>()); 35 | clientDyeColorEnum = builder 36 | .comment("An example DyeColor enum in the client config") 37 | .translation(ExampleMod.MODID + ".config.clientDyeColorEnum") 38 | .defineEnum("clientDyeColorEnum", DyeColor.WHITE); 39 | 40 | modelTranslucency = builder 41 | .comment("If the model should be rendered translucent") 42 | .translation(ExampleMod.MODID + ".config.modelTranslucency") 43 | .define("modelTranslucency", true); 44 | modelScale = builder 45 | .comment("The scale to render the model at") 46 | .translation(ExampleMod.MODID + ".config.modelScale") 47 | .defineInRange("modelScale", 0.0625F, 0.0001F, 100F); 48 | builder.pop(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/config/ConfigHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.config; 2 | 3 | import net.minecraftforge.fml.config.ModConfig; 4 | 5 | /** 6 | * This bakes the config values to normal fields 7 | * 8 | * @author Cadiboo 9 | * It can be merged into the main ExampleModConfig class, but is separate because of personal preference and to keep the code organised 10 | */ 11 | public final class ConfigHelper { 12 | 13 | public static void bakeClient(final ModConfig config) { 14 | ExampleModConfig.clientBoolean = ConfigHolder.CLIENT.clientBoolean.get(); 15 | ExampleModConfig.clientStringList = ConfigHolder.CLIENT.clientStringList.get(); 16 | ExampleModConfig.clientDyeColorEnum = ConfigHolder.CLIENT.clientDyeColorEnum.get(); 17 | 18 | ExampleModConfig.modelTranslucency = ConfigHolder.CLIENT.modelTranslucency.get(); 19 | ExampleModConfig.modelScale = ConfigHolder.CLIENT.modelScale.get().floatValue(); 20 | } 21 | 22 | public static void bakeServer(final ModConfig config) { 23 | ExampleModConfig.serverBoolean = ConfigHolder.SERVER.serverBoolean.get(); 24 | ExampleModConfig.serverStringList = ConfigHolder.SERVER.serverStringList.get(); 25 | ExampleModConfig.serverEnumDyeColor = ConfigHolder.SERVER.serverEnumDyeColor.get(); 26 | 27 | ExampleModConfig.electricFurnaceEnergySmeltCostPerTick = ConfigHolder.SERVER.electricFurnaceEnergySmeltCostPerTick.get(); 28 | ExampleModConfig.heatCollectorTransferAmountPerTick = ConfigHolder.SERVER.heatCollectorTransferAmountPerTick.get(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/config/ConfigHolder.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.config; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | import org.apache.commons.lang3.tuple.Pair; 5 | 6 | /** 7 | * This holds the Client & Server Configs and the Client & Server ConfigSpecs. 8 | * It can be merged into the main ExampleModConfig class, but is separate because of personal preference and to keep the code organised 9 | * 10 | * @author Cadiboo 11 | */ 12 | public final class ConfigHolder { 13 | 14 | public static final ForgeConfigSpec CLIENT_SPEC; 15 | public static final ForgeConfigSpec SERVER_SPEC; 16 | static final ClientConfig CLIENT; 17 | static final ServerConfig SERVER; 18 | static { 19 | { 20 | final Pair specPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new); 21 | CLIENT = specPair.getLeft(); 22 | CLIENT_SPEC = specPair.getRight(); 23 | } 24 | { 25 | final Pair specPair = new ForgeConfigSpec.Builder().configure(ServerConfig::new); 26 | SERVER = specPair.getLeft(); 27 | SERVER_SPEC = specPair.getRight(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/config/ExampleModConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.config; 2 | 3 | import net.minecraft.item.DyeColor; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * This holds the baked (runtime) values for our config. 9 | * These values should never be from changed outside this package. 10 | * This can be split into multiple classes (Server, Client, Player, Common) 11 | * but has been kept in one class for simplicity 12 | * 13 | * @author Cadiboo 14 | */ 15 | public final class ExampleModConfig { 16 | 17 | // Client 18 | public static boolean clientBoolean; 19 | public static List clientStringList; 20 | public static DyeColor clientDyeColorEnum; 21 | 22 | public static boolean modelTranslucency; 23 | public static float modelScale; 24 | 25 | // Server 26 | public static boolean serverBoolean; 27 | public static List serverStringList; 28 | public static DyeColor serverEnumDyeColor; 29 | 30 | public static int electricFurnaceEnergySmeltCostPerTick = 100; 31 | public static int heatCollectorTransferAmountPerTick = 100; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/config/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.config; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import net.minecraft.item.DyeColor; 5 | import net.minecraftforge.common.ForgeConfigSpec; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * For configuration settings that change the behaviour of code on the LOGICAL SERVER. 12 | * This can be moved to an inner class of ExampleModConfig, but is separate because of personal preference and to keep the code organised 13 | * 14 | * @author Cadiboo 15 | */ 16 | final class ServerConfig { 17 | 18 | final ForgeConfigSpec.BooleanValue serverBoolean; 19 | final ForgeConfigSpec.ConfigValue> serverStringList; 20 | final ForgeConfigSpec.ConfigValue serverEnumDyeColor; 21 | 22 | final ForgeConfigSpec.IntValue electricFurnaceEnergySmeltCostPerTick; 23 | final ForgeConfigSpec.IntValue heatCollectorTransferAmountPerTick; 24 | 25 | ServerConfig(final ForgeConfigSpec.Builder builder) { 26 | builder.push("general"); 27 | serverBoolean = builder 28 | .comment("An example boolean in the server config") 29 | .translation(ExampleMod.MODID + ".config.serverBoolean") 30 | .define("serverBoolean", true); 31 | serverStringList = builder 32 | .comment("An example list of Strings in the server config") 33 | .translation(ExampleMod.MODID + ".config.serverStringList") 34 | .define("serverStringList", new ArrayList<>()); 35 | serverEnumDyeColor = builder 36 | .comment("An example enum DyeColor in the server config") 37 | .translation(ExampleMod.MODID + ".config.serverEnumDyeColor") 38 | .defineEnum("serverEnumDyeColor", DyeColor.WHITE); 39 | 40 | electricFurnaceEnergySmeltCostPerTick = builder 41 | .comment("How much energy for the Electric Furnace to consume to smelt an item per tick") 42 | .translation(ExampleMod.MODID + ".config.electricFurnaceEnergySmeltCostPerTick") 43 | .defineInRange("electricFurnaceEnergySmeltCostPerTick", 100, 0, Integer.MAX_VALUE); 44 | heatCollectorTransferAmountPerTick = builder 45 | .comment("How much energy for the Heat Collector to try and transfer in each direction per tick") 46 | .translation(ExampleMod.MODID + ".config.heatCollectorTransferAmountPerTick") 47 | .defineInRange("heatCollectorTransferAmountPerTick", 100, 0, Integer.MAX_VALUE); 48 | builder.pop(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/container/ElectricFurnaceContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.container; 2 | 3 | import io.github.cadiboo.examplemod.init.ModBlocks; 4 | import io.github.cadiboo.examplemod.init.ModContainerTypes; 5 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity; 6 | import net.minecraft.client.network.play.ClientPlayNetHandler; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import net.minecraft.entity.player.PlayerInventory; 9 | import net.minecraft.entity.player.ServerPlayerEntity; 10 | import net.minecraft.inventory.container.Container; 11 | import net.minecraft.inventory.container.ContainerType; 12 | import net.minecraft.inventory.container.Slot; 13 | import net.minecraft.item.ItemStack; 14 | import net.minecraft.network.PacketBuffer; 15 | import net.minecraft.network.play.server.SWindowPropertyPacket; 16 | import net.minecraft.tileentity.TileEntity; 17 | import net.minecraft.util.IWorldPosCallable; 18 | import net.minecraft.util.IntReferenceHolder; 19 | import net.minecraftforge.fml.network.IContainerFactory; 20 | import net.minecraftforge.items.SlotItemHandler; 21 | 22 | import javax.annotation.Nonnull; 23 | import java.util.Objects; 24 | 25 | /** 26 | * Smelt time is synced with 27 | * Server: Each tick {@link #detectAndSendChanges()} is called ({@link ServerPlayerEntity#tick()}) 28 | * Server: The (tracked) value of the tile's energy is updated ({@link #updateProgressBar(int, int)}) 29 | * Server: If the value is different from the value last sent to the client ({@link IntReferenceHolder#isDirty()}), 30 | * it is synced to the client ({@link ServerPlayerEntity#sendWindowProperty(Container, int, int)}) 31 | * Client: The sync packet is received ({@link ClientPlayNetHandler#handleWindowProperty(SWindowPropertyPacket)}) 32 | * and the tracked value of is updated ({@link Container#updateProgressBar(int, int)}) 33 | * Client: The tile's data is set to the new value 34 | * 35 | * @author Cadiboo 36 | */ 37 | public class ElectricFurnaceContainer extends Container { 38 | 39 | public final ElectricFurnaceTileEntity tileEntity; 40 | private final IWorldPosCallable canInteractWithCallable; 41 | 42 | /** 43 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)} 44 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer 45 | */ 46 | public ElectricFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) { 47 | this(windowId, playerInventory, getTileEntity(playerInventory, data)); 48 | } 49 | 50 | /** 51 | * Constructor called logical-server-side from {@link ElectricFurnaceTileEntity#createMenu} 52 | * and logical-client-side from {@link #ElectricFurnaceContainer(int, PlayerInventory, PacketBuffer)} 53 | */ 54 | public ElectricFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final ElectricFurnaceTileEntity tileEntity) { 55 | super(ModContainerTypes.ELECTRIC_FURNACE.get(), windowId); 56 | this.tileEntity = tileEntity; 57 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos()); 58 | 59 | // Add tracking for data (Syncs to client/updates value when it changes) 60 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.smeltTimeLeft, v -> tileEntity.smeltTimeLeft = (short) v)); 61 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxSmeltTime, v -> tileEntity.maxSmeltTime = (short) v)); 62 | 63 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container 64 | 65 | // Tile inventory slot(s) 66 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ElectricFurnaceTileEntity.INPUT_SLOT, 56, 35)); 67 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ElectricFurnaceTileEntity.OUTPUT_SLOT, 116, 35)); 68 | 69 | final int playerInventoryStartX = 8; 70 | final int playerInventoryStartY = 84; 71 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18 72 | 73 | // Player Top Inventory slots 74 | for (int row = 0; row < 3; ++row) { 75 | for (int column = 0; column < 9; ++column) { 76 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2))); 77 | } 78 | } 79 | 80 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4; 81 | // Player Hotbar slots 82 | for (int column = 0; column < 9; ++column) { 83 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY)); 84 | } 85 | } 86 | 87 | private static ElectricFurnaceTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) { 88 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!"); 89 | Objects.requireNonNull(data, "data cannot be null!"); 90 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos()); 91 | if (tileAtPos instanceof ElectricFurnaceTileEntity) 92 | return (ElectricFurnaceTileEntity) tileAtPos; 93 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos); 94 | } 95 | 96 | /** 97 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}. 98 | * Handle when the stack in slot {@code index} is shift-clicked. 99 | * Normally this moves the stack between the player inventory and the other inventory(s). 100 | * 101 | * @param player the player passed in 102 | * @param index the index passed in 103 | * @return the {@link ItemStack} 104 | */ 105 | @Nonnull 106 | @Override 107 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) { 108 | ItemStack returnStack = ItemStack.EMPTY; 109 | final Slot slot = this.inventorySlots.get(index); 110 | if (slot != null && slot.getHasStack()) { 111 | final ItemStack slotStack = slot.getStack(); 112 | returnStack = slotStack.copy(); 113 | 114 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size(); 115 | if (index < containerSlots) { 116 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) { 117 | return ItemStack.EMPTY; 118 | } 119 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) { 120 | return ItemStack.EMPTY; 121 | } 122 | if (slotStack.getCount() == 0) { 123 | slot.putStack(ItemStack.EMPTY); 124 | } else { 125 | slot.onSlotChanged(); 126 | } 127 | if (slotStack.getCount() == returnStack.getCount()) { 128 | return ItemStack.EMPTY; 129 | } 130 | slot.onTake(player, slotStack); 131 | } 132 | return returnStack; 133 | } 134 | 135 | @Override 136 | public boolean canInteractWith(@Nonnull final PlayerEntity player) { 137 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.ELECTRIC_FURNACE.get()); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/container/FunctionalIntReferenceHolder.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.container; 2 | 3 | import net.minecraft.util.IntReferenceHolder; 4 | 5 | import java.util.function.IntConsumer; 6 | import java.util.function.IntSupplier; 7 | 8 | /** 9 | * An {@link IntReferenceHolder} that uses {@link IntSupplier}s for its getter and setter 10 | * 11 | * @author Cadiboo 12 | */ 13 | public class FunctionalIntReferenceHolder extends IntReferenceHolder { 14 | 15 | private final IntSupplier getter; 16 | private final IntConsumer setter; 17 | 18 | public FunctionalIntReferenceHolder(final IntSupplier getter, final IntConsumer setter) { 19 | this.getter = getter; 20 | this.setter = setter; 21 | } 22 | 23 | @Override 24 | public int get() { 25 | return this.getter.getAsInt(); 26 | } 27 | 28 | @Override 29 | public void set(final int newValue) { 30 | this.setter.accept(newValue); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/container/HeatCollectorContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.container; 2 | 3 | import io.github.cadiboo.examplemod.init.ModBlocks; 4 | import io.github.cadiboo.examplemod.init.ModContainerTypes; 5 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.entity.player.PlayerInventory; 8 | import net.minecraft.inventory.container.Container; 9 | import net.minecraft.inventory.container.ContainerType; 10 | import net.minecraft.inventory.container.Slot; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.network.PacketBuffer; 13 | import net.minecraft.tileentity.TileEntity; 14 | import net.minecraft.util.IWorldPosCallable; 15 | import net.minecraftforge.fml.network.IContainerFactory; 16 | import net.minecraftforge.items.SlotItemHandler; 17 | 18 | import javax.annotation.Nonnull; 19 | import java.util.Objects; 20 | 21 | /** 22 | * @author Cadiboo 23 | */ 24 | public class HeatCollectorContainer extends Container { 25 | 26 | public final HeatCollectorTileEntity tileEntity; 27 | private final IWorldPosCallable canInteractWithCallable; 28 | 29 | /** 30 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)} 31 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer 32 | */ 33 | public HeatCollectorContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) { 34 | this(windowId, playerInventory, getTileEntity(playerInventory, data)); 35 | } 36 | 37 | /** 38 | * Constructor called logical-server-side from {@link HeatCollectorTileEntity#createMenu} 39 | * and logical-client-side from {@link #HeatCollectorContainer(int, PlayerInventory, PacketBuffer)} 40 | */ 41 | public HeatCollectorContainer(final int windowId, final PlayerInventory playerInventory, final HeatCollectorTileEntity tileEntity) { 42 | super(ModContainerTypes.HEAT_COLLECTOR.get(), windowId); 43 | this.tileEntity = tileEntity; 44 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos()); 45 | 46 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container 47 | 48 | // Tile inventory slot(s) 49 | this.addSlot(new SlotItemHandler(tileEntity.inventory, HeatCollectorTileEntity.FUEL_SLOT, 80, 35)); 50 | 51 | final int playerInventoryStartX = 8; 52 | final int playerInventoryStartY = 84; 53 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18 54 | 55 | // Player Top Inventory slots 56 | for (int row = 0; row < 3; ++row) { 57 | for (int column = 0; column < 9; ++column) { 58 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2))); 59 | } 60 | } 61 | 62 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4; 63 | // Player Hotbar slots 64 | for (int column = 0; column < 9; ++column) { 65 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY)); 66 | } 67 | } 68 | 69 | private static HeatCollectorTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) { 70 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!"); 71 | Objects.requireNonNull(data, "data cannot be null!"); 72 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos()); 73 | if (tileAtPos instanceof HeatCollectorTileEntity) 74 | return (HeatCollectorTileEntity) tileAtPos; 75 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos); 76 | } 77 | 78 | /** 79 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}. 80 | * Handle when the stack in slot {@code index} is shift-clicked. 81 | * Normally this moves the stack between the player inventory and the other inventory(s). 82 | * 83 | * @param player the player passed in 84 | * @param index the index passed in 85 | * @return the {@link ItemStack} 86 | */ 87 | @Nonnull 88 | @Override 89 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) { 90 | ItemStack returnStack = ItemStack.EMPTY; 91 | final Slot slot = this.inventorySlots.get(index); 92 | if (slot != null && slot.getHasStack()) { 93 | final ItemStack slotStack = slot.getStack(); 94 | returnStack = slotStack.copy(); 95 | 96 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size(); 97 | if (index < containerSlots) { 98 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) { 99 | return ItemStack.EMPTY; 100 | } 101 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) { 102 | return ItemStack.EMPTY; 103 | } 104 | if (slotStack.getCount() == 0) { 105 | slot.putStack(ItemStack.EMPTY); 106 | } else { 107 | slot.onSlotChanged(); 108 | } 109 | if (slotStack.getCount() == returnStack.getCount()) { 110 | return ItemStack.EMPTY; 111 | } 112 | slot.onTake(player, slotStack); 113 | } 114 | return returnStack; 115 | } 116 | 117 | @Override 118 | public boolean canInteractWith(@Nonnull final PlayerEntity player) { 119 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.HEAT_COLLECTOR.get()); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/container/ModFurnaceContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.container; 2 | 3 | import io.github.cadiboo.examplemod.init.ModBlocks; 4 | import io.github.cadiboo.examplemod.init.ModContainerTypes; 5 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity; 6 | import net.minecraft.client.network.play.ClientPlayNetHandler; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import net.minecraft.entity.player.PlayerInventory; 9 | import net.minecraft.entity.player.ServerPlayerEntity; 10 | import net.minecraft.inventory.container.Container; 11 | import net.minecraft.inventory.container.ContainerType; 12 | import net.minecraft.inventory.container.Slot; 13 | import net.minecraft.item.ItemStack; 14 | import net.minecraft.network.PacketBuffer; 15 | import net.minecraft.network.play.server.SWindowPropertyPacket; 16 | import net.minecraft.tileentity.TileEntity; 17 | import net.minecraft.util.IWorldPosCallable; 18 | import net.minecraft.util.IntReferenceHolder; 19 | import net.minecraftforge.fml.network.IContainerFactory; 20 | import net.minecraftforge.items.SlotItemHandler; 21 | 22 | import javax.annotation.Nonnull; 23 | import java.util.Objects; 24 | 25 | /** 26 | * Smelt time is synced with 27 | * Server: Each tick {@link #detectAndSendChanges()} is called ({@link ServerPlayerEntity#tick()}) 28 | * Server: The (tracked) value of the tile's energy is updated ({@link #updateProgressBar(int, int)}) 29 | * Server: If the value is different from the value last sent to the client ({@link IntReferenceHolder#isDirty()}), 30 | * it is synced to the client ({@link ServerPlayerEntity#sendWindowProperty(Container, int, int)}) 31 | * Client: The sync packet is received ({@link ClientPlayNetHandler#handleWindowProperty(SWindowPropertyPacket)}) 32 | * and the tracked value of is updated ({@link Container#updateProgressBar(int, int)}) 33 | * Client: The tile's data is set to the new value 34 | * 35 | * @author Cadiboo 36 | */ 37 | public class ModFurnaceContainer extends Container { 38 | 39 | public final ModFurnaceTileEntity tileEntity; 40 | private final IWorldPosCallable canInteractWithCallable; 41 | 42 | /** 43 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)} 44 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer 45 | */ 46 | public ModFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) { 47 | this(windowId, playerInventory, getTileEntity(playerInventory, data)); 48 | } 49 | 50 | /** 51 | * Constructor called logical-server-side from {@link ModFurnaceTileEntity#createMenu} 52 | * and logical-client-side from {@link #ModFurnaceContainer(int, PlayerInventory, PacketBuffer)} 53 | */ 54 | public ModFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final ModFurnaceTileEntity tileEntity) { 55 | super(ModContainerTypes.MOD_FURNACE.get(), windowId); 56 | this.tileEntity = tileEntity; 57 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos()); 58 | 59 | // Add tracking for data (Syncs to client/updates value when it changes) 60 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.smeltTimeLeft, v -> tileEntity.smeltTimeLeft = (short) v)); 61 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxSmeltTime, v -> tileEntity.maxSmeltTime = (short) v)); 62 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.fuelBurnTimeLeft, v -> tileEntity.fuelBurnTimeLeft = (short) v)); 63 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxFuelBurnTime, v -> tileEntity.maxFuelBurnTime = (short) v)); 64 | 65 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container 66 | 67 | // Tile inventory slot(s) 68 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.FUEL_SLOT, 56, 53)); 69 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.INPUT_SLOT, 56, 17)); 70 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.OUTPUT_SLOT, 116, 35)); 71 | 72 | final int playerInventoryStartX = 8; 73 | final int playerInventoryStartY = 84; 74 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18 75 | 76 | // Player Top Inventory slots 77 | for (int row = 0; row < 3; ++row) { 78 | for (int column = 0; column < 9; ++column) { 79 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2))); 80 | } 81 | } 82 | 83 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4; 84 | // Player Hotbar slots 85 | for (int column = 0; column < 9; ++column) { 86 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY)); 87 | } 88 | } 89 | 90 | private static ModFurnaceTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) { 91 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!"); 92 | Objects.requireNonNull(data, "data cannot be null!"); 93 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos()); 94 | if (tileAtPos instanceof ModFurnaceTileEntity) 95 | return (ModFurnaceTileEntity) tileAtPos; 96 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos); 97 | } 98 | 99 | /** 100 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}. 101 | * Handle when the stack in slot {@code index} is shift-clicked. 102 | * Normally this moves the stack between the player inventory and the other inventory(s). 103 | * 104 | * @param player the player passed in 105 | * @param index the index passed in 106 | * @return the {@link ItemStack} 107 | */ 108 | @Nonnull 109 | @Override 110 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) { 111 | ItemStack returnStack = ItemStack.EMPTY; 112 | final Slot slot = this.inventorySlots.get(index); 113 | if (slot != null && slot.getHasStack()) { 114 | final ItemStack slotStack = slot.getStack(); 115 | returnStack = slotStack.copy(); 116 | 117 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size(); 118 | if (index < containerSlots) { 119 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) { 120 | return ItemStack.EMPTY; 121 | } 122 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) { 123 | return ItemStack.EMPTY; 124 | } 125 | if (slotStack.getCount() == 0) { 126 | slot.putStack(ItemStack.EMPTY); 127 | } else { 128 | slot.onSlotChanged(); 129 | } 130 | if (slotStack.getCount() == returnStack.getCount()) { 131 | return ItemStack.EMPTY; 132 | } 133 | slot.onTake(player, slotStack); 134 | } 135 | return returnStack; 136 | } 137 | 138 | @Override 139 | public boolean canInteractWith(@Nonnull final PlayerEntity player) { 140 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.MOD_FURNACE.get()); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/energy/SettableEnergyStorage.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.energy; 2 | 3 | import net.minecraftforge.energy.EnergyStorage; 4 | 5 | /** 6 | * @author Cadiboo 7 | */ 8 | public class SettableEnergyStorage extends EnergyStorage { 9 | 10 | public SettableEnergyStorage(final int capacity) { 11 | super(capacity); 12 | } 13 | 14 | public SettableEnergyStorage(final int capacity, final int maxTransfer) { 15 | super(capacity, maxTransfer); 16 | } 17 | 18 | public SettableEnergyStorage(final int capacity, final int maxReceive, final int maxExtract) { 19 | super(capacity, maxReceive, maxExtract); 20 | } 21 | 22 | public SettableEnergyStorage(final int capacity, final int maxReceive, final int maxExtract, final int energy) { 23 | super(capacity, maxReceive, maxExtract, energy); 24 | } 25 | 26 | /** 27 | * @return The amount of energy that was put into the storage 28 | */ 29 | public int setEnergy(final int maxSet) { 30 | return this.energy = Math.min(this.capacity, maxSet); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/entity/WildBoarEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.entity; 2 | 3 | import net.minecraft.entity.AgeableEntity; 4 | import net.minecraft.entity.EntityType; 5 | import net.minecraft.entity.SharedMonsterAttributes; 6 | import net.minecraft.entity.passive.PigEntity; 7 | import net.minecraft.entity.passive.horse.AbstractHorseEntity; 8 | import net.minecraft.network.IPacket; 9 | import net.minecraft.world.World; 10 | import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; 11 | import net.minecraftforge.fml.network.FMLPlayMessages; 12 | import net.minecraftforge.fml.network.NetworkHooks; 13 | 14 | /** 15 | * A Wild Boar entity. 16 | * Literally just a pig (with a different texture). 17 | * TODO: Will have more stuff added to it soon. (Will charge at player 18 | * 19 | * @author Cadiboo 20 | */ 21 | public class WildBoarEntity extends PigEntity { 22 | 23 | public WildBoarEntity(final EntityType entityType, final World world) { 24 | super(entityType, world); 25 | } 26 | 27 | @Override 28 | protected void registerAttributes() { 29 | super.registerAttributes(); 30 | 31 | final double baseSpeed = this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getBaseValue(); 32 | final double baseHealth = this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).getBaseValue(); 33 | // Multiply base health and base speed by one and a half 34 | this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(baseSpeed * 1.5D); 35 | this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(baseHealth * 1.5D); 36 | } 37 | 38 | /** 39 | * Creates a child new entity from the parent entity. 40 | * Can be used to set additional on the child entity based on the parent. 41 | * 42 | * @param parent The entity that made this child 43 | * @return A new WildBoar 44 | * @see AbstractHorseEntity#setOffspringAttributes(AgeableEntity, AbstractHorseEntity) 45 | */ 46 | @Override 47 | public WildBoarEntity createChild(final AgeableEntity parent) { 48 | // Use getType to support overrides in subclasses 49 | return (WildBoarEntity) getType().create(this.world); 50 | } 51 | 52 | /** 53 | * Called on the logical server to get a packet to send to the client containing data necessary to spawn your entity. 54 | * Using Forge's method instead of the default vanilla one allows extra stuff to work such as sending extra data, 55 | * using a non-default entity factory and having {@link IEntityAdditionalSpawnData} work. 56 | * 57 | * It is not actually necessary for our WildBoarEntity to use Forge's method as it doesn't need any of this extra 58 | * functionality, however, this is an example mod and many modders are unaware that Forge's method exists. 59 | * 60 | * @return The packet with data about your entity 61 | * @see FMLPlayMessages.SpawnEntity 62 | */ 63 | @Override 64 | public IPacket createSpawnPacket() { 65 | return NetworkHooks.getEntitySpawningPacket(this); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModBlocks.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock; 5 | import io.github.cadiboo.examplemod.block.HeatCollectorBlock; 6 | import io.github.cadiboo.examplemod.block.MiniModelBlock; 7 | import io.github.cadiboo.examplemod.block.ModFurnaceBlock; 8 | import net.minecraft.block.Block; 9 | import net.minecraft.block.SoundType; 10 | import net.minecraft.block.material.Material; 11 | import net.minecraft.block.material.MaterialColor; 12 | import net.minecraftforge.fml.RegistryObject; 13 | import net.minecraftforge.registries.DeferredRegister; 14 | import net.minecraftforge.registries.ForgeRegistries; 15 | 16 | /** 17 | * Holds a list of all our {@link Block}s. 18 | * Suppliers that create Blocks are added to the DeferredRegister. 19 | * The DeferredRegister is then added to our mod event bus in our constructor. 20 | * When the Block Registry Event is fired by Forge and it is time for the mod to 21 | * register its Blocks, our Blocks are created and registered by the DeferredRegister. 22 | * The Block Registry Event will always be called before the Item registry is filled. 23 | * Note: This supports registry overrides. 24 | * 25 | * @author Cadiboo 26 | */ 27 | public final class ModBlocks { 28 | 29 | public static final DeferredRegister BLOCKS = new DeferredRegister<>(ForgeRegistries.BLOCKS, ExampleMod.MODID); 30 | 31 | // This block has the ROCK material, meaning it needs at least a wooden pickaxe to break it. It is very similar to Iron Ore 32 | public static final RegistryObject EXAMPLE_ORE = BLOCKS.register("example_ore", () -> new Block(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.0F, 3.0F))); 33 | // This block has the IRON material, meaning it needs at least a stone pickaxe to break it. It is very similar to the Iron Block 34 | public static final RegistryObject EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(5.0F, 6.0F).sound(SoundType.METAL))); 35 | // This block has the MISCELLANEOUS material. It is very similar to the Redstone Torch 36 | public static final RegistryObject MINI_MODEL = BLOCKS.register("mini_model", () -> new MiniModelBlock(Block.Properties.create(Material.MISCELLANEOUS).hardnessAndResistance(1.5F).lightValue(13).doesNotBlockMovement())); 37 | // This block has the ROCK material, meaning it needs at least a wooden pickaxe to break it. It is very similar to the Furnace 38 | public static final RegistryObject HEAT_COLLECTOR = BLOCKS.register("heat_collector", () -> new HeatCollectorBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F).lightValue(13))); 39 | public static final RegistryObject ELECTRIC_FURNACE = BLOCKS.register("electric_furnace", () -> new ElectricFurnaceBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F))); 40 | public static final RegistryObject MOD_FURNACE = BLOCKS.register("mod_furnace", () -> new ModFurnaceBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F).lightValue(13))); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModContainerTypes.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer; 5 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer; 6 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer; 7 | import net.minecraft.inventory.container.ContainerType; 8 | import net.minecraftforge.common.extensions.IForgeContainerType; 9 | import net.minecraftforge.fml.RegistryObject; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | /** 14 | * Holds a list of all our {@link ContainerType}s. 15 | * Suppliers that create ContainerTypes are added to the DeferredRegister. 16 | * The DeferredRegister is then added to our mod event bus in our constructor. 17 | * When the ContainerType Registry Event is fired by Forge and it is time for the mod to 18 | * register its ContainerTypes, our ContainerTypes are created and registered by the DeferredRegister. 19 | * The ContainerType Registry Event will always be called after the Block and Item registries are filled. 20 | * Note: This supports registry overrides. 21 | * 22 | * @author Cadiboo 23 | */ 24 | public final class ModContainerTypes { 25 | 26 | public static final DeferredRegister> CONTAINER_TYPES = new DeferredRegister<>(ForgeRegistries.CONTAINERS, ExampleMod.MODID); 27 | 28 | public static final RegistryObject> HEAT_COLLECTOR = CONTAINER_TYPES.register("heat_collector", () -> IForgeContainerType.create(HeatCollectorContainer::new)); 29 | public static final RegistryObject> ELECTRIC_FURNACE = CONTAINER_TYPES.register("electric_furnace", () -> IForgeContainerType.create(ElectricFurnaceContainer::new)); 30 | public static final RegistryObject> MOD_FURNACE = CONTAINER_TYPES.register("mod_furnace", () -> IForgeContainerType.create(ModFurnaceContainer::new)); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModEntityTypes.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.entity.WildBoarEntity; 5 | import net.minecraft.entity.EntityClassification; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.util.ResourceLocation; 8 | import net.minecraftforge.fml.RegistryObject; 9 | import net.minecraftforge.registries.DeferredRegister; 10 | import net.minecraftforge.registries.ForgeRegistries; 11 | 12 | /** 13 | * Holds a list of all our {@link EntityType}s. 14 | * Suppliers that create EntityTypes are added to the DeferredRegister. 15 | * The DeferredRegister is then added to our mod event bus in our constructor. 16 | * When the EntityType Registry Event is fired by Forge and it is time for the mod to 17 | * register its EntityTypes, our EntityTypes are created and registered by the DeferredRegister. 18 | * The EntityType Registry Event will always be called after the Block and Item registries are filled. 19 | * Note: This supports registry overrides. 20 | * 21 | * @author Cadiboo 22 | */ 23 | public final class ModEntityTypes { 24 | 25 | public static final DeferredRegister> ENTITY_TYPES = new DeferredRegister<>(ForgeRegistries.ENTITIES, ExampleMod.MODID); 26 | 27 | public static final String WILD_BOAR_NAME = "wild_boar"; 28 | public static final RegistryObject> WILD_BOAR = ENTITY_TYPES.register(WILD_BOAR_NAME, () -> 29 | EntityType.Builder.create(WildBoarEntity::new, EntityClassification.CREATURE) 30 | .size(EntityType.PIG.getWidth(), EntityType.PIG.getHeight()) 31 | .build(new ResourceLocation(ExampleMod.MODID, WILD_BOAR_NAME).toString()) 32 | ); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModItemGroups.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import net.minecraft.item.ItemGroup; 5 | import net.minecraft.item.ItemStack; 6 | 7 | import javax.annotation.Nonnull; 8 | import java.util.function.Supplier; 9 | 10 | /** 11 | * This class holds all our ItemGroups (Formerly called CreativeTabs). 12 | * Static initialisers are fine here. 13 | * 14 | * @author Cadiboo 15 | */ 16 | public final class ModItemGroups { 17 | 18 | public static final ItemGroup MOD_ITEM_GROUP = new ModItemGroup(ExampleMod.MODID, () -> new ItemStack(ModItems.EXAMPLE_CRYSTAL.get())); 19 | 20 | public static final class ModItemGroup extends ItemGroup { 21 | 22 | @Nonnull 23 | private final Supplier iconSupplier; 24 | 25 | public ModItemGroup(@Nonnull final String name, @Nonnull final Supplier iconSupplier) { 26 | super(name); 27 | this.iconSupplier = iconSupplier; 28 | } 29 | 30 | @Override 31 | @Nonnull 32 | public ItemStack createIcon() { 33 | return iconSupplier.get(); 34 | } 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModItems.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.item.ModdedSpawnEggItem; 5 | import net.minecraft.item.Item; 6 | import net.minecraftforge.fml.RegistryObject; 7 | import net.minecraftforge.registries.DeferredRegister; 8 | import net.minecraftforge.registries.ForgeRegistries; 9 | 10 | /** 11 | * Holds a list of all our {@link Item}s. 12 | * Suppliers that create Items are added to the DeferredRegister. 13 | * The DeferredRegister is then added to our mod event bus in our constructor. 14 | * When the Item Registry Event is fired by Forge and it is time for the mod to 15 | * register its Items, our Items are created and registered by the DeferredRegister. 16 | * The Item Registry Event will always be called after the Block registry is filled. 17 | * Note: This supports registry overrides. 18 | * 19 | * @author Cadiboo 20 | */ 21 | public final class ModItems { 22 | 23 | public static final DeferredRegister ITEMS = new DeferredRegister<>(ForgeRegistries.ITEMS, ExampleMod.MODID); 24 | 25 | // This is a very simple Item. It has no special properties except for being on our creative tab. 26 | public static final RegistryObject EXAMPLE_CRYSTAL = ITEMS.register("example_crystal", () -> new Item(new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP))); 27 | public static final RegistryObject WILD_BOAR_SPAWN_EGG = ITEMS.register("wild_boar_spawn_egg", () -> new ModdedSpawnEggItem(ModEntityTypes.WILD_BOAR, 0xF0A5A2, 0xA9672B, new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP))); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/init/ModTileEntityTypes.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.init; 2 | 3 | import io.github.cadiboo.examplemod.ExampleMod; 4 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity; 5 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity; 6 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity; 7 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity; 8 | import net.minecraft.tileentity.TileEntityType; 9 | import net.minecraftforge.fml.RegistryObject; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | /** 14 | * Holds a list of all our {@link TileEntityType}s. 15 | * Suppliers that create TileEntityTypes are added to the DeferredRegister. 16 | * The DeferredRegister is then added to our mod event bus in our constructor. 17 | * When the TileEntityType Registry Event is fired by Forge and it is time for the mod to 18 | * register its TileEntityTypes, our TileEntityTypes are created and registered by the DeferredRegister. 19 | * The TileEntityType Registry Event will always be called after the Block and Item registries are filled. 20 | * Note: This supports registry overrides. 21 | * 22 | * @author Cadiboo 23 | */ 24 | public final class ModTileEntityTypes { 25 | 26 | public static final DeferredRegister> TILE_ENTITY_TYPES = new DeferredRegister<>(ForgeRegistries.TILE_ENTITIES, ExampleMod.MODID); 27 | 28 | // We don't have a datafixer for our TileEntities, so we pass null into build. 29 | public static final RegistryObject> MINI_MODEL = TILE_ENTITY_TYPES.register("mini_model", () -> 30 | TileEntityType.Builder.create(MiniModelTileEntity::new, ModBlocks.MINI_MODEL.get()) 31 | .build(null) 32 | ); 33 | public static final RegistryObject> HEAT_COLLECTOR = TILE_ENTITY_TYPES.register("heat_collector", () -> 34 | TileEntityType.Builder.create(HeatCollectorTileEntity::new, ModBlocks.HEAT_COLLECTOR.get()) 35 | .build(null) 36 | ); 37 | public static final RegistryObject> ELECTRIC_FURNACE = TILE_ENTITY_TYPES.register("electric_furnace", () -> 38 | TileEntityType.Builder.create(ElectricFurnaceTileEntity::new, ModBlocks.ELECTRIC_FURNACE.get()) 39 | .build(null) 40 | ); 41 | public static final RegistryObject> MOD_FURNACE = TILE_ENTITY_TYPES.register("mod_furnace", () -> 42 | TileEntityType.Builder.create(ModFurnaceTileEntity::new, ModBlocks.MOD_FURNACE.get()) 43 | .build(null) 44 | ); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/item/ModdedSpawnEggItem.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.item; 2 | 3 | import net.minecraft.block.DispenserBlock; 4 | import net.minecraft.dispenser.DefaultDispenseItemBehavior; 5 | import net.minecraft.dispenser.IBlockSource; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.SpawnReason; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.item.SpawnEggItem; 10 | import net.minecraft.nbt.CompoundNBT; 11 | import net.minecraft.util.Direction; 12 | import net.minecraftforge.common.util.Lazy; 13 | import net.minecraftforge.common.util.NonNullSupplier; 14 | import net.minecraftforge.fml.RegistryObject; 15 | import net.minecraftforge.fml.common.ObfuscationReflectionHelper; 16 | 17 | import javax.annotation.Nullable; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * Exists to work around a limitation with Spawn Eggs: 24 | * Spawn Eggs require an EntityType, but EntityTypes are created AFTER Items. 25 | * Therefore it is "impossible" for modded spawn eggs to exist. 26 | * This class gets around it by passing "null" to the SpawnEggItem constructor 27 | * and doing the initialisation after registry events have finished firing. 28 | *

29 | * TODO: Remove once Forge adds this stuff in itself. 30 | * 31 | * @author Cadiboo 32 | */ 33 | public class ModdedSpawnEggItem extends SpawnEggItem { 34 | 35 | protected static final List UNADDED_EGGS = new ArrayList<>(); 36 | private final Lazy> entityTypeSupplier; 37 | 38 | public ModdedSpawnEggItem(final NonNullSupplier> entityTypeSupplier, final int p_i48465_2_, final int p_i48465_3_, final Properties p_i48465_4_) { 39 | super(null, p_i48465_2_, p_i48465_3_, p_i48465_4_); 40 | this.entityTypeSupplier = Lazy.of(entityTypeSupplier::get); 41 | UNADDED_EGGS.add(this); 42 | } 43 | 44 | public ModdedSpawnEggItem(final RegistryObject> entityTypeSupplier, final int p_i48465_2_, final int p_i48465_3_, final Properties p_i48465_4_) { 45 | super(null, p_i48465_2_, p_i48465_3_, p_i48465_4_); 46 | this.entityTypeSupplier = Lazy.of(entityTypeSupplier); 47 | UNADDED_EGGS.add(this); 48 | } 49 | 50 | /** 51 | * Adds all the supplier based spawn eggs to vanilla's map and registers an 52 | * IDispenseItemBehavior for each of them as normal spawn eggs have one 53 | * registered for each of them during {@link net.minecraft.dispenser.IDispenseItemBehavior#init()} 54 | * but supplier based ones won't have had their EntityTypes created yet. 55 | */ 56 | public static void initUnaddedEggs() { 57 | final Map, SpawnEggItem> EGGS = ObfuscationReflectionHelper.getPrivateValue(SpawnEggItem.class, null, "field_195987_b"); 58 | DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior() { 59 | public ItemStack dispenseStack(IBlockSource source, ItemStack stack) { 60 | Direction direction = source.getBlockState().get(DispenserBlock.FACING); 61 | EntityType entitytype = ((SpawnEggItem) stack.getItem()).getType(stack.getTag()); 62 | entitytype.spawn(source.getWorld(), stack, null, source.getBlockPos().offset(direction), SpawnReason.DISPENSER, direction != Direction.UP, false); 63 | stack.shrink(1); 64 | return stack; 65 | } 66 | }; 67 | for (final SpawnEggItem egg : UNADDED_EGGS) { 68 | EGGS.put(egg.getType(null), egg); 69 | DispenserBlock.registerDispenseBehavior(egg, defaultDispenseItemBehavior); 70 | // ItemColors for each spawn egg don't need to be registered because this method is called before ItemColors is created 71 | } 72 | UNADDED_EGGS.clear(); 73 | } 74 | 75 | @Override 76 | public EntityType getType(@Nullable final CompoundNBT p_208076_1_) { 77 | return entityTypeSupplier.get(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/tileentity/ElectricFurnaceTileEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.tileentity; 2 | 3 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock; 4 | import io.github.cadiboo.examplemod.config.ExampleModConfig; 5 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer; 6 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage; 7 | import io.github.cadiboo.examplemod.init.ModBlocks; 8 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 9 | import net.minecraft.block.BlockState; 10 | import net.minecraft.entity.player.PlayerEntity; 11 | import net.minecraft.entity.player.PlayerInventory; 12 | import net.minecraft.inventory.IInventory; 13 | import net.minecraft.inventory.Inventory; 14 | import net.minecraft.inventory.container.Container; 15 | import net.minecraft.inventory.container.INamedContainerProvider; 16 | import net.minecraft.item.ItemStack; 17 | import net.minecraft.item.crafting.AbstractCookingRecipe; 18 | import net.minecraft.item.crafting.FurnaceRecipe; 19 | import net.minecraft.item.crafting.IRecipeType; 20 | import net.minecraft.nbt.CompoundNBT; 21 | import net.minecraft.network.NetworkManager; 22 | import net.minecraft.network.play.server.SUpdateTileEntityPacket; 23 | import net.minecraft.tileentity.AbstractFurnaceTileEntity; 24 | import net.minecraft.tileentity.ITickableTileEntity; 25 | import net.minecraft.tileentity.TileEntity; 26 | import net.minecraft.util.Direction; 27 | import net.minecraft.util.text.ITextComponent; 28 | import net.minecraft.util.text.TranslationTextComponent; 29 | import net.minecraftforge.common.capabilities.Capability; 30 | import net.minecraftforge.common.util.LazyOptional; 31 | import net.minecraftforge.energy.CapabilityEnergy; 32 | import net.minecraftforge.energy.EnergyStorage; 33 | import net.minecraftforge.fml.network.NetworkHooks; 34 | import net.minecraftforge.items.CapabilityItemHandler; 35 | import net.minecraftforge.items.IItemHandlerModifiable; 36 | import net.minecraftforge.items.ItemStackHandler; 37 | import net.minecraftforge.items.wrapper.RangedWrapper; 38 | 39 | import javax.annotation.Nonnull; 40 | import javax.annotation.Nullable; 41 | import java.util.Optional; 42 | 43 | /** 44 | * @author Cadiboo 45 | */ 46 | public class ElectricFurnaceTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider { 47 | 48 | public static final int INPUT_SLOT = 0; 49 | public static final int OUTPUT_SLOT = 1; 50 | 51 | private static final String INVENTORY_TAG = "inventory"; 52 | private static final String SMELT_TIME_LEFT_TAG = "smeltTimeLeft"; 53 | private static final String MAX_SMELT_TIME_TAG = "maxSmeltTime"; 54 | private static final String ENERGY_TAG = "energy"; 55 | public final ItemStackHandler inventory = new ItemStackHandler(2) { 56 | @Override 57 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) { 58 | switch (slot) { 59 | case INPUT_SLOT: 60 | return isInput(stack); 61 | case OUTPUT_SLOT: 62 | return isOutput(stack); 63 | default: 64 | return false; 65 | } 66 | } 67 | 68 | @Override 69 | protected void onContentsChanged(final int slot) { 70 | super.onContentsChanged(slot); 71 | // Mark the tile entity as having changed whenever its inventory changes. 72 | // "markDirty" tells vanilla that the chunk containing the tile entity has 73 | // changed and means the game will save the chunk to disk later. 74 | ElectricFurnaceTileEntity.this.markDirty(); 75 | } 76 | }; 77 | public final SettableEnergyStorage energy = new SettableEnergyStorage(100_000); 78 | 79 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum 80 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory); 81 | // Machines (hoppers, pipes) connected to this furnace's top can only insert/extract items from the input slot 82 | private final LazyOptional inventoryCapabilityExternalUpAndSides = LazyOptional.of(() -> new RangedWrapper(this.inventory, INPUT_SLOT, INPUT_SLOT + 1)); 83 | // Machines (hoppers, pipes) connected to this furnace's bottom can only insert/extract items from the output slot 84 | private final LazyOptional inventoryCapabilityExternalDown = LazyOptional.of(() -> new RangedWrapper(this.inventory, OUTPUT_SLOT, OUTPUT_SLOT + 1)); 85 | private final LazyOptional energyCapabilityExternal = LazyOptional.of(() -> this.energy); 86 | 87 | public short smeltTimeLeft = -1; 88 | public short maxSmeltTime = -1; 89 | private int lastEnergy = -1; 90 | 91 | public ElectricFurnaceTileEntity() { 92 | super(ModTileEntityTypes.ELECTRIC_FURNACE.get()); 93 | } 94 | 95 | /** 96 | * @return If the stack is not empty and has a smelting recipe associated with it 97 | */ 98 | private boolean isInput(final ItemStack stack) { 99 | if (stack.isEmpty()) 100 | return false; 101 | return getRecipe(stack).isPresent(); 102 | } 103 | 104 | /** 105 | * @return If the stack's item is equal to the result of smelting our input 106 | */ 107 | private boolean isOutput(final ItemStack stack) { 108 | final Optional result = getResult(inventory.getStackInSlot(INPUT_SLOT)); 109 | return result.isPresent() && ItemStack.areItemsEqual(result.get(), stack); 110 | } 111 | 112 | /** 113 | * @return The smelting recipe for the input stack 114 | */ 115 | private Optional getRecipe(final ItemStack input) { 116 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe so we make one here. 117 | return getRecipe(new Inventory(input)); 118 | } 119 | 120 | /** 121 | * @return The smelting recipe for the inventory 122 | */ 123 | private Optional getRecipe(final IInventory inventory) { 124 | return world.getRecipeManager().getRecipe(IRecipeType.SMELTING, inventory, world); 125 | } 126 | 127 | /** 128 | * @return The result of smelting the input stack 129 | */ 130 | private Optional getResult(final ItemStack input) { 131 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe and 132 | // AbstractCookingRecipe#getCraftingResult() so we make one here. 133 | final Inventory dummyInventory = new Inventory(input); 134 | return getRecipe(dummyInventory).map(recipe -> recipe.getCraftingResult(dummyInventory)); 135 | } 136 | 137 | /** 138 | * Called every tick to update our tile entity 139 | */ 140 | @Override 141 | public void tick() { 142 | if (world == null || world.isRemote) 143 | return; 144 | 145 | // Energy will get pushed into our machine by generators/wires 146 | 147 | // Smelting code 148 | 149 | final ItemStack input = inventory.getStackInSlot(INPUT_SLOT).copy(); 150 | final ItemStack result = getResult(input).orElse(ItemStack.EMPTY); 151 | 152 | if (!result.isEmpty() && isInput(input)) { 153 | final boolean canInsertResultIntoOutput = inventory.insertItem(OUTPUT_SLOT, result, true).isEmpty(); 154 | if (canInsertResultIntoOutput) { 155 | // Energy consuming code 156 | final int energySmeltCostPerTick = ExampleModConfig.electricFurnaceEnergySmeltCostPerTick; 157 | boolean hasEnergy = false; 158 | if (energy.extractEnergy(energySmeltCostPerTick, true) == energySmeltCostPerTick) { 159 | hasEnergy = true; 160 | energy.extractEnergy(energySmeltCostPerTick, false); 161 | } 162 | if (hasEnergy) { 163 | if (smeltTimeLeft == -1) { // Item has not been smelted before 164 | smeltTimeLeft = maxSmeltTime = getSmeltTime(input); 165 | } else { // Item was already being smelted 166 | --smeltTimeLeft; 167 | if (smeltTimeLeft == 0) { 168 | inventory.insertItem(OUTPUT_SLOT, result, false); 169 | if (input.hasContainerItem()) 170 | inventory.setStackInSlot(INPUT_SLOT, input.getContainerItem()); 171 | else { 172 | input.shrink(1); 173 | inventory.setStackInSlot(INPUT_SLOT, input); // Update the data 174 | } 175 | smeltTimeLeft = -1; // Set to -1 so we smelt the next stack on the next tick 176 | } 177 | } 178 | } else // No energy -> add to smelt time left to simulate cooling 179 | if (smeltTimeLeft < maxSmeltTime) 180 | ++smeltTimeLeft; 181 | } 182 | } else // We have an invalid input stack (somehow) 183 | smeltTimeLeft = maxSmeltTime = -1; 184 | 185 | // If the energy has changed. 186 | if (lastEnergy != energy.getEnergyStored()) { 187 | 188 | // "markDirty" tells vanilla that the chunk containing the tile entity has 189 | // changed and means the game will save the chunk to disk later. 190 | this.markDirty(); 191 | 192 | // Notify clients of a block update. 193 | // This will result in the packet from getUpdatePacket being sent to the client 194 | // and our energy being synced. 195 | final BlockState blockState = this.getBlockState(); 196 | // Flag 2: Send the change to clients 197 | world.notifyBlockUpdate(pos, blockState, blockState, 2); 198 | 199 | // Update the last synced energy to the current energy 200 | lastEnergy = energy.getEnergyStored(); 201 | } 202 | 203 | } 204 | 205 | /** 206 | * Mimics the code in {@link AbstractFurnaceTileEntity#func_214005_h()} 207 | * 208 | * @return The custom smelt time or 200 if there is no recipe for the input 209 | */ 210 | private short getSmeltTime(final ItemStack input) { 211 | return getRecipe(input) 212 | .map(AbstractCookingRecipe::getCookTime) 213 | .orElse(200) 214 | .shortValue(); 215 | } 216 | 217 | /** 218 | * Retrieves the Optional handler for the capability requested on the specific side. 219 | * 220 | * @param cap The capability to check 221 | * @param side The Direction to check from. CAN BE NULL! Null is defined to represent 'internal' or 'self' 222 | * @return The requested an optional holding the requested capability. 223 | */ 224 | @Nonnull 225 | @Override 226 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) { 227 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { 228 | if (side == null) 229 | return inventoryCapabilityExternal.cast(); 230 | switch (side) { 231 | case DOWN: 232 | return inventoryCapabilityExternalDown.cast(); 233 | case UP: 234 | case NORTH: 235 | case SOUTH: 236 | case WEST: 237 | case EAST: 238 | return inventoryCapabilityExternalUpAndSides.cast(); 239 | } 240 | } 241 | if (cap == CapabilityEnergy.ENERGY) 242 | return energyCapabilityExternal.cast(); 243 | return super.getCapability(cap, side); 244 | } 245 | 246 | /** 247 | * Handle a packet created in {@link #getUpdatePacket()} 248 | */ 249 | @Override 250 | public void onDataPacket(final NetworkManager net, final SUpdateTileEntityPacket pkt) { 251 | this.energy.setEnergy(pkt.getNbtCompound().getInt(ENERGY_TAG)); 252 | } 253 | 254 | @Override 255 | public void onLoad() { 256 | super.onLoad(); 257 | // We set this in onLoad instead of the constructor so that TileEntities 258 | // constructed from NBT (saved tile entities) have this set to the proper value 259 | if (world != null && !world.isRemote) 260 | lastEnergy = energy.getEnergyStored(); 261 | } 262 | 263 | /** 264 | * Read saved data from disk into the tile. 265 | */ 266 | @Override 267 | public void read(final CompoundNBT compound) { 268 | super.read(compound); 269 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG)); 270 | this.smeltTimeLeft = compound.getShort(SMELT_TIME_LEFT_TAG); 271 | this.maxSmeltTime = compound.getShort(MAX_SMELT_TIME_TAG); 272 | this.energy.setEnergy(compound.getInt(ENERGY_TAG)); 273 | } 274 | 275 | /** 276 | * Write data from the tile into a compound tag for saving to disk. 277 | */ 278 | @Nonnull 279 | @Override 280 | public CompoundNBT write(final CompoundNBT compound) { 281 | super.write(compound); 282 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT()); 283 | compound.putShort(SMELT_TIME_LEFT_TAG, this.smeltTimeLeft); 284 | compound.putShort(MAX_SMELT_TIME_TAG, this.maxSmeltTime); 285 | compound.putInt(ENERGY_TAG, this.energy.getEnergyStored()); 286 | return compound; 287 | } 288 | 289 | /** 290 | * Retrieves packet to send to the client whenever this Tile Entity is re-synced via World#notifyBlockUpdate. 291 | * This packet comes back client-side in {@link #onDataPacket} 292 | */ 293 | @Nullable 294 | public SUpdateTileEntityPacket getUpdatePacket() { 295 | final CompoundNBT tag = new CompoundNBT(); 296 | tag.putInt(ENERGY_TAG, this.energy.getEnergyStored()); 297 | // We pass 0 for tileEntityTypeIn because we have a modded TE. See ClientPlayNetHandler#handleUpdateTileEntity(SUpdateTileEntityPacket) 298 | return new SUpdateTileEntityPacket(this.pos, 0, tag); 299 | } 300 | 301 | /** 302 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the 303 | * chunk or when many blocks change at once. 304 | * This compound comes back to you client-side in {@link #handleUpdateTag} 305 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)} 306 | * which doesn't save any of our extra data so we override it to call {@link #write} instead 307 | */ 308 | @Nonnull 309 | public CompoundNBT getUpdateTag() { 310 | return this.write(new CompoundNBT()); 311 | } 312 | 313 | /** 314 | * Invalidates our tile entity 315 | */ 316 | @Override 317 | public void remove() { 318 | super.remove(); 319 | // We need to invalidate our capability references so that any cached references (by other mods) don't 320 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected 321 | inventoryCapabilityExternal.invalidate(); 322 | energyCapabilityExternal.invalidate(); 323 | } 324 | 325 | @Nonnull 326 | @Override 327 | public ITextComponent getDisplayName() { 328 | return new TranslationTextComponent(ModBlocks.ELECTRIC_FURNACE.get().getTranslationKey()); 329 | } 330 | 331 | /** 332 | * Called from {@link NetworkHooks#openGui} 333 | * (which is called from {@link ElectricFurnaceBlock#onBlockActivated} on the logical server) 334 | * 335 | * @return The logical-server-side Container for this TileEntity 336 | */ 337 | @Nonnull 338 | @Override 339 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) { 340 | return new ElectricFurnaceContainer(windowId, inventory, this); 341 | } 342 | 343 | } 344 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/tileentity/HeatCollectorTileEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.tileentity; 2 | 3 | import io.github.cadiboo.examplemod.ModUtil; 4 | import io.github.cadiboo.examplemod.block.HeatCollectorBlock; 5 | import io.github.cadiboo.examplemod.config.ExampleModConfig; 6 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer; 7 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage; 8 | import io.github.cadiboo.examplemod.init.ModBlocks; 9 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 10 | import net.minecraft.block.BlockState; 11 | import net.minecraft.block.Blocks; 12 | import net.minecraft.entity.player.PlayerEntity; 13 | import net.minecraft.entity.player.PlayerInventory; 14 | import net.minecraft.fluid.IFluidState; 15 | import net.minecraft.inventory.container.Container; 16 | import net.minecraft.inventory.container.INamedContainerProvider; 17 | import net.minecraft.item.ItemStack; 18 | import net.minecraft.nbt.CompoundNBT; 19 | import net.minecraft.network.NetworkManager; 20 | import net.minecraft.network.play.server.SUpdateTileEntityPacket; 21 | import net.minecraft.tags.FluidTags; 22 | import net.minecraft.tileentity.FurnaceTileEntity; 23 | import net.minecraft.tileentity.ITickableTileEntity; 24 | import net.minecraft.tileentity.TileEntity; 25 | import net.minecraft.util.Direction; 26 | import net.minecraft.util.math.BlockPos; 27 | import net.minecraft.util.text.ITextComponent; 28 | import net.minecraft.util.text.TranslationTextComponent; 29 | import net.minecraft.world.World; 30 | import net.minecraftforge.common.ForgeHooks; 31 | import net.minecraftforge.common.capabilities.Capability; 32 | import net.minecraftforge.common.util.LazyOptional; 33 | import net.minecraftforge.energy.CapabilityEnergy; 34 | import net.minecraftforge.energy.EnergyStorage; 35 | import net.minecraftforge.fml.network.NetworkHooks; 36 | import net.minecraftforge.items.CapabilityItemHandler; 37 | import net.minecraftforge.items.ItemStackHandler; 38 | 39 | import javax.annotation.Nonnull; 40 | import javax.annotation.Nullable; 41 | 42 | /** 43 | * @author Cadiboo 44 | */ 45 | public class HeatCollectorTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider { 46 | 47 | public static final int FUEL_SLOT = 0; 48 | 49 | private static final String INVENTORY_TAG = "inventory"; 50 | private static final String ENERGY_TAG = "energy"; 51 | 52 | public final ItemStackHandler inventory = new ItemStackHandler(1) { 53 | @Override 54 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) { 55 | return FurnaceTileEntity.isFuel(stack); 56 | } 57 | 58 | @Override 59 | protected void onContentsChanged(final int slot) { 60 | super.onContentsChanged(slot); 61 | // Mark the tile entity as having changed whenever its inventory changes. 62 | // "markDirty" tells vanilla that the chunk containing the tile entity has 63 | // changed and means the game will save the chunk to disk later. 64 | HeatCollectorTileEntity.this.markDirty(); 65 | } 66 | }; 67 | public final SettableEnergyStorage energy = new SettableEnergyStorage(100_000); 68 | 69 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum 70 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory); 71 | private final LazyOptional energyCapabilityExternal = LazyOptional.of(() -> this.energy); 72 | private int lastEnergy = -1; 73 | 74 | public HeatCollectorTileEntity() { 75 | super(ModTileEntityTypes.HEAT_COLLECTOR.get()); 76 | } 77 | 78 | @Override 79 | public void tick() { 80 | 81 | final World world = this.world; 82 | if (world == null || world.isRemote) 83 | return; 84 | 85 | final BlockPos pos = this.pos; 86 | final SettableEnergyStorage energy = this.energy; 87 | 88 | final ItemStack fuelStack = this.inventory.getStackInSlot(FUEL_SLOT).copy(); 89 | if (!fuelStack.isEmpty()) { 90 | int energyToReceive = ForgeHooks.getBurnTime(fuelStack); 91 | // Only use the stack if we can receive 100% of the energy from it 92 | if (energy.receiveEnergy(energyToReceive, true) == energyToReceive) { 93 | energy.receiveEnergy(energyToReceive, false); 94 | if (fuelStack.hasContainerItem()) 95 | inventory.setStackInSlot(FUEL_SLOT, fuelStack.getContainerItem()); 96 | else { 97 | fuelStack.shrink(1); 98 | inventory.setStackInSlot(FUEL_SLOT, fuelStack); // Update the data 99 | } 100 | } 101 | } 102 | 103 | // Collect from all heat blocks in a 5 block radius 104 | for (final BlockPos blockPos : BlockPos.getAllInBoxMutable(pos.add(-5, -5, -5), pos.add(5, 5, 5))) { 105 | final BlockState blockState = world.getBlockState(blockPos); 106 | final IFluidState fluidState = world.getFluidState(blockPos); 107 | 108 | int fireEnergy = 0; 109 | 110 | if (blockState.getBlock() == Blocks.FIRE) 111 | fireEnergy += 20; 112 | else if (blockState.getBlock() == Blocks.CAMPFIRE) 113 | fireEnergy += 10; 114 | 115 | if (fluidState.isTagged(FluidTags.LAVA)) 116 | fireEnergy += 50; 117 | 118 | if (fireEnergy > 0) 119 | energy.receiveEnergy(fireEnergy, false); 120 | 121 | } 122 | 123 | final int transferAmountPerTick = ExampleModConfig.heatCollectorTransferAmountPerTick; 124 | // Skip trying to transfer if there isn't enough energy to transfer 125 | if (energy.getEnergyStored() >= transferAmountPerTick) { 126 | for (Direction direction : ModUtil.DIRECTIONS) { 127 | final TileEntity te = world.getTileEntity(pos.offset(direction)); 128 | if (te == null) { 129 | continue; 130 | } 131 | te.getCapability(CapabilityEnergy.ENERGY, direction).ifPresent(otherTileEnergy -> { 132 | if (!otherTileEnergy.canReceive()) { 133 | // Optimisation: Skip this tile if it can't receive 134 | return; 135 | } 136 | energy.extractEnergy( 137 | otherTileEnergy.receiveEnergy( 138 | energy.extractEnergy(100, true), 139 | false 140 | ), 141 | false 142 | ); 143 | }); 144 | } 145 | } 146 | 147 | // If the energy has changed. 148 | if (lastEnergy != energy.getEnergyStored()) { 149 | 150 | // "markDirty" tells vanilla that the chunk containing the tile entity has 151 | // changed and means the game will save the chunk to disk later. 152 | this.markDirty(); 153 | 154 | // Notify clients of a block update. 155 | // This will result in the packet from getUpdatePacket being sent to the client 156 | // and our energy being synced. 157 | final BlockState blockState = this.getBlockState(); 158 | // Flag 2: Send the change to clients 159 | world.notifyBlockUpdate(pos, blockState, blockState, 2); 160 | 161 | // Update the last synced energy to the current energy 162 | lastEnergy = energy.getEnergyStored(); 163 | } 164 | 165 | } 166 | 167 | @Nonnull 168 | @Override 169 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) { 170 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) 171 | return inventoryCapabilityExternal.cast(); 172 | if (cap == CapabilityEnergy.ENERGY) 173 | return energyCapabilityExternal.cast(); 174 | return super.getCapability(cap, side); 175 | } 176 | 177 | /** 178 | * Handle a packet created in {@link #getUpdatePacket()} 179 | */ 180 | @Override 181 | public void onDataPacket(final NetworkManager net, final SUpdateTileEntityPacket pkt) { 182 | this.energy.setEnergy(pkt.getNbtCompound().getInt(ENERGY_TAG)); 183 | } 184 | 185 | @Override 186 | public void onLoad() { 187 | super.onLoad(); 188 | // We set this in onLoad instead of the constructor so that TileEntities 189 | // constructed from NBT (saved tile entities) have this set to the proper value 190 | if (world != null && !world.isRemote) 191 | lastEnergy = energy.getEnergyStored(); 192 | } 193 | 194 | /** 195 | * Read saved data from disk into the tile. 196 | */ 197 | @Override 198 | public void read(final CompoundNBT compound) { 199 | super.read(compound); 200 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG)); 201 | this.energy.setEnergy(compound.getInt(ENERGY_TAG)); 202 | } 203 | 204 | /** 205 | * Write data from the tile into a compound tag for saving to disk. 206 | */ 207 | @Nonnull 208 | @Override 209 | public CompoundNBT write(final CompoundNBT compound) { 210 | super.write(compound); 211 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT()); 212 | compound.putInt(ENERGY_TAG, this.energy.getEnergyStored()); 213 | return compound; 214 | } 215 | 216 | /** 217 | * Retrieves packet to send to the client whenever this Tile Entity is re-synced via World#notifyBlockUpdate. 218 | * This packet comes back client-side in {@link #onDataPacket} 219 | */ 220 | @Nullable 221 | public SUpdateTileEntityPacket getUpdatePacket() { 222 | final CompoundNBT tag = new CompoundNBT(); 223 | tag.putInt(ENERGY_TAG, this.energy.getEnergyStored()); 224 | return new SUpdateTileEntityPacket(this.pos, 0, tag); 225 | } 226 | 227 | /** 228 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the 229 | * chunk or when many blocks change at once. 230 | * This compound comes back to you client-side in {@link #handleUpdateTag} 231 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)} 232 | * which doesn't save any of our extra data so we override it to call {@link #write} instead 233 | */ 234 | @Nonnull 235 | public CompoundNBT getUpdateTag() { 236 | return this.write(new CompoundNBT()); 237 | } 238 | 239 | /** 240 | * Invalidates our tile entity 241 | */ 242 | @Override 243 | public void remove() { 244 | super.remove(); 245 | // We need to invalidate our capability references so that any cached references (by other mods) don't 246 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected 247 | inventoryCapabilityExternal.invalidate(); 248 | energyCapabilityExternal.invalidate(); 249 | } 250 | 251 | @Nonnull 252 | @Override 253 | public ITextComponent getDisplayName() { 254 | return new TranslationTextComponent(ModBlocks.HEAT_COLLECTOR.get().getTranslationKey()); 255 | } 256 | 257 | /** 258 | * Called from {@link NetworkHooks#openGui} 259 | * (which is called from {@link HeatCollectorBlock#onBlockActivated} on the logical server) 260 | * 261 | * @return The logical-server-side Container for this TileEntity 262 | */ 263 | @Nonnull 264 | @Override 265 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) { 266 | return new HeatCollectorContainer(windowId, inventory, this); 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/tileentity/MiniModelTileEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.tileentity; 2 | 3 | import io.github.cadiboo.examplemod.client.render.MiniModel; 4 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 5 | import net.minecraft.tileentity.TileEntity; 6 | import net.minecraft.tileentity.TileEntityType; 7 | import net.minecraft.util.math.AxisAlignedBB; 8 | import net.minecraft.world.World; 9 | import net.minecraftforge.api.distmarker.Dist; 10 | import net.minecraftforge.api.distmarker.OnlyIn; 11 | 12 | import javax.annotation.Nullable; 13 | 14 | /** 15 | * This is the TileEntity for our MiniModel. 16 | * It stores the render data for its surroundings. 17 | *

18 | * Rendering code only exists on the client distribution so @OnlyIn is used a lot. 19 | * The @OnlyIn annotation makes it so that a class/field/method annotated with it does not 20 | * exist on the other distribution. This is important as TileEntities exist on both distributions 21 | * but rendering only happens on the client. 22 | * As a general rule you should avoid using @OnlyIn unless it is absolutely necessary. 23 | * 24 | * @author Cadiboo 25 | */ 26 | public class MiniModelTileEntity extends TileEntity { 27 | 28 | @Nullable // May be accessed before onLoad 29 | // @OnlyIn(Dist.CLIENT) Makes it so this field will be removed from the class on the PHYSICAL SERVER 30 | // This is because we only want the MiniModel on the physical client - its rendering only. 31 | @OnlyIn(Dist.CLIENT) 32 | public MiniModel miniModel; 33 | 34 | // Constructor without hardcoded TileEntityType so that subclasses can use their own. 35 | // Alternatively, subclasses can also override getType if a hardcoded type is used in a superclass' constructor 36 | public MiniModelTileEntity(final TileEntityType type) { 37 | super(type); 38 | } 39 | 40 | public MiniModelTileEntity() { 41 | this(ModTileEntityTypes.MINI_MODEL.get()); 42 | } 43 | 44 | // @OnlyIn(Dist.CLIENT) Makes it so this method will be removed from the class on the PHYSICAL SERVER 45 | // This is because we only want the MiniModel on the physical client - its rendering only. 46 | @OnlyIn(Dist.CLIENT) 47 | @Override 48 | public void onLoad() { 49 | super.onLoad(); 50 | World world = getWorld(); 51 | if (world == null || !world.isRemote) 52 | return; // Return if the world is null or if we are on the logical server 53 | miniModel = MiniModel.forTileEntity(this); 54 | } 55 | 56 | @Override 57 | public AxisAlignedBB getRenderBoundingBox() { 58 | // This, combined with isGlobalRenderer in the TileEntityRenderer makes it so that the 59 | // render does not disappear if the player can't see the block 60 | // This is useful for rendering larger models or dynamically sized models 61 | return INFINITE_EXTENT_AABB; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/github/cadiboo/examplemod/tileentity/ModFurnaceTileEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.cadiboo.examplemod.tileentity; 2 | 3 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock; 4 | import io.github.cadiboo.examplemod.block.ModFurnaceBlock; 5 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer; 6 | import io.github.cadiboo.examplemod.init.ModBlocks; 7 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes; 8 | import net.minecraft.block.BlockState; 9 | import net.minecraft.entity.player.PlayerEntity; 10 | import net.minecraft.entity.player.PlayerInventory; 11 | import net.minecraft.inventory.IInventory; 12 | import net.minecraft.inventory.Inventory; 13 | import net.minecraft.inventory.container.Container; 14 | import net.minecraft.inventory.container.INamedContainerProvider; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.item.crafting.AbstractCookingRecipe; 17 | import net.minecraft.item.crafting.FurnaceRecipe; 18 | import net.minecraft.item.crafting.IRecipeType; 19 | import net.minecraft.nbt.CompoundNBT; 20 | import net.minecraft.tileentity.AbstractFurnaceTileEntity; 21 | import net.minecraft.tileentity.FurnaceTileEntity; 22 | import net.minecraft.tileentity.ITickableTileEntity; 23 | import net.minecraft.tileentity.TileEntity; 24 | import net.minecraft.util.Direction; 25 | import net.minecraft.util.text.ITextComponent; 26 | import net.minecraft.util.text.TranslationTextComponent; 27 | import net.minecraftforge.common.ForgeHooks; 28 | import net.minecraftforge.common.capabilities.Capability; 29 | import net.minecraftforge.common.util.LazyOptional; 30 | import net.minecraftforge.fml.network.NetworkHooks; 31 | import net.minecraftforge.items.CapabilityItemHandler; 32 | import net.minecraftforge.items.IItemHandlerModifiable; 33 | import net.minecraftforge.items.ItemStackHandler; 34 | import net.minecraftforge.items.wrapper.RangedWrapper; 35 | 36 | import javax.annotation.Nonnull; 37 | import javax.annotation.Nullable; 38 | import java.util.Optional; 39 | 40 | /** 41 | * @author Cadiboo 42 | */ 43 | public class ModFurnaceTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider { 44 | 45 | public static final int FUEL_SLOT = 0; 46 | public static final int INPUT_SLOT = 1; 47 | public static final int OUTPUT_SLOT = 2; 48 | 49 | private static final String INVENTORY_TAG = "inventory"; 50 | private static final String SMELT_TIME_LEFT_TAG = "smeltTimeLeft"; 51 | private static final String MAX_SMELT_TIME_TAG = "maxSmeltTime"; 52 | private static final String FUEL_BURN_TIME_LEFT_TAG = "fuelBurnTimeLeft"; 53 | private static final String MAX_FUEL_BURN_TIME_TAG = "maxFuelBurnTime"; 54 | 55 | public final ItemStackHandler inventory = new ItemStackHandler(3) { 56 | @Override 57 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) { 58 | switch (slot) { 59 | case FUEL_SLOT: 60 | return FurnaceTileEntity.isFuel(stack); 61 | case INPUT_SLOT: 62 | return isInput(stack); 63 | case OUTPUT_SLOT: 64 | return isOutput(stack); 65 | default: 66 | return false; 67 | } 68 | } 69 | 70 | @Override 71 | protected void onContentsChanged(final int slot) { 72 | super.onContentsChanged(slot); 73 | // Mark the tile entity as having changed whenever its inventory changes. 74 | // "markDirty" tells vanilla that the chunk containing the tile entity has 75 | // changed and means the game will save the chunk to disk later. 76 | ModFurnaceTileEntity.this.markDirty(); 77 | } 78 | }; 79 | 80 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum 81 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory); 82 | // Machines (hoppers, pipes) connected to this furnace's top can only insert/extract items from the input slot 83 | private final LazyOptional inventoryCapabilityExternalUp = LazyOptional.of(() -> new RangedWrapper(this.inventory, INPUT_SLOT, INPUT_SLOT + 1)); 84 | // Machines (hoppers, pipes) connected to this furnace's bottom can only insert/extract items from the output slot 85 | private final LazyOptional inventoryCapabilityExternalDown = LazyOptional.of(() -> new RangedWrapper(this.inventory, OUTPUT_SLOT, OUTPUT_SLOT + 1)); 86 | // Machines (hoppers, pipes) connected to this furnace's side can only insert/extract items from the fuel and input slots 87 | private final LazyOptional inventoryCapabilityExternalSides = LazyOptional.of(() -> new RangedWrapper(this.inventory, FUEL_SLOT, INPUT_SLOT + 1)); 88 | 89 | public short smeltTimeLeft = -1; 90 | public short maxSmeltTime = -1; 91 | public short fuelBurnTimeLeft = -1; 92 | public short maxFuelBurnTime = -1; 93 | private boolean lastBurning = false; 94 | 95 | public ModFurnaceTileEntity() { 96 | super(ModTileEntityTypes.MOD_FURNACE.get()); 97 | } 98 | 99 | /** 100 | * @return If the stack is not empty and has a smelting recipe associated with it 101 | */ 102 | private boolean isInput(final ItemStack stack) { 103 | if (stack.isEmpty()) 104 | return false; 105 | return getRecipe(stack).isPresent(); 106 | } 107 | 108 | /** 109 | * @return If the stack's item is equal to the result of smelting our input 110 | */ 111 | private boolean isOutput(final ItemStack stack) { 112 | final Optional result = getResult(inventory.getStackInSlot(INPUT_SLOT)); 113 | return result.isPresent() && ItemStack.areItemsEqual(result.get(), stack); 114 | } 115 | 116 | /** 117 | * @return The smelting recipe for the input stack 118 | */ 119 | private Optional getRecipe(final ItemStack input) { 120 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe so we make one here. 121 | return getRecipe(new Inventory(input)); 122 | } 123 | 124 | /** 125 | * @return The smelting recipe for the inventory 126 | */ 127 | private Optional getRecipe(final IInventory inventory) { 128 | return world.getRecipeManager().getRecipe(IRecipeType.SMELTING, inventory, world); 129 | } 130 | 131 | /** 132 | * @return The result of smelting the input stack 133 | */ 134 | private Optional getResult(final ItemStack input) { 135 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe and 136 | // AbstractCookingRecipe#getCraftingResult() so we make one here. 137 | final Inventory dummyInventory = new Inventory(input); 138 | return getRecipe(dummyInventory).map(recipe -> recipe.getCraftingResult(dummyInventory)); 139 | } 140 | 141 | /** 142 | * Called every tick to update our tile entity 143 | */ 144 | @Override 145 | public void tick() { 146 | if (world == null || world.isRemote) 147 | return; 148 | 149 | // Fuel burning code 150 | 151 | boolean hasFuel = false; 152 | if (isBurning()) { 153 | hasFuel = true; 154 | --fuelBurnTimeLeft; 155 | } 156 | 157 | // Smelting code 158 | 159 | final ItemStack input = inventory.getStackInSlot(INPUT_SLOT).copy(); 160 | final ItemStack result = getResult(input).orElse(ItemStack.EMPTY); 161 | 162 | if (!result.isEmpty() && isInput(input)) { 163 | final boolean canInsertResultIntoOutput = inventory.insertItem(OUTPUT_SLOT, result, true).isEmpty(); 164 | if (canInsertResultIntoOutput) { 165 | if (!hasFuel) 166 | if (burnFuel()) 167 | hasFuel = true; 168 | if (hasFuel) { 169 | if (smeltTimeLeft == -1) { // Item has not been smelted before 170 | smeltTimeLeft = maxSmeltTime = getSmeltTime(input); 171 | } else { // Item was already being smelted 172 | --smeltTimeLeft; 173 | if (smeltTimeLeft == 0) { 174 | inventory.insertItem(OUTPUT_SLOT, result, false); 175 | if (input.hasContainerItem()) 176 | inventory.setStackInSlot(INPUT_SLOT, input.getContainerItem()); 177 | else { 178 | input.shrink(1); 179 | inventory.setStackInSlot(INPUT_SLOT, input); // Update the data 180 | } 181 | smeltTimeLeft = -1; // Set to -1 so we smelt the next stack on the next tick 182 | } 183 | } 184 | } else // No fuel -> add to smelt time left to simulate cooling 185 | if (smeltTimeLeft < maxSmeltTime) 186 | ++smeltTimeLeft; 187 | } 188 | } else // We have an invalid input stack (somehow) 189 | smeltTimeLeft = maxSmeltTime = -1; 190 | 191 | // Syncing code 192 | 193 | // If the burning state has changed. 194 | if (lastBurning != hasFuel) { // We use hasFuel because the current fuel may be all burnt out but we have more that will be used next tick 195 | 196 | // "markDirty" tells vanilla that the chunk containing the tile entity has 197 | // changed and means the game will save the chunk to disk later. 198 | this.markDirty(); 199 | 200 | final BlockState newState = this.getBlockState() 201 | .with(ModFurnaceBlock.BURNING, hasFuel); 202 | 203 | // Flag 2: Send the change to clients 204 | world.setBlockState(pos, newState, 2); 205 | 206 | // Update the last synced burning state to the current burning state 207 | lastBurning = hasFuel; 208 | } 209 | 210 | } 211 | 212 | /** 213 | * Mimics the code in {@link AbstractFurnaceTileEntity#func_214005_h()} 214 | * 215 | * @return The custom smelt time or 200 if there is no recipe for the input 216 | */ 217 | private short getSmeltTime(final ItemStack input) { 218 | return getRecipe(input) 219 | .map(AbstractCookingRecipe::getCookTime) 220 | .orElse(200) 221 | .shortValue(); 222 | } 223 | 224 | /** 225 | * @return If the fuel was burnt 226 | */ 227 | private boolean burnFuel() { 228 | final ItemStack fuelStack = inventory.getStackInSlot(FUEL_SLOT).copy(); 229 | if (!fuelStack.isEmpty()) { 230 | final int burnTime = ForgeHooks.getBurnTime(fuelStack); 231 | if (burnTime > 0) { 232 | fuelBurnTimeLeft = maxFuelBurnTime = ((short) burnTime); 233 | if (fuelStack.hasContainerItem()) 234 | inventory.setStackInSlot(FUEL_SLOT, fuelStack.getContainerItem()); 235 | else { 236 | fuelStack.shrink(1); 237 | inventory.setStackInSlot(FUEL_SLOT, fuelStack); // Update the data 238 | } 239 | return true; 240 | } 241 | } 242 | fuelBurnTimeLeft = maxFuelBurnTime = -1; 243 | return false; 244 | } 245 | 246 | public boolean isBurning() { 247 | return this.fuelBurnTimeLeft > 0; 248 | } 249 | 250 | /** 251 | * Retrieves the Optional handler for the capability requested on the specific side. 252 | * 253 | * @param cap The capability to check 254 | * @param side The Direction to check from. CAN BE NULL! Null is defined to represent 'internal' or 'self' 255 | * @return The requested an optional holding the requested capability. 256 | */ 257 | @Nonnull 258 | @Override 259 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) { 260 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { 261 | if (side == null) 262 | return inventoryCapabilityExternal.cast(); 263 | switch (side) { 264 | case DOWN: 265 | return inventoryCapabilityExternalDown.cast(); 266 | case UP: 267 | return inventoryCapabilityExternalUp.cast(); 268 | case NORTH: 269 | case SOUTH: 270 | case WEST: 271 | case EAST: 272 | return inventoryCapabilityExternalSides.cast(); 273 | } 274 | } 275 | return super.getCapability(cap, side); 276 | } 277 | 278 | @Override 279 | public void onLoad() { 280 | super.onLoad(); 281 | // We set this in onLoad instead of the constructor so that TileEntities 282 | // constructed from NBT (saved tile entities) have this set to the proper value 283 | if (world != null && !world.isRemote) 284 | lastBurning = isBurning(); 285 | } 286 | 287 | /** 288 | * Read saved data from disk into the tile. 289 | */ 290 | @Override 291 | public void read(final CompoundNBT compound) { 292 | super.read(compound); 293 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG)); 294 | this.smeltTimeLeft = compound.getShort(SMELT_TIME_LEFT_TAG); 295 | this.maxSmeltTime = compound.getShort(MAX_SMELT_TIME_TAG); 296 | this.fuelBurnTimeLeft = compound.getShort(FUEL_BURN_TIME_LEFT_TAG); 297 | this.maxFuelBurnTime = compound.getShort(MAX_FUEL_BURN_TIME_TAG); 298 | } 299 | 300 | /** 301 | * Write data from the tile into a compound tag for saving to disk. 302 | */ 303 | @Nonnull 304 | @Override 305 | public CompoundNBT write(final CompoundNBT compound) { 306 | super.write(compound); 307 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT()); 308 | compound.putShort(SMELT_TIME_LEFT_TAG, this.smeltTimeLeft); 309 | compound.putShort(MAX_SMELT_TIME_TAG, this.maxSmeltTime); 310 | compound.putShort(FUEL_BURN_TIME_LEFT_TAG, this.fuelBurnTimeLeft); 311 | compound.putShort(MAX_FUEL_BURN_TIME_TAG, this.maxFuelBurnTime); 312 | return compound; 313 | } 314 | 315 | /** 316 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the 317 | * chunk or when many blocks change at once. 318 | * This compound comes back to you client-side in {@link #handleUpdateTag} 319 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)} 320 | * which doesn't save any of our extra data so we override it to call {@link #write} instead 321 | */ 322 | @Nonnull 323 | public CompoundNBT getUpdateTag() { 324 | return this.write(new CompoundNBT()); 325 | } 326 | 327 | /** 328 | * Invalidates our tile entity 329 | */ 330 | @Override 331 | public void remove() { 332 | super.remove(); 333 | // We need to invalidate our capability references so that any cached references (by other mods) don't 334 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected 335 | inventoryCapabilityExternal.invalidate(); 336 | } 337 | 338 | @Nonnull 339 | @Override 340 | public ITextComponent getDisplayName() { 341 | return new TranslationTextComponent(ModBlocks.MOD_FURNACE.get().getTranslationKey()); 342 | } 343 | 344 | /** 345 | * Called from {@link NetworkHooks#openGui} 346 | * (which is called from {@link ElectricFurnaceBlock#onBlockActivated} on the logical server) 347 | * 348 | * @return The logical-server-side Container for this TileEntity 349 | */ 350 | @Nonnull 351 | @Override 352 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) { 353 | return new ModFurnaceContainer(windowId, inventory, this); 354 | } 355 | 356 | } 357 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | # This is an example mods.toml file. It contains the data relating to the loading mods. 2 | # There are several mandatory fields (#mandatory), and many more that are optional (#optional). 3 | # The overall format is standard TOML format, v0.5.0. 4 | # Note that there are a couple of TOML lists in this file. 5 | # Find more information on toml format here: https://github.com/toml-lang/toml 6 | 7 | # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml 8 | modLoader="javafml" #mandatory 9 | 10 | # A version range to match for said mod loader - for regular FML @Mod it will be the forge version 11 | # See "https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html" 12 | # 24 is 1.13-pre, 25 is 1.13.2, 26 is 1.14.2, 27 is 1.14.3, 28 is 1.14.4, 29 is 1.15, 30 is 1.15.1, 31 is 1.15.2 13 | loaderVersion="[31,)" #mandatory 14 | 15 | # A URL to refer people to when problems occur with this mod 16 | issueTrackerURL="http://github.com/YourName/Your-Mod-Name/issues" #optional 17 | 18 | # A list of mods - how many allowed here is determined by the individual mod loader 19 | [[mods]] #mandatory 20 | # The modid of the mod 21 | modId="examplemod" #mandatory 22 | # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it 23 | version="${version}" #mandatory 24 | # A display name for the mod 25 | displayName="Example Mod" #mandatory 26 | # A URL to query for updates for this mod. See the JSON update specification 27 | updateJSONURL="http://github.com/YourName/Your-Mod-Name/update.json" #optional 28 | # A URL for the "homepage" for this mod, displayed in the mod UI 29 | displayURL="http://github.com/YourName/Your-Mod-Name" #optional 30 | # A file name (in the root of the mod JAR) containing a logo for display 31 | logoFile="examplemod.png" #optional 32 | # A text field displayed in the mod UI 33 | credits="Thanks for this example mod goes to Java. The logo for this mod was taken from a post by DavidM on the Forge Forums" #optional 34 | # A text field displayed in the mod UI 35 | authors="Love, Cheese and small house plants" #optional 36 | # The description text for the mod (multi line!) (#mandatory) 37 | description=''' 38 | This is a long form description of the mod. You can write whatever you want here 39 | 40 | Have some lorem ipsum. 41 | 42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 43 | ''' 44 | 45 | # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. 46 | [[dependencies.examplemod]] #optional 47 | # The modid of the dependency 48 | modId="forge" #mandatory 49 | # Does this dependency have to exist - if not, ordering below must be specified 50 | mandatory=true #mandatory 51 | # The version range of the dependency (see "https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html") 52 | versionRange="[31,)" #mandatory 53 | # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory 54 | ordering="NONE" 55 | # Side this dependency is applied on - BOTH, CLIENT or SERVER 56 | side="BOTH" 57 | 58 | # Here's another dependency 59 | [[dependencies.examplemod]] 60 | modId="minecraft" 61 | mandatory=true 62 | versionRange="[1.15.2]" 63 | ordering="NONE" 64 | side="BOTH" 65 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/electric_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=north": { "model": "examplemod:block/electric_furnace" }, 4 | "facing=south": { "model": "examplemod:block/electric_furnace", "y": 180 }, 5 | "facing=west": { "model": "examplemod:block/electric_furnace", "y": 270 }, 6 | "facing=east": { "model": "examplemod:block/electric_furnace", "y": 90 } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/example_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "examplemod:block/example_block" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/example_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "examplemod:block/example_ore" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/heat_collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "examplemod:block/heat_collector" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/mini_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "examplemod:block/mini_model" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/blockstates/mod_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=north,burning=false": { "model": "examplemod:block/mod_furnace" }, 4 | "facing=south,burning=false": { "model": "examplemod:block/mod_furnace", "y": 180 }, 5 | "facing=west,burning=false": { "model": "examplemod:block/mod_furnace", "y": 270 }, 6 | "facing=east,burning=false": { "model": "examplemod:block/mod_furnace", "y": 90 }, 7 | "facing=north,burning=true": { "model": "examplemod:block/mod_furnace_burning" }, 8 | "facing=south,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 180 }, 9 | "facing=west,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 270 }, 10 | "facing=east,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 90 } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.examplemod.example_ore": "Example Ore", 3 | "block.examplemod.example_block": "Example Block", 4 | "block.examplemod.mini_model": "Mini Model", 5 | "block.examplemod.heat_collector": "Heat Collector", 6 | "block.examplemod.electric_furnace": "Electric Furnace", 7 | "block.examplemod.mod_furnace": "Mod Furnace", 8 | "itemGroup.examplemod": "Example Mod", 9 | "item.examplemod.example_crystal": "Example Crystal", 10 | "item.examplemod.wild_boar_spawn_egg": "Wild Boar Spawn Egg", 11 | "gui.examplemod.refresh_mini_model": "Refresh Mini Model", 12 | "gui.examplemod.energy": "Energy %s", 13 | "gui.examplemod.smeltTimeProgress": "Smelt time %s/%s", 14 | "gui.examplemod.fuelBurnTimeProgress": "Fuel burn time %s/%s" 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/electric_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/furnace", 3 | "textures": { 4 | "front": "examplemod:block/electric_furnace_front" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/example_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube_all", 3 | "textures": { 4 | "all": "examplemod:block/example_block" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/example_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube_all", 3 | "textures": { 4 | "all": "examplemod:block/example_ore" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/heat_collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "parent": "block/block", 4 | "textures": { 5 | "0": "block/anvil_top", 6 | "1": "block/anvil", 7 | "2": "block/obsidian", 8 | "particle": "block/anvil_top" 9 | }, 10 | "elements": [ 11 | { 12 | "from": [ 13 | 11, 14 | 0, 15 | 0 16 | ], 17 | "to": [ 18 | 16, 19 | 16, 20 | 5 21 | ], 22 | "faces": { 23 | "north": { 24 | "uv": [ 25 | 3, 26 | 0, 27 | 13, 28 | 16 29 | ], 30 | "texture": "#0", 31 | "cullface": "north" 32 | }, 33 | "east": { 34 | "uv": [ 35 | 3, 36 | 0, 37 | 13, 38 | 16 39 | ], 40 | "texture": "#0", 41 | "cullface": "east" 42 | }, 43 | "south": { 44 | "uv": [ 45 | 3, 46 | 0, 47 | 13, 48 | 16 49 | ], 50 | "texture": "#0" 51 | }, 52 | "west": { 53 | "uv": [ 54 | 3, 55 | 0, 56 | 13, 57 | 16 58 | ], 59 | "texture": "#0" 60 | }, 61 | "up": { 62 | "uv": [ 63 | 0, 64 | 0, 65 | 16, 66 | 16 67 | ], 68 | "texture": "#1", 69 | "cullface": "up" 70 | }, 71 | "down": { 72 | "uv": [ 73 | 0, 74 | 0, 75 | 16, 76 | 16 77 | ], 78 | "texture": "#1", 79 | "cullface": "down" 80 | } 81 | } 82 | }, 83 | { 84 | "from": [ 85 | 11, 86 | 0, 87 | 11 88 | ], 89 | "to": [ 90 | 16, 91 | 16, 92 | 16 93 | ], 94 | "faces": { 95 | "north": { 96 | "uv": [ 97 | 3, 98 | 0, 99 | 13, 100 | 16 101 | ], 102 | "texture": "#0" 103 | }, 104 | "east": { 105 | "uv": [ 106 | 3, 107 | 0, 108 | 13, 109 | 16 110 | ], 111 | "texture": "#0", 112 | "cullface": "east" 113 | }, 114 | "south": { 115 | "uv": [ 116 | 3, 117 | 0, 118 | 13, 119 | 16 120 | ], 121 | "texture": "#0", 122 | "cullface": "south" 123 | }, 124 | "west": { 125 | "uv": [ 126 | 3, 127 | 0, 128 | 13, 129 | 16 130 | ], 131 | "texture": "#0" 132 | }, 133 | "up": { 134 | "uv": [ 135 | 0, 136 | 0, 137 | 16, 138 | 16 139 | ], 140 | "texture": "#1", 141 | "cullface": "up" 142 | }, 143 | "down": { 144 | "uv": [ 145 | 0, 146 | 0, 147 | 16, 148 | 16 149 | ], 150 | "texture": "#1", 151 | "cullface": "down" 152 | } 153 | } 154 | }, 155 | { 156 | "from": [ 157 | 0, 158 | 0, 159 | 11 160 | ], 161 | "to": [ 162 | 5, 163 | 16, 164 | 16 165 | ], 166 | "faces": { 167 | "north": { 168 | "uv": [ 169 | 3, 170 | 0, 171 | 13, 172 | 16 173 | ], 174 | "texture": "#0" 175 | }, 176 | "east": { 177 | "uv": [ 178 | 3, 179 | 0, 180 | 13, 181 | 16 182 | ], 183 | "texture": "#0" 184 | }, 185 | "south": { 186 | "uv": [ 187 | 3, 188 | 0, 189 | 13, 190 | 16 191 | ], 192 | "texture": "#0", 193 | "cullface": "south" 194 | }, 195 | "west": { 196 | "uv": [ 197 | 3, 198 | 0, 199 | 13, 200 | 16 201 | ], 202 | "texture": "#0", 203 | "cullface": "west" 204 | }, 205 | "up": { 206 | "uv": [ 207 | 0, 208 | 0, 209 | 16, 210 | 16 211 | ], 212 | "texture": "#1", 213 | "cullface": "up" 214 | }, 215 | "down": { 216 | "uv": [ 217 | 0, 218 | 0, 219 | 16, 220 | 16 221 | ], 222 | "texture": "#1", 223 | "cullface": "down" 224 | } 225 | } 226 | }, 227 | { 228 | "from": [ 229 | 0, 230 | 0, 231 | 0 232 | ], 233 | "to": [ 234 | 5, 235 | 16, 236 | 5 237 | ], 238 | "faces": { 239 | "north": { 240 | "uv": [ 241 | 3, 242 | 0, 243 | 13, 244 | 16 245 | ], 246 | "texture": "#0", 247 | "cullface": "north" 248 | }, 249 | "east": { 250 | "uv": [ 251 | 3, 252 | 0, 253 | 13, 254 | 16 255 | ], 256 | "texture": "#0" 257 | }, 258 | "south": { 259 | "uv": [ 260 | 3, 261 | 0, 262 | 13, 263 | 16 264 | ], 265 | "texture": "#0" 266 | }, 267 | "west": { 268 | "uv": [ 269 | 3, 270 | 0, 271 | 13, 272 | 16 273 | ], 274 | "texture": "#0", 275 | "cullface": "west" 276 | }, 277 | "up": { 278 | "uv": [ 279 | 0, 280 | 0, 281 | 16, 282 | 16 283 | ], 284 | "texture": "#1", 285 | "cullface": "up" 286 | }, 287 | "down": { 288 | "uv": [ 289 | 0, 290 | 0, 291 | 16, 292 | 16 293 | ], 294 | "texture": "#1", 295 | "cullface": "down" 296 | } 297 | } 298 | }, 299 | { 300 | "from": [ 301 | 5, 302 | 5, 303 | 5 304 | ], 305 | "to": [ 306 | 11, 307 | 11, 308 | 11 309 | ], 310 | "faces": { 311 | "north": { 312 | "uv": [ 313 | 0, 314 | 0, 315 | 16, 316 | 16 317 | ], 318 | "texture": "#2" 319 | }, 320 | "east": { 321 | "uv": [ 322 | 0, 323 | 0, 324 | 16, 325 | 16 326 | ], 327 | "texture": "#2" 328 | }, 329 | "south": { 330 | "uv": [ 331 | 0, 332 | 0, 333 | 16, 334 | 16 335 | ], 336 | "texture": "#2" 337 | }, 338 | "west": { 339 | "uv": [ 340 | 0, 341 | 0, 342 | 16, 343 | 16 344 | ], 345 | "texture": "#2" 346 | }, 347 | "up": { 348 | "uv": [ 349 | 0, 350 | 0, 351 | 16, 352 | 16 353 | ], 354 | "texture": "#2" 355 | }, 356 | "down": { 357 | "uv": [ 358 | 0, 359 | 0, 360 | 16, 361 | 16 362 | ], 363 | "texture": "#2" 364 | } 365 | } 366 | } 367 | ] 368 | } 369 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/mini_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/block", 3 | "textures": { 4 | "particle": "examplemod:block/mini_model" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/mod_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/furnace" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/block/mod_furnace_burning.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/furnace_on" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/electric_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "examplemod:block/electric_furnace" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/example_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "examplemod:block/example_block" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/example_crystal.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "examplemod:item/example_crystal" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/example_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "examplemod:block/example_ore" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/heat_collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "examplemod:block/heat_collector" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/mini_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube_all", 3 | "textures": { 4 | "all": "examplemod:block/mini_model" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/mod_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "examplemod:block/mod_furnace" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/models/item/wild_boar_spawn_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/template_spawn_egg" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/block/electric_furnace_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/electric_furnace_front.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/block/example_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/example_block.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/block/example_ore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/example_ore.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/block/mini_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/mini_model.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar_saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar_saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/gui/container/electric_furnace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/gui/container/electric_furnace.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/gui/container/heat_collector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/gui/container/heat_collector.png -------------------------------------------------------------------------------- /src/main/resources/assets/examplemod/textures/item/example_crystal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/item/example_crystal.png -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/electric_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:electric_furnace" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/example_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:example_block" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/example_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:example_ore" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/heat_collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:heat_collector" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/mini_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:mini_model" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/loot_tables/blocks/mod_furnace.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "examplemod:mod_furnace" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/recipes/example_block_from_example_crystal.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": [ 4 | "###", 5 | "###", 6 | "###" 7 | ], 8 | "key": { 9 | "#": { 10 | "item": "examplemod:example_crystal" 11 | } 12 | }, 13 | "result": { 14 | "item": "examplemod:example_block" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/recipes/example_crystal_from_example_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shapeless", 3 | "ingredients": [ 4 | { 5 | "item": "examplemod:example_block" 6 | } 7 | ], 8 | "result": { 9 | "item": "examplemod:example_crystal", 10 | "count": 9 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/recipes/example_crystal_from_smelting.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:smelting", 3 | "ingredient": { 4 | "item": "examplemod:example_ore" 5 | }, 6 | "result": "examplemod:example_crystal", 7 | "experience": 0.7, 8 | "cookingtime": 200 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/recipes/heat_collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": [ 4 | "i i", 5 | " o ", 6 | "i i" 7 | ], 8 | "key": { 9 | "i": { 10 | "item": "minecraft:iron_block" 11 | }, 12 | "o": { 13 | "item": "minecraft:obsidian" 14 | } 15 | }, 16 | "result": { 17 | "item": "examplemod:heat_collector" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/data/examplemod/recipes/mini_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shapeless", 3 | "ingredients": [ 4 | { 5 | "item": "minecraft:map" 6 | }, 7 | { 8 | "item": "minecraft:beacon" 9 | } 10 | ], 11 | "result": { 12 | "item": "examplemod:mini_model" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/examplemod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/examplemod.png -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "examplemod resources", 4 | "pack_format": 5, 5 | "_comment": "A pack_format of 5 requires json lang files and some texture changes from 1.15. Note: Forge requires v5 pack meta for all mods." 6 | } 7 | } 8 | --------------------------------------------------------------------------------