├── .gitignore ├── README.md ├── TechincalDoc.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src └── main ├── java └── com │ └── cleanroommc │ └── millennium │ ├── Millennium.java │ ├── MillenniumConfig.java │ ├── client │ ├── ColorUtil.java │ ├── font │ │ └── TextRenderer.java │ ├── gui │ │ ├── BarrelGui.java │ │ └── GuiHandler.java │ ├── resource │ │ ├── AssetHashMap.java │ │ ├── MillenniumSounds.java │ │ └── MillenniumTextures.java │ └── sounds │ │ ├── BarrelSoundEvents.java │ │ ├── BundleSoundEvents.java │ │ └── SignSoundEvents.java │ ├── common │ ├── blocks │ │ ├── BarrelBlock.java │ │ ├── BlastFurnaceBlock.java │ │ ├── ContainerBlock.java │ │ ├── DirectionalBlock.java │ │ ├── HorizontalBlock.java │ │ ├── MillenniumBlock.java │ │ ├── MillenniumBlocks.java │ │ └── ScaffoldingBlock.java │ ├── container │ │ └── BarrelContainer.java │ ├── items │ │ ├── BundleItem.java │ │ ├── MillenniumItem.java │ │ └── MillenniumItems.java │ ├── tag │ │ ├── ITaggable.java │ │ ├── MetaItem.java │ │ ├── Tag.java │ │ ├── TagDelegate.java │ │ ├── TagEventHandler.java │ │ └── TagHelpers.java │ ├── tileentity │ │ ├── BarrelBlockTileEntity.java │ │ ├── BlastFurnaceTileEntity.java │ │ └── MillenniumSignTileEntity.java │ └── util │ │ ├── ChunkPosHelper.java │ │ ├── ICachedTeleporter.java │ │ └── VoxelShape.java │ ├── core │ └── MillenniumCore.java │ ├── mixin │ ├── BlockMixin.java │ ├── BlockPortalMixin.java │ ├── BlockStateBaseMixin.java │ ├── ContainerMixin.java │ ├── EntityLivingBaseMixin.java │ ├── ForgeRegistryEntryImplMixin.java │ ├── ItemMixin.java │ ├── ItemStackMixin.java │ ├── MinecraftClientMixin.java │ ├── OreDictionaryMixin.java │ ├── SignBlockMixin.java │ ├── SignTileEntityRendererMixin.java │ ├── TeleporterMixin.java │ └── WorldMixin.java │ ├── network │ ├── POIBulkUpdateMessage.java │ └── POIUpdateMessage.java │ ├── poi │ ├── IPOICapability.java │ ├── PointOfInterest.java │ ├── PointOfInterestHelper.java │ ├── PointOfInterestType.java │ └── event │ │ └── POIEvent.java │ └── proxy │ ├── ClientProxy.java │ └── CommonProxy.java └── resources ├── assets └── millennium │ ├── blockstates │ └── barrel.json │ ├── lang │ └── en_us.lang │ ├── models │ ├── block │ │ ├── barrel.json │ │ ├── barrel_open.json │ │ └── cube_bottom_top.json │ └── item │ │ ├── barrel.json │ │ ├── bundle.json │ │ ├── bundle_filled.json │ │ └── glow_ink_sac.json │ ├── recipes │ └── barrel.json │ └── sounds.json ├── mcmod.info ├── mixin.millennium.bundle.json ├── mixin.millennium.json └── pack.mcmeta /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | logs 16 | 17 | # gradle 18 | build 19 | .gradle 20 | 21 | # other 22 | eclipse 23 | run 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Millennium 2 | References: 3 | [Technical Documentation - For developers](TechincalDoc.md) -------------------------------------------------------------------------------- /TechincalDoc.md: -------------------------------------------------------------------------------- 1 | # Technical Documentation 2 | If you're going to develop mods based on Millennium, here are 3 | some important changes Millennium done to Minecraft: 4 | ## FontRenderer => TextRenderer 5 | 6 | > Deprecated, this change will move to Blackbox 7 | 8 | `net.minecraft.client.gui.FontRenderer fontRenderer` in `net.minecraft.client.Minecraft` 9 | has been replaced by Millennium with extended child class `io.github.cleanroommc.millennium.client.font.TextRenderer`, 10 | which means there are more modern TextRenderer method implementations, 11 | to use it, cast it into Millennium's TextRenderer first. 12 | 13 | It is always safe to cast it into Millennium's TextRenderer. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { 4 | url = 'https://maven.cleanroommc.com' 5 | } 6 | maven { 7 | url = 'https://repo.spongepowered.org/maven' 8 | } 9 | } 10 | dependencies { 11 | classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' 12 | if (project.use_mixins) { 13 | classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' 14 | } 15 | } 16 | } 17 | apply plugin: 'checkstyle' // now we need to define our codestyle ourself 18 | apply plugin: 'net.minecraftforge.gradle.forge' 19 | 20 | if (project.use_mixins) { 21 | apply plugin: 'org.spongepowered.mixin' 22 | } 23 | 24 | version = project.mod_version 25 | group = project.maven_group 26 | archivesBaseName = project.archives_base_name 27 | 28 | sourceCompatibility = targetCompatibility = '1.8' 29 | 30 | compileJava { 31 | sourceCompatibility = targetCompatibility = '1.8' 32 | } 33 | 34 | configurations { 35 | embed 36 | implementation.extendsFrom(embed) 37 | } 38 | 39 | minecraft { 40 | version = '1.12.2-14.23.5.2847' 41 | runDir = 'run' 42 | mappings = 'stable_39' 43 | def args = [] 44 | if (project.use_coremod) { 45 | args << '-Dfml.coreMods.load=' + coremod_plugin_class_name 46 | } 47 | if (project.use_mixins) { 48 | args << '-Dmixin.hotSwap=true' 49 | args << '-Dmixin.checks.interfaces=true' 50 | args << '-Dmixin.debug.export=true' 51 | } 52 | clientJvmArgs.addAll(args) 53 | serverJvmArgs.addAll(args) 54 | } 55 | 56 | repositories { 57 | maven { 58 | url = 'https://maven.cleanroommc.com' 59 | } 60 | maven { 61 | url = 'https://repo.spongepowered.org/maven' 62 | } 63 | } 64 | 65 | dependencies { 66 | if (project.use_assetmover) { 67 | deobfCompile 'com.cleanroommc:assetmover:0.2' 68 | } 69 | if (project.use_mixins) { 70 | deobfCompile 'zone.rong:mixinbooter:4.2' 71 | } 72 | } 73 | 74 | sourceSets { 75 | 76 | main { 77 | ext.refMap = 'mixins.' + archives_base_name + '.refmap.json' 78 | } 79 | 80 | } 81 | 82 | processResources { 83 | // this will ensure that this task is redone when the versions change. 84 | inputs.property 'version', project.version 85 | inputs.property 'mcversion', project.minecraft.version 86 | // replace stuff in mcmod.info, nothing else 87 | from(sourceSets.main.resources.srcDirs) { 88 | include 'mcmod.info' 89 | // replace version and mcversion 90 | expand 'version':project.version, 'mcversion':project.minecraft.version 91 | } 92 | // copy everything else except the mcmod.info 93 | from(sourceSets.main.resources.srcDirs) { 94 | exclude 'mcmod.info' 95 | } 96 | rename '(.+_at.cfg)', 'META-INF/$1' // Access Transformers 97 | } 98 | 99 | jar { 100 | manifest { 101 | def attribute_map = [:] 102 | if (project.use_coremod) { 103 | attribute_map['FMLCorePlugin'] = project.coremod_plugin_class_name 104 | } 105 | if (project.use_mixins) { 106 | attribute_map['TweakClass'] = 'org.spongepowered.asm.launch.MixinTweaker' 107 | } 108 | attributes(attribute_map) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /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 | # Mod Information 6 | mod_version = 1.0 7 | maven_group = com.cleanroommc 8 | archives_base_name = millennium 9 | 10 | # Boilerplate Options 11 | use_mixins = true 12 | use_coremod = true 13 | use_assetmover = true 14 | 15 | # Coremod Arguments 16 | coremod_plugin_class_name = com.cleanroommc.millennium.core.MillenniumCore -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleanroomMC/Millennium/9777aaf9b6ec775cfe0426897d83999aaba6d7d7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 14 12:28:28 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/Millennium.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium; 2 | 3 | import com.cleanroommc.millennium.client.sounds.BarrelSoundEvents; 4 | import com.cleanroommc.millennium.client.sounds.BundleSoundEvents; 5 | import com.cleanroommc.millennium.common.blocks.MillenniumBlocks; 6 | import com.cleanroommc.millennium.common.items.MillenniumItems; 7 | import com.cleanroommc.millennium.network.POIBulkUpdateMessage; 8 | import com.cleanroommc.millennium.network.POIUpdateMessage; 9 | import com.cleanroommc.millennium.proxy.CommonProxy; 10 | import net.minecraft.block.Block; 11 | import net.minecraft.item.Item; 12 | import net.minecraft.util.SoundEvent; 13 | import net.minecraftforge.client.event.ModelRegistryEvent; 14 | import net.minecraftforge.common.MinecraftForge; 15 | import net.minecraftforge.event.RegistryEvent; 16 | import net.minecraftforge.fml.common.Mod; 17 | import net.minecraftforge.fml.common.SidedProxy; 18 | import net.minecraftforge.fml.common.event.FMLConstructionEvent; 19 | import net.minecraftforge.fml.common.event.FMLInitializationEvent; 20 | import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; 21 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 22 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 23 | import net.minecraftforge.fml.common.network.NetworkRegistry; 24 | import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; 25 | import net.minecraftforge.fml.relauncher.Side; 26 | import org.apache.logging.log4j.LogManager; 27 | import org.apache.logging.log4j.Logger; 28 | 29 | @Mod(modid = Millennium.MODID, name = Millennium.NAME, version = Millennium.VERSION) 30 | public class Millennium { 31 | public static final Logger LOGGER = LogManager.getLogger(); 32 | public static final String MODID = "millennium"; 33 | public static final String NAME = "Millennium"; 34 | public static final String VERSION = "@VERSION@"; 35 | 36 | public static final SimpleNetworkWrapper CHANNEL = NetworkRegistry.INSTANCE.newSimpleChannel("millennium"); 37 | 38 | @Mod.Instance public static Millennium INSTANCE; 39 | 40 | @SidedProxy( 41 | clientSide = "com.cleanroommc.millennium.proxy.ClientProxy", 42 | serverSide = "com.cleanroommc.millennium.proxy.CommonProxy") 43 | public static CommonProxy proxy; 44 | 45 | @Mod.EventHandler 46 | public void construct(FMLConstructionEvent event) { 47 | MinecraftForge.EVENT_BUS.register(this); 48 | } 49 | 50 | @Mod.EventHandler 51 | public void preInit(FMLPreInitializationEvent event) { 52 | proxy.preInit(event); 53 | } 54 | 55 | @Mod.EventHandler 56 | public void init(FMLInitializationEvent event) { 57 | proxy.init(event); 58 | CHANNEL.registerMessage(POIUpdateMessage.Handler.INSTANCE, POIUpdateMessage.class, 0, Side.CLIENT); 59 | CHANNEL.registerMessage(POIBulkUpdateMessage.Handler.INSTANCE, POIBulkUpdateMessage.class, 1, Side.CLIENT); 60 | } 61 | 62 | @Mod.EventHandler 63 | public void postInit(FMLPostInitializationEvent event) { 64 | proxy.postInit(event); 65 | } 66 | 67 | @SubscribeEvent 68 | public void onBlockRegister(RegistryEvent.Register event) { 69 | MillenniumBlocks.registerBlock(event.getRegistry()); 70 | } 71 | 72 | @SubscribeEvent 73 | public void onItemRegister(RegistryEvent.Register event) { 74 | MillenniumItems.registerItem(event.getRegistry()); 75 | } 76 | 77 | @SubscribeEvent 78 | public void onSoundRegister(RegistryEvent.Register event) { 79 | BundleSoundEvents.init(event.getRegistry()); 80 | BarrelSoundEvents.init(event.getRegistry()); 81 | } 82 | 83 | @SubscribeEvent 84 | public void onItemModelRegister(ModelRegistryEvent event) { 85 | MillenniumItems.registerModel(); 86 | MillenniumBlocks.registerModel(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/MillenniumConfig.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium; 2 | 3 | import net.minecraft.launchwrapper.Launch; 4 | import net.minecraftforge.common.config.Configuration; 5 | import net.minecraftforge.common.config.Property; 6 | 7 | import java.io.File; 8 | 9 | public class MillenniumConfig { 10 | 11 | public static final MillenniumConfig INSTANCE = new MillenniumConfig(); 12 | 13 | static { 14 | INSTANCE.initialize(); 15 | } 16 | 17 | private Configuration configuration; 18 | 19 | public boolean bundles$Enabled; 20 | 21 | public void initialize() { 22 | configuration = new Configuration(new File(Launch.minecraftHome, "config" + File.separator + "millennium.cfg")); 23 | load(); 24 | } 25 | 26 | private void load() { 27 | bundles$Enabled = getCoreBoolean("enabled", "bundles", "Enable bundles module?", true); 28 | } 29 | 30 | private boolean getCoreBoolean(String name, String category, String description, boolean defaultValue) { 31 | Property prop = configuration.get(category, name, defaultValue); 32 | prop.setDefaultValue(defaultValue); 33 | prop.setComment(description + " - "); 34 | prop.setRequiresMcRestart(true); 35 | prop.setShowInGui(false); 36 | prop.setLanguageKey("millennium.config." + name); 37 | return prop.getBoolean(defaultValue); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client; 2 | 3 | import net.minecraft.item.EnumDyeColor; 4 | 5 | public final class ColorUtil { 6 | public static int getRed(int color) { 7 | return color & 255; 8 | } 9 | 10 | public static int getGreen(int color) { 11 | return color >> 8 & 255; 12 | } 13 | 14 | public static int getBlue(int color) { 15 | return color >> 16 & 255; 16 | } 17 | 18 | public static int getAbgrColor(int alpha, int blue, int green, int red) { 19 | return (alpha & 255) << 24 | (blue & 255) << 16 | (green & 255) << 8 | (red & 255) << 0; 20 | } 21 | 22 | public static int getColor(EnumDyeColor color, boolean isGlowing) { 23 | int i = color.getColorValue(); 24 | int j = (int)(getRed(i) * 0.4D); 25 | int k = (int)(getGreen(i) * 0.4D); 26 | int l = (int)(getBlue(i) * 0.4D); 27 | return i == EnumDyeColor.BLACK.getColorValue() && isGlowing ? -988212 : getAbgrColor(0, l, k, j); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/font/TextRenderer.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.font; 2 | 3 | import net.minecraft.client.gui.FontRenderer; 4 | import net.minecraft.client.renderer.texture.TextureManager; 5 | import net.minecraft.client.settings.GameSettings; 6 | import net.minecraft.item.EnumDyeColor; 7 | import net.minecraft.util.ResourceLocation; 8 | 9 | import java.util.Arrays; 10 | import java.util.Optional; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * This class will remove soon when Blackbox's implementation is done 15 | */ 16 | @Deprecated 17 | public class TextRenderer extends FontRenderer { 18 | private boolean drawOutline; 19 | 20 | public TextRenderer( 21 | GameSettings gameSettingsIn, 22 | ResourceLocation location, 23 | TextureManager textureManagerIn, 24 | boolean unicode) { 25 | super(gameSettingsIn, location, textureManagerIn, unicode); 26 | } 27 | 28 | @Override 29 | protected float renderDefaultChar(int ch, boolean italic) { 30 | if (!drawOutline) return super.renderDefaultChar(ch, italic); 31 | 32 | float lastValue = 0; 33 | float orgX = posX, orgY = posY; 34 | 35 | for (int j = -1; j < 2; j++) 36 | for (int k = -1; k < 2; k++) { 37 | posX = orgX + j; 38 | posY = orgY + k; 39 | lastValue = super.renderDefaultChar(ch, italic); 40 | } 41 | 42 | posX = orgX; 43 | posY = orgY; 44 | 45 | return lastValue; 46 | } 47 | 48 | @Override 49 | protected float renderUnicodeChar(char ch, boolean italic) { 50 | if (!drawOutline) return super.renderUnicodeChar(ch, italic); 51 | 52 | float lastValue = 0; 53 | float orgX = posX, orgY = posY; 54 | 55 | for (float j = -0.5F; j < 1.5; j += 0.5) 56 | for (float k = -0.5F; k < 1.5; k += 0.5) { 57 | posX = orgX + j; 58 | posY = orgY + k; 59 | lastValue = super.renderUnicodeChar(ch, italic); 60 | } 61 | 62 | posX = orgX; 63 | posY = orgY; 64 | 65 | return lastValue; 66 | } 67 | 68 | public void renderStringWithOutline(String text, float x, float y, int color, boolean shadow) { 69 | Optional dyeColor = 70 | Arrays.stream(EnumDyeColor.values()) 71 | .filter(clr -> clr.getColorValue() == color) 72 | .collect(Collectors.toList()) 73 | .stream() 74 | .findFirst(); // Should only have up to one match or none 75 | int outlineColor = getOutlineColor(dyeColor.orElse(EnumDyeColor.BLACK)); 76 | 77 | setDrawOutline(true); 78 | drawString(text, x, y, outlineColor, shadow); 79 | setDrawOutline(false); 80 | drawString(text, x, y, color, shadow); 81 | } 82 | 83 | public void setDrawOutline(boolean drawOutline) { 84 | this.drawOutline = drawOutline; 85 | } 86 | 87 | public static int getOutlineColor(EnumDyeColor color) { 88 | switch (color) { 89 | case WHITE: 90 | return 0x656565; 91 | case ORANGE: 92 | return 0x65280C; 93 | case MAGENTA: 94 | return 0x650065; 95 | case LIGHT_BLUE: 96 | return 0x3C4B51; 97 | case YELLOW: 98 | return 0x656500; 99 | case LIME: 100 | return 0x4B6500; 101 | case PINK: 102 | return 0x652947; 103 | case GRAY: 104 | return 0x323232; 105 | case SILVER: 106 | return 0x535353; 107 | case CYAN: 108 | return 0x006565; 109 | case PURPLE: 110 | return 0x3F0C5F; 111 | case BLUE: 112 | return 0x000065; 113 | case BROWN: 114 | return 0x361B07; 115 | case GREEN: 116 | return 0x006500; 117 | case RED: 118 | return 0x650000; 119 | case BLACK: 120 | default: 121 | return 0xEDE8CA; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/gui/BarrelGui.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.gui; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.common.container.BarrelContainer; 5 | import net.minecraft.client.gui.inventory.GuiContainer; 6 | import net.minecraft.client.renderer.GlStateManager; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.inventory.IInventory; 9 | import net.minecraft.util.ResourceLocation; 10 | 11 | public class BarrelGui extends GuiContainer { 12 | private static final ResourceLocation TEXTURE = 13 | new ResourceLocation(Millennium.MODID, "textures/gui/container/shulker_box.png"); 14 | private final IInventory playerInv; 15 | private final IInventory inventory; 16 | 17 | public BarrelGui(IInventory playerInv, IInventory inventory, EntityPlayer player) { 18 | super(new BarrelContainer(playerInv, inventory, player)); 19 | 20 | this.playerInv = playerInv; 21 | this.inventory = inventory; 22 | } 23 | 24 | @Override 25 | public void drawScreen(int mouseX, int mouseY, float partialTicks) { 26 | drawDefaultBackground(); 27 | super.drawScreen(mouseX, mouseY, partialTicks); 28 | renderHoveredToolTip(mouseX, mouseY); 29 | } 30 | 31 | @Override 32 | protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { 33 | String s = inventory.getDisplayName().getUnformattedText(); 34 | fontRenderer.drawString(s, 8, 6, 4210752); 35 | fontRenderer.drawString( 36 | playerInv.getDisplayName().getUnformattedText(), 8, ySize - 96 + 2, 4210752); 37 | } 38 | 39 | @Override 40 | protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { 41 | GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); 42 | mc.getTextureManager().bindTexture(TEXTURE); 43 | int i = (width - xSize) / 2; 44 | int j = (height - ySize) / 2; 45 | drawTexturedModalRect(i, j, 0, 0, xSize, ySize); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/gui/GuiHandler.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.gui; 2 | 3 | import com.cleanroommc.millennium.common.container.BarrelContainer; 4 | import com.cleanroommc.millennium.common.tileentity.BarrelBlockTileEntity; 5 | import net.minecraft.entity.player.EntityPlayer; 6 | import net.minecraft.tileentity.TileEntity; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.world.World; 9 | import net.minecraftforge.fml.common.network.IGuiHandler; 10 | 11 | import javax.annotation.Nullable; 12 | 13 | public class GuiHandler implements IGuiHandler { 14 | public static final int GUI_BARREL = 0; 15 | 16 | @Nullable 17 | @Override 18 | public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { 19 | TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); 20 | switch (ID) { 21 | case GUI_BARREL: 22 | return new BarrelContainer(player.inventory, (BarrelBlockTileEntity) tileEntity, player); 23 | default: 24 | return null; 25 | } 26 | } 27 | 28 | @Nullable 29 | @Override 30 | public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { 31 | TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); 32 | switch (ID) { 33 | case GUI_BARREL: 34 | return new BarrelGui(player.inventory, (BarrelBlockTileEntity) tileEntity, player); 35 | default: 36 | return null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/resource/AssetHashMap.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.resource; 2 | 3 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 4 | 5 | // Internal usage only 6 | class AssetHashMap extends Object2ObjectOpenHashMap { 7 | private final String resourceType; 8 | 9 | AssetHashMap(String resourceType) { 10 | this.resourceType = resourceType; 11 | } 12 | 13 | void put(String subPath) { 14 | put( 15 | "assets/minecraft/" + resourceType + "/" + subPath, 16 | "assets/millennium/" + resourceType + "/" + subPath); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/resource/MillenniumSounds.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.resource; 2 | 3 | import com.cleanroommc.assetmover.AssetMoverAPI; 4 | import net.minecraftforge.fml.relauncher.Side; 5 | import net.minecraftforge.fml.relauncher.SideOnly; 6 | 7 | public class MillenniumSounds { 8 | @SideOnly(Side.CLIENT) 9 | public static void initSounds() { 10 | AssetHashMap assets = new AssetHashMap("sounds"); 11 | // Bundle 12 | assets.put("item/bundle/drop_contents1.ogg"); 13 | assets.put("item/bundle/drop_contents2.ogg"); 14 | assets.put("item/bundle/drop_contents2.ogg"); 15 | assets.put("item/bundle/drop_contents3.ogg"); 16 | assets.put("item/bundle/insert1.ogg"); 17 | assets.put("item/bundle/insert2.ogg"); 18 | assets.put("item/bundle/insert3.ogg"); 19 | assets.put("item/bundle/remove_one1.ogg"); 20 | assets.put("item/bundle/remove_one2.ogg"); 21 | assets.put("item/bundle/remove_one3.ogg"); 22 | // Barrel 23 | assets.put("block/barrel/open1.ogg"); 24 | assets.put("block/barrel/open2.ogg"); 25 | assets.put("block/barrel/close.ogg"); 26 | AssetMoverAPI.fromMinecraft("1.18.1", assets); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/resource/MillenniumTextures.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.resource; 2 | 3 | import com.cleanroommc.assetmover.AssetMoverAPI; 4 | import net.minecraftforge.fml.relauncher.Side; 5 | import net.minecraftforge.fml.relauncher.SideOnly; 6 | 7 | public class MillenniumTextures { 8 | @SideOnly(Side.CLIENT) 9 | public static void initTextures() { 10 | AssetHashMap textures = new AssetHashMap("textures"); 11 | // Bundle 12 | textures.put("item/bundle.png"); 13 | textures.put("item/bundle_filled.png"); 14 | // Glow ink sac 15 | textures.put("item/glow_ink_sac.png"); 16 | // Barrel, Shulker Box 17 | textures.put("gui/container/shulker_box.png"); 18 | textures.put("block/barrel_bottom.png"); 19 | textures.put("block/barrel_side.png"); 20 | textures.put("block/barrel_top.png"); 21 | textures.put("block/barrel_top_open.png"); 22 | AssetMoverAPI.fromMinecraft("1.18.1", textures); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/sounds/BarrelSoundEvents.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.sounds; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import net.minecraft.util.ResourceLocation; 5 | import net.minecraft.util.SoundEvent; 6 | import net.minecraftforge.registries.IForgeRegistry; 7 | 8 | public class BarrelSoundEvents { 9 | public static SoundEvent OPEN, CLOSE; 10 | 11 | public static void init(IForgeRegistry registry) { 12 | OPEN = new SoundEvent(new ResourceLocation(Millennium.MODID, "block.barrel.open")); 13 | OPEN.setRegistryName(OPEN.getSoundName()); 14 | CLOSE = new SoundEvent(new ResourceLocation(Millennium.MODID, "block.barrel.close")); 15 | CLOSE.setRegistryName(CLOSE.getSoundName()); 16 | registry.registerAll(OPEN, CLOSE); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/sounds/BundleSoundEvents.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.sounds; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import net.minecraft.util.ResourceLocation; 5 | import net.minecraft.util.SoundEvent; 6 | import net.minecraftforge.registries.IForgeRegistry; 7 | 8 | public class BundleSoundEvents { 9 | public static SoundEvent REMOVE_ONE, DROP_CONTENTS, INSERT; 10 | 11 | public static void init(IForgeRegistry registry) { 12 | REMOVE_ONE = new SoundEvent(new ResourceLocation(Millennium.MODID, "item.bundle.remove_one")); 13 | REMOVE_ONE.setRegistryName(REMOVE_ONE.getSoundName()); 14 | DROP_CONTENTS = 15 | new SoundEvent(new ResourceLocation(Millennium.MODID, "item.bundle.drop_contents")); 16 | DROP_CONTENTS.setRegistryName(DROP_CONTENTS.getSoundName()); 17 | INSERT = new SoundEvent(new ResourceLocation(Millennium.MODID, "item.bundle.insert")); 18 | INSERT.setRegistryName(INSERT.getSoundName()); 19 | registry.registerAll(REMOVE_ONE, DROP_CONTENTS, INSERT); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/client/sounds/SignSoundEvents.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.client.sounds; 2 | 3 | public class SignSoundEvents { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/BarrelBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.client.gui.GuiHandler; 5 | import com.cleanroommc.millennium.common.tileentity.BarrelBlockTileEntity; 6 | import net.minecraft.block.ITileEntityProvider; 7 | import net.minecraft.block.SoundType; 8 | import net.minecraft.block.material.Material; 9 | import net.minecraft.block.properties.PropertyBool; 10 | import net.minecraft.block.state.BlockStateContainer; 11 | import net.minecraft.block.state.IBlockState; 12 | import net.minecraft.creativetab.CreativeTabs; 13 | import net.minecraft.entity.player.EntityPlayer; 14 | import net.minecraft.inventory.InventoryHelper; 15 | import net.minecraft.tileentity.TileEntity; 16 | import net.minecraft.util.EnumFacing; 17 | import net.minecraft.util.EnumHand; 18 | import net.minecraft.util.math.BlockPos; 19 | import net.minecraft.world.World; 20 | 21 | import javax.annotation.Nullable; 22 | 23 | public class BarrelBlock extends DirectionalBlock implements ITileEntityProvider { 24 | public static final PropertyBool OPEN = PropertyBool.create("open"); 25 | 26 | public BarrelBlock() { 27 | super( 28 | new Settings(Material.WOOD) 29 | .strength(2.5F) 30 | .soundType(SoundType.WOOD) 31 | .creativeTab(CreativeTabs.DECORATIONS) 32 | .translationKey("barrel")); 33 | setDefaultState(getBlockState().getBaseState().withProperty(OPEN, false)); 34 | } 35 | 36 | @Override 37 | public boolean onBlockActivated( 38 | World worldIn, 39 | BlockPos pos, 40 | IBlockState state, 41 | EntityPlayer playerIn, 42 | EnumHand hand, 43 | EnumFacing facing, 44 | float hitX, 45 | float hitY, 46 | float hitZ) { 47 | if (!worldIn.isRemote) 48 | playerIn.openGui( 49 | Millennium.INSTANCE, GuiHandler.GUI_BARREL, worldIn, pos.getX(), pos.getY(), pos.getZ()); 50 | 51 | return true; 52 | } 53 | 54 | @Override 55 | public void breakBlock(World worldIn, BlockPos pos, IBlockState state) { 56 | TileEntity tileEntity = worldIn.getTileEntity(pos); 57 | 58 | if (tileEntity instanceof BarrelBlockTileEntity) { 59 | InventoryHelper.dropInventoryItems(worldIn, pos, (BarrelBlockTileEntity) tileEntity); 60 | } 61 | 62 | super.breakBlock(worldIn, pos, state); 63 | } 64 | 65 | @Override 66 | public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int id, int param) { 67 | super.eventReceived(state, worldIn, pos, id, param); 68 | TileEntity tileentity = worldIn.getTileEntity(pos); 69 | return tileentity != null && tileentity.receiveClientEvent(id, param); 70 | } 71 | 72 | @Override 73 | public int getMetaFromState(IBlockState state) { 74 | int meta = super.getMetaFromState(state); 75 | 76 | if (state.getValue(OPEN)) meta |= 8; 77 | 78 | return meta; 79 | } 80 | 81 | @Override 82 | public IBlockState getStateFromMeta(int meta) { 83 | return getDefaultState().withProperty(FACING, EnumFacing.byIndex(meta & 7)); 84 | } 85 | 86 | @Override 87 | protected BlockStateContainer createBlockState() { 88 | return new BlockStateContainer(this, FACING, OPEN); 89 | } 90 | 91 | @Nullable 92 | @Override 93 | public TileEntity createNewTileEntity(World worldIn, int meta) { 94 | return new BarrelBlockTileEntity(); 95 | } 96 | 97 | @Override 98 | public boolean hasTileEntity(IBlockState state) { 99 | return true; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/BlastFurnaceBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import net.minecraft.block.BlockContainer; 4 | import net.minecraft.block.material.Material; 5 | import net.minecraft.tileentity.TileEntity; 6 | import net.minecraft.world.World; 7 | 8 | import javax.annotation.Nullable; 9 | 10 | public class BlastFurnaceBlock extends BlockContainer { 11 | protected BlastFurnaceBlock(Material materialIn) { 12 | super(materialIn); 13 | } 14 | 15 | @Nullable 16 | @Override 17 | public TileEntity createNewTileEntity(World worldIn, int meta) { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/ContainerBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import net.minecraft.block.ITileEntityProvider; 4 | import net.minecraft.block.material.Material; 5 | import net.minecraft.block.state.IBlockState; 6 | import net.minecraft.enchantment.EnchantmentHelper; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.init.Enchantments; 9 | import net.minecraft.init.Items; 10 | import net.minecraft.item.Item; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.stats.StatList; 13 | import net.minecraft.tileentity.TileEntity; 14 | import net.minecraft.util.EnumBlockRenderType; 15 | import net.minecraft.util.EnumFacing; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.IWorldNameable; 18 | import net.minecraft.world.World; 19 | 20 | import javax.annotation.Nullable; 21 | 22 | /** 23 | * Adapted version of {@link net.minecraft.block.BlockContainer} for Millennium 24 | */ 25 | public abstract class ContainerBlock extends MillenniumBlock implements ITileEntityProvider { 26 | public ContainerBlock(Settings settings) { 27 | super(settings); 28 | hasTileEntity = true; 29 | } 30 | 31 | protected boolean isInvalidNeighbor(World worldIn, BlockPos pos, EnumFacing facing) 32 | { 33 | return worldIn.getBlockState(pos.offset(facing)).getMaterial() == Material.CACTUS; 34 | } 35 | 36 | protected boolean hasInvalidNeighbor(World worldIn, BlockPos pos) 37 | { 38 | return this.isInvalidNeighbor(worldIn, pos, EnumFacing.NORTH) || this.isInvalidNeighbor(worldIn, pos, EnumFacing.SOUTH) || this.isInvalidNeighbor(worldIn, pos, EnumFacing.WEST) || this.isInvalidNeighbor(worldIn, pos, EnumFacing.EAST); 39 | } 40 | 41 | /** 42 | * The type of render function called. MODEL for mixed tesr and static model, MODELBLOCK_ANIMATED for TESR-only, 43 | * LIQUID for vanilla liquids, INVISIBLE to skip all rendering 44 | * @deprecated call via {@link IBlockState#getRenderType()} whenever possible. Implementing/overriding is fine. 45 | */ 46 | public EnumBlockRenderType getRenderType(IBlockState state) 47 | { 48 | return EnumBlockRenderType.INVISIBLE; 49 | } 50 | 51 | /** 52 | * Called serverside after this block is replaced with another in Chunk, but before the Tile Entity is updated 53 | */ 54 | public void breakBlock(World worldIn, BlockPos pos, IBlockState state) 55 | { 56 | super.breakBlock(worldIn, pos, state); 57 | worldIn.removeTileEntity(pos); 58 | } 59 | 60 | /** 61 | * Spawns the block's drops in the world. By the time this is called the Block has possibly been set to air via 62 | * Block.removedByPlayer 63 | */ 64 | public void harvestBlock(World worldIn, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack stack) 65 | { 66 | if (te instanceof IWorldNameable && ((IWorldNameable)te).hasCustomName()) 67 | { 68 | player.addStat(StatList.getBlockStats(this)); 69 | player.addExhaustion(0.005F); 70 | 71 | if (worldIn.isRemote) 72 | { 73 | return; 74 | } 75 | 76 | int i = EnchantmentHelper.getEnchantmentLevel(Enchantments.FORTUNE, stack); 77 | Item item = this.getItemDropped(state, worldIn.rand, i); 78 | 79 | if (item == Items.AIR) 80 | { 81 | return; 82 | } 83 | 84 | ItemStack itemstack = new ItemStack(item, this.quantityDropped(worldIn.rand)); 85 | itemstack.setStackDisplayName(((IWorldNameable)te).getName()); 86 | spawnAsEntity(worldIn, pos, itemstack); 87 | } 88 | else 89 | { 90 | super.harvestBlock(worldIn, player, pos, state, (TileEntity)null, stack); 91 | } 92 | } 93 | 94 | /** 95 | * Called on server when World#addBlockEvent is called. If server returns true, then also called on the client. On 96 | * the Server, this may perform additional changes to the world, like pistons replacing the block with an extended 97 | * base. On the client, the update may involve replacing tile entities or effects such as sounds or particles 98 | * @deprecated call via {@link IBlockState#onBlockEventReceived(World,BlockPos,int,int)} whenever possible. 99 | * Implementing/overriding is fine. 100 | */ 101 | public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int id, int param) 102 | { 103 | super.eventReceived(state, worldIn, pos, id, param); 104 | TileEntity tileentity = worldIn.getTileEntity(pos); 105 | return tileentity == null ? false : tileentity.receiveClientEvent(id, param); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/DirectionalBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import net.minecraft.block.BlockDirectional; 4 | import net.minecraft.block.properties.PropertyDirection; 5 | import net.minecraft.block.state.BlockStateContainer; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.entity.EntityLivingBase; 8 | import net.minecraft.util.EnumFacing; 9 | import net.minecraft.util.EnumHand; 10 | import net.minecraft.util.Mirror; 11 | import net.minecraft.util.Rotation; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.world.World; 14 | 15 | /** 16 | * Adapted version of {@link BlockDirectional} for Millennium 17 | */ 18 | public abstract class DirectionalBlock extends MillenniumBlock { 19 | public static final PropertyDirection FACING = BlockDirectional.FACING; 20 | 21 | public DirectionalBlock(Settings settings) { 22 | super(settings); 23 | setDefaultState(getBlockState().getBaseState().withProperty(FACING, EnumFacing.NORTH)); 24 | } 25 | 26 | @Override 27 | public IBlockState withRotation(IBlockState state, Rotation rot) { 28 | return state.withProperty(FACING, rot.rotate(state.getValue(FACING))); 29 | } 30 | 31 | @Override 32 | public IBlockState withMirror(IBlockState state, Mirror mirrorIn) { 33 | return state.withProperty(FACING, mirrorIn.mirror(state.getValue(FACING))); 34 | } 35 | 36 | @Override 37 | public IBlockState getStateForPlacement( 38 | World world, 39 | BlockPos pos, 40 | EnumFacing facing, 41 | float hitX, 42 | float hitY, 43 | float hitZ, 44 | int meta, 45 | EntityLivingBase placer, 46 | EnumHand hand) { 47 | return getDefaultState() 48 | .withProperty(FACING, EnumFacing.getDirectionFromEntityLiving(pos, placer)); 49 | } 50 | 51 | @Override 52 | public int getMetaFromState(IBlockState state) { 53 | return state.getValue(FACING).getIndex(); 54 | } 55 | 56 | @Override 57 | public IBlockState getStateFromMeta(int meta) { 58 | return getDefaultState().withProperty(FACING, EnumFacing.byIndex(meta)); 59 | } 60 | 61 | @Override 62 | protected BlockStateContainer createBlockState() { 63 | return new BlockStateContainer(this, FACING); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/HorizontalBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import net.minecraft.block.BlockHorizontal; 4 | import net.minecraft.block.properties.PropertyDirection; 5 | import net.minecraft.block.state.BlockStateContainer; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.entity.EntityLivingBase; 8 | import net.minecraft.util.EnumFacing; 9 | import net.minecraft.util.EnumHand; 10 | import net.minecraft.util.Mirror; 11 | import net.minecraft.util.Rotation; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.world.World; 14 | 15 | /** 16 | * Adapted version of {@link BlockHorizontal} for Millennium 17 | */ 18 | public abstract class HorizontalBlock extends MillenniumBlock { 19 | public static final PropertyDirection FACING = BlockHorizontal.FACING; 20 | 21 | public HorizontalBlock(Settings settings) { 22 | super(settings); 23 | setDefaultState(getBlockState().getBaseState().withProperty(FACING, EnumFacing.NORTH)); 24 | } 25 | 26 | @Override 27 | public IBlockState withRotation(IBlockState state, Rotation rot) { 28 | return state.withProperty(FACING, rot.rotate(state.getValue(FACING))); 29 | } 30 | 31 | @Override 32 | public IBlockState withMirror(IBlockState state, Mirror mirrorIn) { 33 | return state.withProperty(FACING, mirrorIn.mirror(state.getValue(FACING))); 34 | } 35 | 36 | @Override 37 | public IBlockState getStateForPlacement( 38 | World world, 39 | BlockPos pos, 40 | EnumFacing facing, 41 | float hitX, 42 | float hitY, 43 | float hitZ, 44 | int meta, 45 | EntityLivingBase placer, 46 | EnumHand hand) { 47 | return getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite()); 48 | } 49 | 50 | @Override 51 | public int getMetaFromState(IBlockState state) { 52 | return state.getValue(FACING).getIndex(); 53 | } 54 | 55 | @Override 56 | public IBlockState getStateFromMeta(int meta) { 57 | EnumFacing enumfacing = EnumFacing.byIndex(meta); 58 | 59 | if (enumfacing.getAxis() == EnumFacing.Axis.Y) { 60 | enumfacing = EnumFacing.NORTH; 61 | } 62 | 63 | return this.getDefaultState().withProperty(FACING, enumfacing); 64 | } 65 | 66 | @Override 67 | protected BlockStateContainer createBlockState() { 68 | return new BlockStateContainer(this, FACING); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/MillenniumBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.common.util.VoxelShape; 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.BlockHorizontal; 7 | import net.minecraft.block.SoundType; 8 | import net.minecraft.block.material.MapColor; 9 | import net.minecraft.block.material.Material; 10 | import net.minecraft.block.state.BlockFaceShape; 11 | import net.minecraft.block.state.IBlockState; 12 | import net.minecraft.creativetab.CreativeTabs; 13 | import net.minecraft.entity.Entity; 14 | import net.minecraft.item.Item; 15 | import net.minecraft.util.EnumFacing; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.IBlockAccess; 18 | 19 | import javax.annotation.Nullable; 20 | import java.util.function.Function; 21 | 22 | /** 23 | * Adapted version of {@link Block} for Millennium 24 | */ 25 | public class MillenniumBlock extends Block { 26 | final Settings settings; 27 | 28 | public MillenniumBlock(Settings settings) { 29 | super(settings.material, settings.material.getMaterialMapColor()); 30 | this.settings = settings; 31 | 32 | setResistance(settings.resistance); 33 | setHardness(settings.hardness); 34 | setSoundType(settings.soundType); 35 | setTranslationKey(settings.translationKey); 36 | setCreativeTab(settings.tab); 37 | } 38 | 39 | @Override 40 | public boolean canCollideCheck(IBlockState state, boolean hitIfLiquid) { 41 | return super.canCollideCheck(state, hitIfLiquid); 42 | } 43 | 44 | @Override 45 | public boolean isOpaqueCube(IBlockState state) { 46 | return settings != null && settings.opaque; 47 | } 48 | 49 | @Override 50 | public boolean isCollidable() { 51 | return settings.collidable; 52 | } 53 | 54 | @Override 55 | public BlockFaceShape getBlockFaceShape( 56 | IBlockAccess worldIn, IBlockState state, BlockPos pos, EnumFacing face) { 57 | return isOpaqueCube(state) ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED; 58 | } 59 | 60 | @Override 61 | public float getSlipperiness( 62 | IBlockState state, IBlockAccess world, BlockPos pos, @Nullable Entity entity) { 63 | return settings.slipperiness.apply(state, world, pos); 64 | } 65 | 66 | @Override 67 | public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos) { 68 | return settings.lightValue.apply(state, world, pos); 69 | } 70 | 71 | @Override 72 | public MapColor getMapColor(IBlockState state, IBlockAccess worldIn, BlockPos pos) { 73 | return settings.mapColor != null 74 | ? settings.mapColor.apply(state, worldIn, pos) 75 | : settings.material.getMaterialMapColor(); 76 | } 77 | 78 | protected void registerModel() { 79 | Millennium.proxy.registerItemRenderer(Item.getItemFromBlock(this), 0, "inventory"); 80 | } 81 | 82 | public static VoxelShape createShape( 83 | double x1, double y1, double z1, double x2, double y2, double z2) { 84 | return new VoxelShape(x1, y1, z1, x2, y2, z2); 85 | } 86 | 87 | @FunctionalInterface 88 | public interface ContextFunction { 89 | R apply(IBlockState t1, IBlockAccess t2, BlockPos t3); 90 | } 91 | 92 | public static class Settings { 93 | final Material material; 94 | final ContextFunction mapColor; 95 | String translationKey; 96 | CreativeTabs tab; 97 | boolean collidable = true; 98 | boolean opaque = true; 99 | float resistance; 100 | float hardness; 101 | SoundType soundType = SoundType.STONE; 102 | ContextFunction lightValue = (state, access, pos) -> 0; 103 | ContextFunction slipperiness = (state, access, pos) -> 0.6F; 104 | 105 | public Settings(Material material) { 106 | this(material, material.getMaterialMapColor()); 107 | } 108 | 109 | public Settings(Material material, MapColor mapColor) { 110 | this(material, (state, access, pos) -> mapColor); 111 | } 112 | 113 | public Settings(Material material, ContextFunction mapColor) { 114 | this.material = material; 115 | this.mapColor = mapColor; 116 | } 117 | 118 | public Settings noCollision() { 119 | collidable = false; 120 | opaque = false; 121 | return this; 122 | } 123 | 124 | public Settings nonOpaque() { 125 | opaque = false; 126 | return this; 127 | } 128 | 129 | public Settings translationKey(String translationKey) { 130 | this.translationKey = translationKey; 131 | return this; 132 | } 133 | 134 | public Settings creativeTab(CreativeTabs tab) { 135 | this.tab = tab; 136 | return this; 137 | } 138 | 139 | public Settings strength(float strength) { 140 | resistance = strength; 141 | hardness = strength; 142 | return this; 143 | } 144 | 145 | public Settings resistance(float resistance) { 146 | this.resistance = resistance; 147 | return this; 148 | } 149 | 150 | public Settings hardness(float hardness) { 151 | this.hardness = hardness; 152 | return this; 153 | } 154 | 155 | public Settings soundType(SoundType soundType) { 156 | this.soundType = soundType; 157 | return this; 158 | } 159 | 160 | public Settings lightValue(ContextFunction lightValue) { 161 | this.lightValue = lightValue; 162 | return this; 163 | } 164 | 165 | public Settings lightValue(Function lightValue) { 166 | this.lightValue = (state, access, pos) -> lightValue.apply(state); 167 | return this; 168 | } 169 | 170 | public Settings lightValue(int lightValue) { 171 | this.lightValue = (state, access, pos) -> lightValue; 172 | return this; 173 | } 174 | 175 | public Settings slipperiness(ContextFunction slipperiness) { 176 | this.slipperiness = slipperiness; 177 | return this; 178 | } 179 | 180 | public Settings slipperiness(Function slipperiness) { 181 | this.slipperiness = (state, access, pos) -> slipperiness.apply(state); 182 | return this; 183 | } 184 | 185 | public Settings slipperiness(float slipperiness) { 186 | this.slipperiness = (state, access, pos) -> slipperiness; 187 | return this; 188 | } 189 | 190 | public static Settings copy(MillenniumBlock block) { 191 | Settings settings = new Settings(block.settings.material, block.blockMapColor); 192 | settings.collidable = block.settings.collidable; 193 | settings.opaque = block.settings.opaque; 194 | settings.soundType = block.settings.soundType; 195 | settings.lightValue = block.settings.lightValue; 196 | settings.slipperiness = block.settings.slipperiness; 197 | return settings; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/MillenniumBlocks.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.common.items.MillenniumItems; 5 | import com.cleanroommc.millennium.common.tileentity.BarrelBlockTileEntity; 6 | import com.cleanroommc.millennium.common.tileentity.MillenniumSignTileEntity; 7 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 8 | import net.minecraft.block.Block; 9 | import net.minecraft.item.ItemBlock; 10 | import net.minecraft.tileentity.TileEntity; 11 | import net.minecraft.util.ResourceLocation; 12 | import net.minecraftforge.fml.common.registry.GameRegistry; 13 | import net.minecraftforge.registries.IForgeRegistry; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class MillenniumBlocks { 20 | private static final List BLOCKS = new ArrayList<>(); 21 | private static final Map, String> TILE_ENTITIES = 22 | new Object2ObjectOpenHashMap<>(); 23 | 24 | static { 25 | register("sign", MillenniumSignTileEntity.class); 26 | } 27 | 28 | public static final BarrelBlock BARREL_BLOCK = 29 | register("barrel", new BarrelBlock(), BarrelBlockTileEntity.class); 30 | 31 | public static final ScaffoldingBlock SCAFFOLDING_BLOCK = 32 | register("scaffolding", new ScaffoldingBlock()); 33 | 34 | private static T register(String id, T block) { 35 | block.setRegistryName(Millennium.MODID, id); 36 | BLOCKS.add(block); 37 | MillenniumItems.ITEM_BLOCKS.add(new ItemBlock(block).setRegistryName(block.getRegistryName())); 38 | return block; 39 | } 40 | 41 | private static T register( 42 | String id, T block, Class tileEntity) { 43 | T registeredBlock = register(id, block); 44 | TILE_ENTITIES.put(tileEntity, id); 45 | return registeredBlock; 46 | } 47 | 48 | private static void register(String id, Class tileEntity) { 49 | TILE_ENTITIES.put(tileEntity, id); 50 | } 51 | 52 | public static void registerBlock(IForgeRegistry registry) { 53 | for (Block block : BLOCKS) { 54 | registry.register(block); 55 | } 56 | 57 | for (Map.Entry, String> entry : TILE_ENTITIES.entrySet()) { 58 | GameRegistry.registerTileEntity( 59 | entry.getKey(), new ResourceLocation(Millennium.MODID, entry.getValue())); 60 | } 61 | } 62 | 63 | public static void registerModel() { 64 | for (MillenniumBlock block : BLOCKS) block.registerModel(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/blocks/ScaffoldingBlock.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.blocks; 2 | 3 | import com.cleanroommc.millennium.common.util.VoxelShape; 4 | import net.minecraft.block.material.MapColor; 5 | import net.minecraft.block.material.Material; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.creativetab.CreativeTabs; 8 | import net.minecraft.entity.Entity; 9 | import net.minecraft.util.math.AxisAlignedBB; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.world.IBlockAccess; 12 | import net.minecraft.world.World; 13 | 14 | import javax.annotation.Nullable; 15 | import java.util.List; 16 | 17 | public class ScaffoldingBlock extends MillenniumBlock { 18 | // NORMAL SHAPES 19 | public static final VoxelShape NORMAL_SHAPE$1 = createShape(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D); 20 | public static final VoxelShape NORMAL_SHAPE$2 = createShape(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D); 21 | public static final VoxelShape NORMAL_SHAPE$3 = createShape(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D); 22 | public static final VoxelShape NORMAL_SHAPE$4 = createShape(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0D); 23 | public static final VoxelShape NORMAL_SHAPE$5 = createShape(14.0D, 0.0D, 14.0D, 16.0D, 16.0D, 16.0D); 24 | // BOTTOM OUTLINE SHAPES TODO: Implement multiple outline shape rendering functionality first 25 | public static final VoxelShape OUTLINE_SHAPE$1 = createShape(0.0D, 0.0D, 0.0D, 2.0D, 2.0D, 16.0D); 26 | public static final VoxelShape OUTLINE_SHAPE$2 = createShape(14.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D); 27 | public static final VoxelShape OUTLINE_SHAPE$3 = createShape(0.0D, 0.0D, 14.0D, 16.0D, 2.0D, 16.0D); 28 | public static final VoxelShape OUTLINE_SHAPE$4 = createShape(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 2.0D); 29 | 30 | public ScaffoldingBlock() { 31 | super( 32 | new MillenniumBlock.Settings(Material.WOOD, MapColor.SAND) 33 | .creativeTab(CreativeTabs.DECORATIONS) 34 | .translationKey("scaffolding")); 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public AxisAlignedBB getCollisionBoundingBox( 40 | IBlockState blockState, IBlockAccess worldIn, BlockPos pos) { 41 | return NULL_AABB; 42 | } 43 | 44 | @Override 45 | public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List collidingBoxes, @Nullable Entity entityIn, boolean isActualState) { 46 | if (entityIn == null) 47 | super.addCollisionBoxToList( 48 | state, worldIn, pos, entityBox, collidingBoxes, entityIn, isActualState); 49 | else { 50 | if (entityIn.isSneaking()) { 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public boolean isFullCube(IBlockState state) { 57 | return false; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/container/BarrelContainer.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.container; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.inventory.Container; 5 | import net.minecraft.inventory.IInventory; 6 | import net.minecraft.inventory.Slot; 7 | import net.minecraft.item.ItemStack; 8 | 9 | public class BarrelContainer extends Container { 10 | private final IInventory inventory; 11 | 12 | public BarrelContainer(IInventory playerInv, IInventory inventory, EntityPlayer player) { 13 | this.inventory = inventory; 14 | inventory.openInventory(player); 15 | 16 | for (int i = 0; i < 3; ++i) 17 | for (int j = 0; j < 9; ++j) 18 | addSlotToContainer(new Slot(inventory, j + i * 9, 8 + j * 18, 18 + i * 18)); 19 | 20 | for (int i = 0; i < 3; ++i) 21 | for (int j = 0; j < 9; ++j) 22 | addSlotToContainer(new Slot(playerInv, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); 23 | 24 | for (int i = 0; i < 9; ++i) addSlotToContainer(new Slot(playerInv, i, 8 + i * 18, 142)); 25 | } 26 | 27 | @Override 28 | public boolean canInteractWith(EntityPlayer playerIn) { 29 | return inventory.isUsableByPlayer(playerIn); 30 | } 31 | 32 | @Override 33 | public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) { 34 | ItemStack itemstack = ItemStack.EMPTY; 35 | Slot slot = inventorySlots.get(index); 36 | 37 | if (slot != null && slot.getHasStack()) { 38 | ItemStack itemstack1 = slot.getStack(); 39 | itemstack = itemstack1.copy(); 40 | 41 | if (index < inventory.getSizeInventory()) { 42 | if (!mergeItemStack( 43 | itemstack1, inventory.getSizeInventory(), inventorySlots.size(), true)) { 44 | return ItemStack.EMPTY; 45 | } 46 | } else if (!mergeItemStack(itemstack1, 0, inventory.getSizeInventory(), false)) { 47 | return ItemStack.EMPTY; 48 | } 49 | 50 | if (itemstack1.isEmpty()) slot.putStack(ItemStack.EMPTY); 51 | else slot.onSlotChanged(); 52 | } 53 | 54 | return itemstack; 55 | } 56 | 57 | @Override 58 | public void onContainerClosed(EntityPlayer playerIn) { 59 | super.onContainerClosed(playerIn); 60 | inventory.closeInventory(playerIn); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/items/BundleItem.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.items; 2 | 3 | import com.cleanroommc.millennium.client.sounds.BundleSoundEvents; 4 | import com.google.common.collect.Streams; 5 | import net.minecraft.client.util.ITooltipFlag; 6 | import net.minecraft.creativetab.CreativeTabs; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.entity.player.EntityPlayerMP; 9 | import net.minecraft.item.IItemPropertyGetter; 10 | import net.minecraft.item.ItemStack; 11 | import net.minecraft.nbt.NBTBase; 12 | import net.minecraft.nbt.NBTTagCompound; 13 | import net.minecraft.nbt.NBTTagList; 14 | import net.minecraft.stats.StatList; 15 | import net.minecraft.util.EnumActionResult; 16 | import net.minecraft.util.EnumFacing; 17 | import net.minecraft.util.EnumHand; 18 | import net.minecraft.util.SoundCategory; 19 | import net.minecraft.util.math.BlockPos; 20 | import net.minecraft.world.World; 21 | import net.minecraftforge.common.ForgeHooks; 22 | 23 | import javax.annotation.Nullable; 24 | import java.util.Iterator; 25 | import java.util.List; 26 | import java.util.stream.Stream; 27 | 28 | public class BundleItem extends MillenniumItem { 29 | private static final IItemPropertyGetter FULLNESS_PROPERTY = 30 | (stack, world, entity) -> (float) getContentWeight(stack) / 64F; 31 | 32 | public static final int MAX_WEIGHT = 64; 33 | 34 | private static final String TAG = "Items"; 35 | private static final int BUNDLE_IN_BUNDLE_WEIGHT = 4; 36 | 37 | BundleItem() { 38 | super(new Settings().maxDamage(1).creativeTab(CreativeTabs.TOOLS).translationKey("bundle")); 39 | } 40 | 41 | private static int getWeight(ItemStack stack) { 42 | if (stack.getItem() instanceof BundleItem) { 43 | return 4 + getContentWeight(stack); 44 | } 45 | return 64 / stack.getMaxStackSize(); 46 | } 47 | 48 | private static int getContentWeight(ItemStack stack) { 49 | return getContents(stack).mapToInt(content -> getWeight(content) * content.getCount()).sum(); 50 | } 51 | 52 | public static ItemStack removeOne(ItemStack stack) { 53 | NBTTagCompound tag = stack.getTagCompound(); 54 | if (tag != null) { 55 | if (tag.hasKey(TAG)) { 56 | NBTTagList list = tag.getTagList(TAG, 10); 57 | ItemStack removedStack = new ItemStack((NBTTagCompound) list.removeTag(0)); 58 | if (list.isEmpty()) { 59 | tag.removeTag(TAG); 60 | } 61 | return removedStack; 62 | } 63 | } 64 | return ItemStack.EMPTY; 65 | } 66 | 67 | public static int addOne(ItemStack bundle, ItemStack add) { 68 | if (!add.isEmpty()) { 69 | int currentWeight = getContentWeight(bundle); 70 | int addWeight = getWeight(add); 71 | int remainingSize = Math.min(add.getCount(), (64 - currentWeight) / addWeight); 72 | if (remainingSize != 0) { 73 | NBTTagCompound tag = bundle.getTagCompound(); 74 | if (tag == null) { 75 | tag = new NBTTagCompound(); 76 | bundle.setTagCompound(tag); 77 | } 78 | NBTTagList list; 79 | if (tag.hasKey(TAG)) { 80 | list = tag.getTagList(TAG, 10); 81 | } else { 82 | list = new NBTTagList(); 83 | tag.setTag(TAG, list); 84 | } 85 | NBTTagCompound matchingTag = getAndRemoveMatchingItem(add, list); 86 | if (matchingTag != null) { 87 | ItemStack matchingStack = new ItemStack(matchingTag); 88 | matchingStack.grow(remainingSize); 89 | matchingStack.writeToNBT(matchingTag); 90 | list.set(0, matchingTag); 91 | } else { 92 | ItemStack copyAdd = add.copy(); 93 | copyAdd.setCount(remainingSize); 94 | NBTTagCompound newTag = new NBTTagCompound(); 95 | copyAdd.writeToNBT(newTag); 96 | list.appendTag(newTag); 97 | } 98 | return remainingSize; 99 | } 100 | } 101 | return 0; 102 | } 103 | 104 | private static boolean dropContents(ItemStack stack, EntityPlayer player) { 105 | NBTTagCompound tag = stack.getTagCompound(); 106 | if (tag != null) { 107 | if (tag.hasKey(TAG)) { 108 | if (player instanceof EntityPlayerMP) { 109 | NBTTagList list = tag.getTagList(TAG, 10); 110 | for (NBTBase itemTag : list) { 111 | if (itemTag instanceof NBTTagCompound) { 112 | ForgeHooks.onPlayerTossEvent(player, new ItemStack((NBTTagCompound) itemTag), true); 113 | } 114 | } 115 | } 116 | tag.removeTag(TAG); 117 | return true; 118 | } 119 | } 120 | return false; 121 | } 122 | 123 | @SuppressWarnings("UnstableApiUsage") 124 | private static Stream getContents(ItemStack stack) { 125 | NBTTagCompound tag = stack.getTagCompound(); 126 | if (tag != null) { 127 | if (tag.hasKey(TAG)) { 128 | NBTTagList list = tag.getTagList(TAG, 10); 129 | return Streams.stream(list).map(NBTTagCompound.class::cast).map(ItemStack::new); 130 | } 131 | } 132 | return Stream.of(); 133 | } 134 | 135 | @Nullable 136 | private static NBTTagCompound getAndRemoveMatchingItem(ItemStack add, NBTTagList list) { 137 | if (!(add.getItem() instanceof BundleItem)) { 138 | Iterator iter = list.iterator(); 139 | while (iter.hasNext()) { 140 | NBTBase base = iter.next(); 141 | if (base instanceof NBTTagCompound) { 142 | NBTTagCompound tag = (NBTTagCompound) base; 143 | ItemStack compareStack = new ItemStack(tag); 144 | if (ItemStack.areItemsEqual(compareStack, add) 145 | && ItemStack.areItemStackTagsEqual(compareStack, add)) { 146 | iter.remove(); 147 | return tag; 148 | } 149 | } 150 | } 151 | } 152 | return null; 153 | } 154 | 155 | @Override 156 | public EnumActionResult onItemUse( 157 | EntityPlayer player, 158 | World world, 159 | BlockPos pos, 160 | EnumHand hand, 161 | EnumFacing facing, 162 | float hitX, 163 | float hitY, 164 | float hitZ) { 165 | ItemStack stack = player.getHeldItem(hand); 166 | if (dropContents(stack, player)) { 167 | world.playSound( 168 | null, 169 | player.posX, 170 | player.posY, 171 | player.posZ, 172 | BundleSoundEvents.DROP_CONTENTS, 173 | SoundCategory.PLAYERS, 174 | 0.8F, 175 | (float) (0.8 + world.rand.nextFloat() * 0.4F)); 176 | player.addStat(StatList.getObjectUseStats(this)); 177 | return EnumActionResult.SUCCESS; 178 | } 179 | return EnumActionResult.FAIL; 180 | } 181 | 182 | @Override 183 | public void addInformation(ItemStack stack, @Nullable World worldIn, List tooltip, ITooltipFlag flagIn) { 184 | getContents(stack).forEach(s -> tooltip.add(s.getDisplayName() + " x" + s.getMaxStackSize())); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/items/MillenniumItem.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.items; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import net.minecraft.creativetab.CreativeTabs; 5 | import net.minecraft.item.EnumRarity; 6 | import net.minecraft.item.Item; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraftforge.common.IRarity; 9 | 10 | public class MillenniumItem extends Item { 11 | EnumRarity rarity; 12 | 13 | public MillenniumItem(Settings settings) { 14 | setMaxStackSize(settings.maxCount); 15 | setMaxDamage(settings.maxDamage); 16 | setCreativeTab(settings.tab); 17 | rarity = settings.rarity; 18 | setTranslationKey(settings.translationKey); 19 | } 20 | 21 | @Override 22 | public IRarity getForgeRarity(ItemStack stack) { 23 | return rarity; 24 | } 25 | 26 | protected void registerModel() { 27 | Millennium.proxy.registerItemRenderer(this, 0, "inventory"); 28 | } 29 | 30 | public static class Settings { 31 | int maxCount = 64; 32 | int maxDamage; 33 | CreativeTabs tab; 34 | EnumRarity rarity; 35 | String translationKey; 36 | 37 | public Settings() { 38 | rarity = EnumRarity.COMMON; 39 | } 40 | 41 | public Settings maxCount(int count) { 42 | maxCount = count; 43 | return this; 44 | } 45 | 46 | public Settings maxDamage(int damage) { 47 | maxCount = damage; 48 | return this; 49 | } 50 | 51 | public Settings creativeTab(CreativeTabs tab) { 52 | this.tab = tab; 53 | return this; 54 | } 55 | 56 | public Settings translationKey(String translationKey) { 57 | this.translationKey = translationKey; 58 | return this; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/items/MillenniumItems.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.items; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import net.minecraft.creativetab.CreativeTabs; 5 | import net.minecraft.item.Item; 6 | import net.minecraftforge.registries.IForgeRegistry; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public final class MillenniumItems { 12 | public static final List ITEMS = new ArrayList<>(); 13 | public static final List ITEM_BLOCKS = new ArrayList<>(); 14 | 15 | public static BundleItem ITEM_BUNDLE = register("bundle", new BundleItem()); 16 | 17 | public static MillenniumItem GLOW_INK_SAC = 18 | register( 19 | "glow_ink_sac", 20 | new MillenniumItem( 21 | new MillenniumItem.Settings() 22 | .creativeTab(CreativeTabs.MATERIALS) 23 | .translationKey("glow_ink_sac"))); 24 | 25 | private static T register(String id, T item) { 26 | item.setRegistryName(Millennium.MODID, id); 27 | ITEMS.add(item); 28 | return item; 29 | } 30 | 31 | public static void registerItem(IForgeRegistry registry) { 32 | for (Item item : ITEMS) registry.register(item); 33 | for (Item item : ITEM_BLOCKS) registry.register(item); 34 | } 35 | 36 | public static void registerModel() { 37 | for (MillenniumItem item : ITEMS) item.registerModel(); 38 | for (Item item : ITEM_BLOCKS) Millennium.proxy.registerItemRenderer(item, 0, "inventory"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/ITaggable.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import java.util.stream.Stream; 4 | 5 | /** 6 | * Represents an object that can be tagged. All Forge registry entries have this interface on them. It is also available 7 | * on ItemStacks and IBlockStates. 8 | */ 9 | public interface ITaggable { 10 | /** 11 | * Add a tag to an object. 12 | * @param tag 13 | */ 14 | void addTag(Tag tag); 15 | 16 | /** 17 | * Check if this ITaggable has a certain tag. 18 | * @param tag 19 | * @return 20 | */ 21 | boolean isTag(Tag tag); 22 | 23 | /** 24 | * Remove a tag from this object. 25 | * @param tag 26 | */ 27 | void removeTag(Tag tag); 28 | 29 | /** 30 | * Get the list of tags this object contains. 31 | * @return 32 | */ 33 | Stream getTags(); 34 | Class getTagDelegateType(); 35 | 36 | /** 37 | * Retrieves the ITaggable or throw an exception if it can't be tagged. 38 | * @param object The object to convert 39 | */ 40 | @SuppressWarnings("unchecked") 41 | static ITaggable of(T object) { 42 | if(!(object instanceof ITaggable)) 43 | throw new IllegalArgumentException(object.toString() + " is not taggable"); 44 | return (ITaggable)object; 45 | } 46 | 47 | /** 48 | * Remove a tag from all objects. Useful if you are trying to clear out uses of the tag to add it again. 49 | * @param tag 50 | */ 51 | static void removeFromAll(Tag tag) { 52 | TagDelegate.removeFromAll(tag); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/MetaItem.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import net.minecraft.item.Item; 4 | 5 | import java.util.Objects; 6 | import java.util.stream.Stream; 7 | 8 | /** 9 | * Proxy class, to avoid storing lots of ItemStack instances in memory. 10 | */ 11 | public class MetaItem implements ITaggable { 12 | private final Item item; 13 | private final int meta; 14 | 15 | public MetaItem(Item item, int meta) { 16 | this.item = item; 17 | this.meta = meta; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if(o == this) 23 | return true; 24 | if(!(o instanceof MetaItem)) 25 | return false; 26 | MetaItem mi = (MetaItem)o; 27 | return mi.item.equals(item) && mi.meta == meta; 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return Objects.hash(item, meta); 33 | } 34 | 35 | @Override 36 | public void addTag(Tag tag) { 37 | TagDelegate.getDelegate(this).addTag(tag); 38 | } 39 | 40 | @Override 41 | public boolean isTag(Tag tag) { 42 | return TagDelegate.getDelegate(this).isTag(tag); 43 | } 44 | 45 | @Override 46 | public void removeTag(Tag tag) { 47 | TagDelegate.getDelegate(this).removeTag(tag); 48 | } 49 | 50 | @Override 51 | public Stream getTags() { 52 | return TagDelegate.getDelegate(this).getTags(); 53 | } 54 | 55 | @Override 56 | public Class getTagDelegateType() { 57 | return MetaItem.class; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "MetaItem[item=" + item.getRegistryName() + ",meta=" + meta + "]"; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/Tag.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.google.common.base.CaseFormat; 5 | import com.google.common.collect.Interner; 6 | import com.google.common.collect.Interners; 7 | import net.minecraft.util.ResourceLocation; 8 | 9 | import javax.annotation.Nonnull; 10 | 11 | /** 12 | * Tags can be used to categorize registry entries. Some registry entries (like blocks and items) have special logic 13 | * to allow you to put a tag on all meta values, otherwise they are just applied to the registry entry directly. 14 | * 15 | * You can access the tagging facilities of registry entries using {@link ITaggable#of(Object)}. 16 | */ 17 | public final class Tag extends ResourceLocation { 18 | private static final Interner INTERNER = Interners.newStrongInterner(); 19 | Tag(String namespaceIn, String pathIn) { 20 | super(namespaceIn, pathIn); 21 | } 22 | 23 | @Override 24 | public int hashCode() { 25 | return 38 * this.namespace.hashCode() * this.path.hashCode(); 26 | } 27 | 28 | @Override 29 | public String toString() 30 | { 31 | return '#' + this.namespace + ':' + this.path; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object other) { 36 | if(this == other) { 37 | return true; 38 | } 39 | return super.equals(other) && (other instanceof Tag); 40 | } 41 | 42 | public static Tag of(@Nonnull ResourceLocation tagId) { 43 | return (Tag)INTERNER.intern(new Tag(tagId.getNamespace(), tagId.getPath())); 44 | } 45 | 46 | public static Tag oredict(@Nonnull String oreName) { 47 | String reformattedName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, oreName); 48 | return of(new ResourceLocation(Millennium.MODID, "oredict/" + reformattedName)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/TagDelegate.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import com.google.common.collect.HashMultimap; 4 | 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * A default implementation of ITaggable to help registry entries that don't want to provide it for themselves. 12 | */ 13 | public class TagDelegate implements ITaggable { 14 | private static final HashMap, TagDelegate> DELEGATES = new HashMap<>(); 15 | private static final HashMultimap> DELEGATES_HOLDING_TAG = HashMultimap.create(); 16 | private final Class delegateType; 17 | private final Set tags = new HashSet<>(); 18 | 19 | /** 20 | * Do not use this TagDelegate instance for anything in your mod, use ITaggable directly. 21 | */ 22 | @SuppressWarnings("unchecked") 23 | public static TagDelegate getDelegate(ITaggable taggable) { 24 | return (TagDelegate) DELEGATES.computeIfAbsent(taggable, t -> new TagDelegate<>(t.getTagDelegateType())); 25 | } 26 | 27 | /** 28 | * Remove a tag from every object that has it. 29 | * @param tag The tag to remove 30 | */ 31 | public static void removeFromAll(Tag tag) { 32 | /* Create a new Set since delegates will be removed in the first place */ 33 | Set> delegates = new HashSet<>(DELEGATES_HOLDING_TAG.get(tag)); 34 | delegates.forEach(delegate -> delegate.removeTag(tag)); 35 | } 36 | 37 | TagDelegate(Class delegateType) { 38 | this.delegateType = delegateType; 39 | } 40 | 41 | @Override 42 | public void addTag(Tag tag) { 43 | tags.add(tag); 44 | DELEGATES_HOLDING_TAG.put(tag, this); 45 | } 46 | 47 | @Override 48 | public void removeTag(Tag tag) { 49 | tags.remove(tag); 50 | DELEGATES_HOLDING_TAG.remove(tag, this); 51 | } 52 | 53 | @Override 54 | public boolean isTag(Tag tag) { 55 | return tags.contains(tag); 56 | } 57 | 58 | @Override 59 | public Stream getTags() { 60 | return tags.stream(); 61 | } 62 | 63 | /** 64 | * DO NOT USE this unless you have a good reason to, it exists to help internal optimization only. 65 | */ 66 | public Set getTagSet() { 67 | return tags; 68 | } 69 | 70 | @Override 71 | public Class getTagDelegateType() { 72 | return delegateType; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/TagEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.state.IBlockState; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.item.Item; 7 | import net.minecraft.item.ItemBlock; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.util.math.RayTraceResult; 11 | import net.minecraft.util.text.TextFormatting; 12 | import net.minecraftforge.client.event.RenderGameOverlayEvent; 13 | import net.minecraftforge.event.entity.player.ItemTooltipEvent; 14 | import net.minecraftforge.fml.common.Mod; 15 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 16 | import net.minecraftforge.oredict.OreDictionary; 17 | 18 | import java.util.ArrayList; 19 | 20 | @Mod.EventBusSubscriber 21 | public class TagEventHandler { 22 | 23 | private static final ArrayList oreDictTags = new ArrayList<>(); 24 | 25 | @SuppressWarnings({"unchecked", "rawtypes"}) 26 | public static void processExistingOredicts() { 27 | for(Tag tag : oreDictTags) { 28 | TagDelegate.removeFromAll(tag); 29 | } 30 | oreDictTags.clear(); 31 | for(String oreName : OreDictionary.getOreNames()) { 32 | for(ItemStack stack : OreDictionary.getOres(oreName)) { 33 | processOredictEntry(stack, oreName); 34 | } 35 | } 36 | } 37 | 38 | public static void processOredictEntry(ItemStack stack, String name) { 39 | Item item = stack.getItem(); 40 | int meta = stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? -1 : stack.getItemDamage(); 41 | Tag tag = Tag.oredict(name); 42 | ITaggable.of(stack).addTag(tag); 43 | assert ITaggable.of(new ItemStack(item, 1, meta == -1 ? 0 : meta)).isTag(tag); 44 | oreDictTags.add(tag); 45 | if(item instanceof ItemBlock) { 46 | Block block = ((ItemBlock)item).getBlock(); 47 | /* This is really not ideal... but what else can one do? */ 48 | for(IBlockState state : block.getBlockState().getValidStates()) { 49 | if(meta == -1 || meta == block.damageDropped(state)) { 50 | ITaggable.of(state).addTag(tag); 51 | } 52 | } 53 | } 54 | } 55 | 56 | @SubscribeEvent 57 | public static void onRenderRight(RenderGameOverlayEvent.Text event) { 58 | Minecraft mc = Minecraft.getMinecraft(); 59 | if (mc.gameSettings.showDebugInfo && mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK && mc.objectMouseOver.getBlockPos() != null) { 60 | BlockPos blockpos = mc.objectMouseOver.getBlockPos(); 61 | IBlockState state = mc.world.getBlockState(blockpos); 62 | ITaggable.of(state).getTags().forEach(tag -> { 63 | event.getRight().add(tag.toString()); 64 | }); 65 | } 66 | } 67 | 68 | @SubscribeEvent 69 | public static void onTooltipEvent(ItemTooltipEvent event) { 70 | ItemStack stack = event.getItemStack(); 71 | if(event.getFlags().isAdvanced() && !stack.isEmpty()) { 72 | ITaggable.of(stack).getTags().forEach(tag -> { 73 | event.getToolTip().add(TextFormatting.DARK_GRAY + "" + tag.toString()); 74 | }); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tag/TagHelpers.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tag; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | import java.util.stream.Stream; 6 | 7 | public final class TagHelpers { 8 | TagHelpers() { 9 | 10 | } 11 | public static Stream streamFromSubObjects(Iterable> subobjects) { 12 | Set tagSet = null; 13 | for(ITaggable subobject : subobjects) { 14 | Set stateTags = ((TagDelegate)subobject).getTagSet(); 15 | if(!stateTags.isEmpty()){ 16 | stateTags.forEach(tag -> System.out.println(tag)); 17 | } 18 | if(tagSet == null) 19 | tagSet = new HashSet<>(stateTags); 20 | else 21 | tagSet.retainAll(stateTags); 22 | } 23 | if(tagSet != null) 24 | return tagSet.stream(); 25 | else 26 | return Stream.empty(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tileentity/BarrelBlockTileEntity.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tileentity; 2 | 3 | import com.cleanroommc.millennium.client.sounds.BarrelSoundEvents; 4 | import com.cleanroommc.millennium.common.blocks.BarrelBlock; 5 | import com.cleanroommc.millennium.common.blocks.MillenniumBlocks; 6 | import com.cleanroommc.millennium.common.container.BarrelContainer; 7 | import net.minecraft.block.state.IBlockState; 8 | import net.minecraft.entity.player.EntityPlayer; 9 | import net.minecraft.entity.player.InventoryPlayer; 10 | import net.minecraft.inventory.Container; 11 | import net.minecraft.inventory.ItemStackHelper; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.nbt.NBTTagCompound; 14 | import net.minecraft.tileentity.TileEntityLockableLoot; 15 | import net.minecraft.util.NonNullList; 16 | import net.minecraft.util.SoundCategory; 17 | import net.minecraft.util.math.BlockPos; 18 | import net.minecraft.world.World; 19 | 20 | public class BarrelBlockTileEntity extends TileEntityLockableLoot { 21 | public int playerUsingCount = 0; 22 | public NonNullList inventory = NonNullList.withSize(27, ItemStack.EMPTY); 23 | 24 | @Override 25 | protected NonNullList getItems() { 26 | return inventory; 27 | } 28 | 29 | @Override 30 | public int getSizeInventory() { 31 | return inventory.size(); 32 | } 33 | 34 | @Override 35 | public boolean isEmpty() { 36 | for (ItemStack itemstack : this.inventory) if (!itemstack.isEmpty()) return false; 37 | 38 | return true; 39 | } 40 | 41 | @Override 42 | public boolean receiveClientEvent(int id, int type) { 43 | IBlockState state = world.getBlockState(pos); 44 | 45 | if (type > 0 && !state.getValue(BarrelBlock.OPEN)) { 46 | world.setBlockState(pos, state.withProperty(BarrelBlock.OPEN, true), 3); 47 | world.markBlockRangeForRenderUpdate(pos, pos); 48 | world.playSound(null, pos, BarrelSoundEvents.OPEN, SoundCategory.BLOCKS, 1F, 1F); 49 | } else { 50 | world.setBlockState(pos, state.withProperty(BarrelBlock.OPEN, false), 3); 51 | world.markBlockRangeForRenderUpdate(pos, pos); 52 | world.playSound(null, pos, BarrelSoundEvents.CLOSE, SoundCategory.BLOCKS, 1F, 1F); 53 | } 54 | 55 | return true; 56 | } 57 | 58 | @Override 59 | public void openInventory(EntityPlayer player) { 60 | playerUsingCount++; 61 | world.addBlockEvent(this.pos, MillenniumBlocks.BARREL_BLOCK, 1, this.playerUsingCount); 62 | } 63 | 64 | @Override 65 | public void closeInventory(EntityPlayer player) { 66 | playerUsingCount--; 67 | world.addBlockEvent(this.pos, MillenniumBlocks.BARREL_BLOCK, 1, this.playerUsingCount); 68 | } 69 | 70 | @Override 71 | public int getInventoryStackLimit() { 72 | return 64; 73 | } 74 | 75 | @Override 76 | public Container createContainer(InventoryPlayer playerInventory, EntityPlayer playerIn) { 77 | return new BarrelContainer(playerInventory, this, playerIn); 78 | } 79 | 80 | @Override 81 | public String getGuiID() { 82 | return "millennium:barrel"; 83 | } 84 | 85 | @Override 86 | public String getName() { 87 | return "container.barrel"; 88 | } 89 | 90 | @Override 91 | public boolean shouldRefresh( 92 | World world, BlockPos pos, IBlockState oldState, IBlockState newSate) { 93 | return oldState.getBlock() != newSate.getBlock(); 94 | } 95 | 96 | @Override 97 | public NBTTagCompound writeToNBT(NBTTagCompound compound) { 98 | super.writeToNBT(compound); 99 | if (!checkLootAndWrite(compound)) ItemStackHelper.saveAllItems(compound, inventory); 100 | return compound; 101 | } 102 | 103 | @Override 104 | public void readFromNBT(NBTTagCompound compound) { 105 | super.readFromNBT(compound); 106 | inventory = NonNullList.withSize(getSizeInventory(), ItemStack.EMPTY); 107 | if (!checkLootAndRead(compound)) ItemStackHelper.loadAllItems(compound, inventory); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tileentity/BlastFurnaceTileEntity.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tileentity; 2 | 3 | import net.minecraft.item.ItemStack; 4 | import net.minecraft.tileentity.TileEntityFurnace; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class BlastFurnaceTileEntity extends TileEntityFurnace { 9 | @Override 10 | public int getCookTime(@Nonnull ItemStack stack) { 11 | return 100; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/tileentity/MillenniumSignTileEntity.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.tileentity; 2 | 3 | import net.minecraft.block.state.IBlockState; 4 | import net.minecraft.item.EnumDyeColor; 5 | import net.minecraft.nbt.NBTTagCompound; 6 | import net.minecraft.tileentity.TileEntitySign; 7 | 8 | public class MillenniumSignTileEntity extends TileEntitySign { 9 | public boolean isGlowingText; 10 | public EnumDyeColor textColor; 11 | 12 | public boolean setColor(EnumDyeColor color) { 13 | if (color == textColor) return false; 14 | else { 15 | IBlockState state = world.getBlockState(pos); 16 | textColor = color; 17 | markDirty(); 18 | world.notifyBlockUpdate(pos, state, state, 3); 19 | return true; 20 | } 21 | } 22 | 23 | @Override 24 | public NBTTagCompound writeToNBT(NBTTagCompound compound) { 25 | super.writeToNBT(compound); 26 | compound.setBoolean("GlowingText", isGlowingText); 27 | if (textColor != null) compound.setString("Color", textColor.getName()); 28 | return compound; 29 | } 30 | 31 | @Override 32 | public void readFromNBT(NBTTagCompound compound) { 33 | super.readFromNBT(compound); 34 | isGlowingText = compound.getBoolean("GlowingText"); 35 | String textColorName = compound.getString("Color"); 36 | 37 | textColor = textColorName.isEmpty() ? null : EnumDyeColor.valueOf(textColorName.toUpperCase()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/util/ChunkPosHelper.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.util; 2 | 3 | import net.minecraft.util.math.ChunkPos; 4 | 5 | import java.util.Spliterator; 6 | import java.util.Spliterators; 7 | import java.util.function.Consumer; 8 | import java.util.stream.Stream; 9 | import java.util.stream.StreamSupport; 10 | 11 | public final class ChunkPosHelper { 12 | /** 13 | * Stream a set of chunk positions. 14 | * @param center The central chunk 15 | * @param radiusChunks radius of chunks around 16 | */ 17 | public static Stream streamSquare(ChunkPos center, int radiusChunks) { 18 | return streamSquare(new ChunkPos(center.x - radiusChunks, center.z - radiusChunks), new ChunkPos(center.x + radiusChunks, center.z + radiusChunks)); 19 | } 20 | 21 | /** 22 | * Stream a set of chunk positions. 23 | * @param start the starting position 24 | * @param end the ending position 25 | * @return 26 | */ 27 | public static Stream streamSquare(ChunkPos start, ChunkPos end) { 28 | int width = Math.abs(end.x - start.x) + 1; 29 | int height = Math.abs(end.z - start.z) + 1; 30 | int xDelta = end.x > start.x ? 1 : -1; 31 | int zDelta = end.z > start.z ? 1 : -1; 32 | return StreamSupport.stream(new Spliterators.AbstractSpliterator((long)width * height, Spliterator.NONNULL | Spliterator.SIZED | Spliterator.IMMUTABLE) { 33 | private ChunkPos currentPosition = null; 34 | @Override 35 | public boolean tryAdvance(Consumer consumer) { 36 | if(currentPosition == null) { 37 | currentPosition = start; 38 | } else { 39 | if(currentPosition.x == end.x) { 40 | if(currentPosition.z == end.z) { 41 | /* Reached the end on the X and Z axes */ 42 | return false; 43 | } else 44 | currentPosition = new ChunkPos(start.x, currentPosition.z + zDelta); 45 | } else 46 | currentPosition = new ChunkPos(currentPosition.x + xDelta, currentPosition.z); 47 | } 48 | consumer.accept(currentPosition); 49 | return true; 50 | } 51 | }, false); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/util/ICachedTeleporter.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.util; 2 | 3 | public interface ICachedTeleporter { 4 | void clearAllDimensionCoordCaches(); 5 | void clearDimensionCoordCache(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/common/util/VoxelShape.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.common.util; 2 | 3 | import net.minecraft.util.math.AxisAlignedBB; 4 | 5 | /** 6 | * Works like {@link net.minecraft.util.shape.VoxelShape}, construct the class with pixel-scaled 7 | * parameters. 8 | */ 9 | public class VoxelShape extends AxisAlignedBB { 10 | public static final VoxelShape EMPTY = new VoxelShape(0, 0, 0, 0, 0, 0); 11 | 12 | /** 13 | * This constructor will automatically divide all parameters with 16 to satisfy AxisAlignedBB's 14 | * actual scale. 15 | * 16 | * @param x1 x start pos 17 | * @param y1 y start pos 18 | * @param z1 z start pos 19 | * @param x2 x end pos 20 | * @param y2 y end pos 21 | * @param z2 z end pos 22 | */ 23 | public VoxelShape(double x1, double y1, double z1, double x2, double y2, double z2) { 24 | super(x1 / 16, y1 / 16, z1 / 16, x2 / 16, y2 / 16, z2 / 16); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/core/MillenniumCore.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.core; 2 | 3 | import com.google.common.collect.Lists; 4 | import net.minecraftforge.common.ForgeVersion; 5 | import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; 6 | import zone.rong.mixinbooter.IEarlyMixinLoader; 7 | 8 | import javax.annotation.Nullable; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @IFMLLoadingPlugin.Name("Millennium|Core") 13 | @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) 14 | public class MillenniumCore implements IFMLLoadingPlugin, IEarlyMixinLoader { 15 | @Override 16 | public String[] getASMTransformerClass() { 17 | return new String[0]; 18 | } 19 | 20 | @Override 21 | public String getModContainerClass() { 22 | return null; 23 | } 24 | 25 | @Nullable 26 | @Override 27 | public String getSetupClass() { 28 | return null; 29 | } 30 | 31 | @Override 32 | public void injectData(Map data) { 33 | } 34 | 35 | @Override 36 | public String getAccessTransformerClass() { 37 | return null; 38 | } 39 | 40 | @Override 41 | public List getMixinConfigs() { 42 | return Lists.newArrayList("mixin.millennium.json"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/BlockMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.ITaggable; 4 | import com.cleanroommc.millennium.common.tag.Tag; 5 | import com.cleanroommc.millennium.common.tag.TagHelpers; 6 | import com.google.common.collect.ImmutableList; 7 | import net.minecraft.block.Block; 8 | import net.minecraft.block.state.BlockStateContainer; 9 | import net.minecraft.block.state.IBlockState; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | 13 | import java.util.stream.Stream; 14 | 15 | @Mixin(Block.class) 16 | public abstract class BlockMixin implements ITaggable { 17 | @Shadow public abstract BlockStateContainer getBlockState(); 18 | 19 | /** 20 | * Add a tag for each of the block's states. 21 | */ 22 | @Override 23 | public void addTag(Tag tag) { 24 | for(IBlockState state : getBlockState().getValidStates()) { 25 | ((ITaggable)state).addTag(tag); 26 | } 27 | } 28 | 29 | /** 30 | * A block only has a tag if all of its states do. 31 | */ 32 | @Override 33 | public boolean isTag(Tag tag) { 34 | for(IBlockState state : getBlockState().getValidStates()) { 35 | if(!((ITaggable)state).isTag(tag)) 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | /** 42 | * Remove a tag from each of the block's states. 43 | */ 44 | @Override 45 | public void removeTag(Tag tag) { 46 | for(IBlockState state : getBlockState().getValidStates()) { 47 | ((ITaggable)state).removeTag(tag); 48 | } 49 | } 50 | 51 | @Override 52 | @SuppressWarnings("unchecked") 53 | public Stream getTags() { 54 | return TagHelpers.streamFromSubObjects((ImmutableList>)(Object)getBlockState().getValidStates()); 55 | } 56 | 57 | @Override 58 | public Class getTagDelegateType() { 59 | return Block.class; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/BlockPortalMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.util.ICachedTeleporter; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockPortal; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.world.World; 9 | import net.minecraft.world.WorldServer; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(BlockPortal.class) 17 | public class BlockPortalMixin { 18 | @Inject(method = "trySpawnPortal", at = @At("RETURN")) 19 | private void clearCacheEntriesWhenSpawning(World worldIn, BlockPos pos, CallbackInfoReturnable cir) { 20 | if(!worldIn.isRemote && cir.getReturnValue()) { 21 | ((ICachedTeleporter)((WorldServer)worldIn).getDefaultTeleporter()).clearAllDimensionCoordCaches(); 22 | } 23 | } 24 | 25 | @Inject(method = "neighborChanged", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", shift = At.Shift.AFTER)) 26 | private void clearCacheEntries(IBlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos, CallbackInfo ci) { 27 | if(!worldIn.isRemote) 28 | ((ICachedTeleporter)((WorldServer)worldIn).getDefaultTeleporter()).clearAllDimensionCoordCaches(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/BlockStateBaseMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.ITaggable; 4 | import com.cleanroommc.millennium.common.tag.Tag; 5 | import com.cleanroommc.millennium.common.tag.TagDelegate; 6 | import net.minecraft.block.state.BlockStateBase; 7 | import net.minecraft.block.state.IBlockState; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | 10 | import java.util.stream.Stream; 11 | 12 | @Mixin(BlockStateBase.class) 13 | public class BlockStateBaseMixin implements ITaggable { 14 | @Override 15 | public void addTag(Tag tag) { 16 | TagDelegate.getDelegate(this).addTag(tag); 17 | } 18 | 19 | @Override 20 | public boolean isTag(Tag tag) { 21 | return TagDelegate.getDelegate(this).isTag(tag); 22 | } 23 | 24 | @Override 25 | public void removeTag(Tag tag) { 26 | TagDelegate.getDelegate(this).removeTag(tag); 27 | } 28 | 29 | @Override 30 | public Stream getTags() { 31 | return TagDelegate.getDelegate(this).getTags(); 32 | } 33 | 34 | @Override 35 | public Class getTagDelegateType() { 36 | return IBlockState.class; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/ContainerMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.client.sounds.BundleSoundEvents; 4 | import com.cleanroommc.millennium.common.items.BundleItem; 5 | import net.minecraft.entity.player.EntityPlayer; 6 | import net.minecraft.inventory.ClickType; 7 | import net.minecraft.inventory.Container; 8 | import net.minecraft.inventory.Slot; 9 | import net.minecraft.item.ItemStack; 10 | import net.minecraft.util.SoundCategory; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | import java.util.List; 18 | 19 | @Mixin(Container.class) 20 | public class ContainerMixin { 21 | 22 | @Shadow public List inventorySlots; 23 | 24 | @Inject(method = "slotClick", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/entity/player/InventoryPlayer;getItemStack()Lnet/minecraft/item/ItemStack;", ordinal = 8), cancellable = true) 25 | private void injectBundleCheck(int slotId, int dragType, ClickType clickType, EntityPlayer player, CallbackInfoReturnable cir) { 26 | ItemStack playerStack = player.inventory.getItemStack(); 27 | Slot slot = this.inventorySlots.get(slotId); 28 | if (dragType == 1 && playerStack.getItem() instanceof BundleItem) { 29 | ItemStack slotStack = slot.getStack(); 30 | if (slotStack.isEmpty()) { 31 | ItemStack removedStack = BundleItem.removeOne(playerStack); 32 | if (!removedStack.isEmpty()) { 33 | player.world.playSound( 34 | null, 35 | player.posX, 36 | player.posY, 37 | player.posZ, 38 | BundleSoundEvents.REMOVE_ONE, 39 | SoundCategory.PLAYERS, 40 | 0.8F, 41 | (float) (0.8 + player.world.rand.nextFloat() * 0.4F)); 42 | slot.putStack(removedStack); 43 | BundleItem.addOne(playerStack, slot.getStack()); 44 | cir.setReturnValue(ItemStack.EMPTY); 45 | } 46 | } 47 | } 48 | ItemStack slotStack = this.inventorySlots.get(slotId).getStack(); 49 | if (dragType == 0 && slotStack.getItem() instanceof BundleItem) { 50 | if (!playerStack.isEmpty()) { 51 | int remainder = BundleItem.addOne(slotStack, playerStack); 52 | player.world.playSound( 53 | null, 54 | player.posX, 55 | player.posY, 56 | player.posZ, 57 | BundleSoundEvents.INSERT, 58 | SoundCategory.PLAYERS, 59 | 0.8F, 60 | (float) (0.8 + player.world.rand.nextFloat() * 0.4F)); 61 | if (remainder > 0) { 62 | playerStack.shrink(remainder); 63 | } 64 | cir.setReturnValue(ItemStack.EMPTY); 65 | } 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/EntityLivingBaseMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.blocks.MillenniumBlocks; 4 | import com.cleanroommc.millennium.common.blocks.ScaffoldingBlock; 5 | import net.minecraft.block.state.IBlockState; 6 | import net.minecraft.entity.Entity; 7 | import net.minecraft.entity.EntityLivingBase; 8 | import net.minecraft.util.math.AxisAlignedBB; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.util.math.MathHelper; 11 | import net.minecraft.world.World; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | import java.util.function.Predicate; 19 | 20 | @Mixin(EntityLivingBase.class) 21 | public abstract class EntityLivingBaseMixin extends Entity { 22 | public EntityLivingBaseMixin(World worldIn) { 23 | super(worldIn); 24 | } 25 | 26 | @Inject( 27 | method = "isOnLadder", 28 | at = @At(value = "RETURN", ordinal = 1), 29 | cancellable = true) 30 | private void injectScaffoldingClimbingCheck(CallbackInfoReturnable cir) { 31 | if (isInScaffolding(world, getEntityBoundingBox())) { 32 | cir.setReturnValue(true); 33 | } 34 | } 35 | 36 | @Inject( 37 | method = "onLivingUpdate", 38 | at = 39 | @At( 40 | value = "INVOKE_ASSIGN", 41 | target = "net/minecraft/entity/EntityLivingBase.isInWater ()Z", 42 | shift = At.Shift.BEFORE)) 43 | private void injectScaffoldingClimbing(CallbackInfo ci) { 44 | if (isInScaffolding(world, getEntityBoundingBox())) { 45 | motionY = 0.2; 46 | } 47 | } 48 | 49 | private boolean isInScaffolding(World world, AxisAlignedBB bb) { 50 | int minX = MathHelper.floor(bb.minX); 51 | int maxX = MathHelper.ceil(bb.maxX); 52 | int minY = MathHelper.floor(bb.minY); 53 | int maxY = MathHelper.ceil(bb.maxY); 54 | int minZ = MathHelper.floor(bb.minZ); 55 | int maxZ = MathHelper.ceil(bb.maxZ); 56 | BlockPos.PooledMutableBlockPos pooled = BlockPos.PooledMutableBlockPos.retain(); 57 | 58 | for (int l3 = minX; l3 < maxX; ++l3) { 59 | for (int i4 = minY; i4 < maxY; ++i4) { 60 | for (int j4 = minZ; j4 < maxZ; ++j4) { 61 | IBlockState state = world.getBlockState(pooled.setPos(l3, i4, j4)); 62 | if (state.getBlock() instanceof ScaffoldingBlock) { 63 | pooled.release(); 64 | return true; 65 | } 66 | } 67 | } 68 | } 69 | 70 | pooled.release(); 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/ForgeRegistryEntryImplMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.ITaggable; 4 | import com.cleanroommc.millennium.common.tag.Tag; 5 | import com.cleanroommc.millennium.common.tag.TagDelegate; 6 | import net.minecraftforge.registries.IForgeRegistryEntry; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | import java.util.stream.Stream; 11 | 12 | @Mixin(IForgeRegistryEntry.Impl.class) 13 | public abstract class ForgeRegistryEntryImplMixin> implements ITaggable { 14 | @Shadow public abstract Class getRegistryType(); 15 | 16 | @Override 17 | public void addTag(Tag tag) { 18 | TagDelegate.getDelegate(this).addTag(tag); 19 | } 20 | 21 | @Override 22 | public boolean isTag(Tag tag) { 23 | return TagDelegate.getDelegate(this).isTag(tag); 24 | } 25 | 26 | @Override 27 | public void removeTag(Tag tag) { 28 | TagDelegate.getDelegate(this).removeTag(tag); 29 | } 30 | 31 | @Override 32 | public Stream getTags() { 33 | return TagDelegate.getDelegate(this).getTags(); 34 | } 35 | 36 | @Override 37 | public Class getTagDelegateType() { 38 | return this.getRegistryType(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/ItemMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.ITaggable; 4 | import com.cleanroommc.millennium.common.tag.Tag; 5 | import com.cleanroommc.millennium.common.tag.TagHelpers; 6 | import com.google.common.collect.ImmutableList; 7 | import net.minecraft.block.state.IBlockState; 8 | import net.minecraft.creativetab.CreativeTabs; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.item.ItemStack; 11 | import net.minecraft.util.NonNullList; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | 15 | import java.util.stream.Stream; 16 | 17 | @Mixin(Item.class) 18 | public abstract class ItemMixin implements ITaggable { 19 | 20 | @Shadow public abstract void getSubItems(CreativeTabs tab, NonNullList items); 21 | 22 | private NonNullList mil$getAllItemStacks() { 23 | NonNullList items = NonNullList.create(); 24 | getSubItems(CreativeTabs.SEARCH, items); 25 | return items; 26 | } 27 | @Override 28 | public void addTag(Tag tag) { 29 | for(ItemStack stack : mil$getAllItemStacks()) { 30 | ITaggable.of(stack).addTag(tag); 31 | } 32 | } 33 | 34 | @Override 35 | public boolean isTag(Tag tag) { 36 | for(ItemStack stack : mil$getAllItemStacks()) { 37 | if(!ITaggable.of(stack).isTag(tag)) 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | @Override 44 | public void removeTag(Tag tag) { 45 | for(ItemStack stack : mil$getAllItemStacks()) { 46 | ITaggable.of(stack).removeTag(tag); 47 | } 48 | } 49 | 50 | @Override 51 | @SuppressWarnings("unchecked") 52 | public Stream getTags() { 53 | return TagHelpers.streamFromSubObjects((ImmutableList>)(Object)mil$getAllItemStacks()); 54 | } 55 | 56 | @Override 57 | public Class getTagDelegateType() { 58 | return Item.class; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/ItemStackMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.ITaggable; 4 | import com.cleanroommc.millennium.common.tag.MetaItem; 5 | import com.cleanroommc.millennium.common.tag.Tag; 6 | import net.minecraft.item.Item; 7 | import net.minecraft.item.ItemStack; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | 11 | import java.util.stream.Stream; 12 | 13 | @Mixin(ItemStack.class) 14 | public abstract class ItemStackMixin implements ITaggable { 15 | @Shadow public abstract Item getItem(); 16 | 17 | @Shadow public abstract int getItemDamage(); 18 | 19 | private MetaItem mil$toMetaItem() { 20 | return new MetaItem(this.getItem(), this.getItemDamage()); 21 | } 22 | @Override 23 | public void addTag(Tag tag) { 24 | mil$toMetaItem().addTag(tag); 25 | } 26 | 27 | @Override 28 | public boolean isTag(Tag tag) { 29 | return mil$toMetaItem().isTag(tag); 30 | } 31 | 32 | @Override 33 | public void removeTag(Tag tag) { 34 | mil$toMetaItem().removeTag(tag); 35 | } 36 | 37 | @Override 38 | public Stream getTags() { 39 | return mil$toMetaItem().getTags(); 40 | } 41 | 42 | @Override 43 | public Class getTagDelegateType() { 44 | return ItemStack.class; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.client.font.TextRenderer; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.gui.FontRenderer; 6 | import net.minecraft.client.renderer.texture.TextureManager; 7 | import net.minecraft.client.settings.GameSettings; 8 | import net.minecraft.util.ResourceLocation; 9 | import net.minecraftforge.fml.relauncher.Side; 10 | import net.minecraftforge.fml.relauncher.SideOnly; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | @Mixin(Minecraft.class) 16 | @SideOnly(Side.CLIENT) 17 | public abstract class MinecraftClientMixin { 18 | @Redirect(method = "init", at = @At(value = "NEW", target = "net/minecraft/client/gui/FontRenderer")) 19 | private FontRenderer redirectFontRendererCtor(GameSettings k1, ResourceLocation l1, TextureManager j, boolean k) { 20 | return new TextRenderer(k1, l1, j, k); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/OreDictionaryMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.tag.TagEventHandler; 4 | import net.minecraftforge.oredict.OreDictionary; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(OreDictionary.class) 11 | public class OreDictionaryMixin { 12 | @Inject(method = "rebakeMap", at = @At("TAIL"), remap = false) 13 | private static void handleTags(CallbackInfo ci) { 14 | TagEventHandler.processExistingOredicts(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/SignBlockMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.items.MillenniumItems; 4 | import com.cleanroommc.millennium.common.tileentity.MillenniumSignTileEntity; 5 | import net.minecraft.block.BlockSign; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.item.EnumDyeColor; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.item.ItemDye; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.tileentity.TileEntity; 13 | import net.minecraft.util.EnumFacing; 14 | import net.minecraft.util.EnumHand; 15 | import net.minecraft.util.math.BlockPos; 16 | import net.minecraft.world.World; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 21 | 22 | @Mixin(BlockSign.class) 23 | public abstract class SignBlockMixin { 24 | @Inject(method = "createNewTileEntity", at = @At(value = "HEAD"), cancellable = true) 25 | private void injectTileEntity(World worldIn, int meta, CallbackInfoReturnable cir) { 26 | cir.setReturnValue(new MillenniumSignTileEntity()); 27 | } 28 | 29 | @Inject(method = "onBlockActivated", at = @At(value = "HEAD"), cancellable = true) 30 | private void injectOnBlockActivated( 31 | World worldIn, 32 | BlockPos pos, 33 | IBlockState state, 34 | EntityPlayer playerIn, 35 | EnumHand hand, 36 | EnumFacing facing, 37 | float hitX, 38 | float hitY, 39 | float hitZ, 40 | CallbackInfoReturnable cir) { 41 | TileEntity te = worldIn.getTileEntity(pos); 42 | 43 | if (te instanceof MillenniumSignTileEntity) { 44 | MillenniumSignTileEntity mte = (MillenniumSignTileEntity) te; 45 | ItemStack stack = playerIn.getHeldItem(hand); 46 | Item item = stack.getItem(); 47 | 48 | if (stack.getItem() instanceof ItemDye) { 49 | ItemDye dye = (ItemDye) item; 50 | 51 | mte.setColor(EnumDyeColor.byDyeDamage(dye.getDamage(stack))); 52 | 53 | cir.setReturnValue(true); 54 | } else if (stack.getItem().equals(MillenniumItems.GLOW_INK_SAC)) { 55 | mte.isGlowingText = true; 56 | 57 | cir.setReturnValue(true); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/SignTileEntityRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.client.font.TextRenderer; 4 | import com.cleanroommc.millennium.common.tileentity.MillenniumSignTileEntity; 5 | import net.minecraft.block.Block; 6 | import net.minecraft.client.gui.FontRenderer; 7 | import net.minecraft.client.renderer.tileentity.TileEntitySignRenderer; 8 | import net.minecraft.tileentity.TileEntitySign; 9 | import net.minecraft.util.text.ITextComponent; 10 | import net.minecraftforge.fml.relauncher.Side; 11 | import net.minecraftforge.fml.relauncher.SideOnly; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.ModifyArg; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 18 | 19 | import java.util.List; 20 | 21 | @Mixin(TileEntitySignRenderer.class) 22 | @SideOnly(Side.CLIENT) 23 | public abstract class SignTileEntityRendererMixin { 24 | private final ThreadLocal te = new ThreadLocal<>(); 25 | 26 | @Inject( 27 | method = "render(Lnet/minecraft/tileentity/TileEntitySign;DDDFIF)V", 28 | at = @At(value = "HEAD")) 29 | private void injectTileEntity( 30 | TileEntitySign te, 31 | double x, 32 | double y, 33 | double z, 34 | float partialTicks, 35 | int destroyStage, 36 | float alpha, 37 | CallbackInfo ci) { 38 | if (te instanceof MillenniumSignTileEntity) { 39 | this.te.set((MillenniumSignTileEntity) te); 40 | } 41 | } 42 | 43 | @SuppressWarnings("InvalidInjectorMethodSignature") 44 | @Inject( 45 | method = "render(Lnet/minecraft/tileentity/TileEntitySign;DDDFIF)V", 46 | at = 47 | @At( 48 | value = "INVOKE", 49 | target = "Lnet/minecraft/client/gui/FontRenderer;drawString(Ljava/lang/String;III)I", 50 | ordinal = 1), 51 | locals = LocalCapture.CAPTURE_FAILSOFT) 52 | private void injectColoredTextRendering( 53 | TileEntitySign te, 54 | double x, 55 | double y, 56 | double z, 57 | float partialTicks, 58 | int destroyStage, 59 | float alpha, 60 | CallbackInfo ci, 61 | Block block, 62 | float f, 63 | FontRenderer fontrenderer, 64 | float f3, 65 | int i, 66 | int j, 67 | ITextComponent itextcomponent, 68 | List list, 69 | String s) { 70 | // te is always MillenniumSignTileEntity (probably not), fontrenderer is always TextRenderer 71 | if (te instanceof MillenniumSignTileEntity) { 72 | MillenniumSignTileEntity mte = (MillenniumSignTileEntity) te; 73 | 74 | if (mte.textColor != null && mte.isGlowingText) { 75 | TextRenderer tr = (TextRenderer) fontrenderer; 76 | 77 | tr.setDrawOutline(true); 78 | tr.drawString( 79 | s, 80 | -tr.getStringWidth(s) / 2, 81 | j * 10 - te.signText.length * 5, 82 | TextRenderer.getOutlineColor(mte.textColor)); 83 | tr.setDrawOutline(false); 84 | } 85 | } 86 | } 87 | 88 | @ModifyArg( 89 | method = "render(Lnet/minecraft/tileentity/TileEntitySign;DDDFIF)V", 90 | at = 91 | @At( 92 | value = "INVOKE", 93 | target = "Lnet/minecraft/client/gui/FontRenderer;drawString(Ljava/lang/String;III)I", 94 | ordinal = 1), 95 | index = 3) 96 | private int modifyRenderingArgs(int x) { 97 | return te.get() != null 98 | ? te.get().textColor != null ? te.get().textColor.getColorValue() : x 99 | : x; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/TeleporterMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.common.util.ICachedTeleporter; 4 | import com.cleanroommc.millennium.poi.IPOICapability; 5 | import com.cleanroommc.millennium.poi.PointOfInterest; 6 | import com.cleanroommc.millennium.poi.PointOfInterestHelper; 7 | import com.cleanroommc.millennium.poi.PointOfInterestType; 8 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 9 | import net.minecraft.entity.Entity; 10 | import net.minecraft.init.Blocks; 11 | import net.minecraft.server.MinecraftServer; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.ChunkPos; 14 | import net.minecraft.util.math.MathHelper; 15 | import net.minecraft.world.Teleporter; 16 | import net.minecraft.world.WorldServer; 17 | import net.minecraft.world.border.WorldBorder; 18 | import net.minecraft.world.chunk.Chunk; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Overwrite; 22 | import org.spongepowered.asm.mixin.Shadow; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 26 | 27 | import java.util.Comparator; 28 | import java.util.Optional; 29 | 30 | @Mixin(Teleporter.class) 31 | public abstract class TeleporterMixin implements ICachedTeleporter { 32 | @Shadow @Final protected Long2ObjectMap destinationCoordinateCache; 33 | 34 | @Shadow @Final protected WorldServer world; 35 | 36 | /** 37 | * @author embeddedt 38 | * @reason Don't clear old coordinates until the cache gets huge. 39 | */ 40 | @Overwrite 41 | public void removeStalePortalLocations(long worldTime) { 42 | if (worldTime % 100L == 0L) 43 | { 44 | if(this.destinationCoordinateCache.size() > 65000) { 45 | this.clearDimensionCoordCache(); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public void clearAllDimensionCoordCaches() 52 | { 53 | MinecraftServer server = world.getMinecraftServer(); 54 | if(server == null) 55 | return; 56 | for(int i = 0; i < server.worlds.length; ++i) { 57 | ((ICachedTeleporter)server.worlds[i].getDefaultTeleporter()).clearDimensionCoordCache(); 58 | } 59 | } 60 | 61 | @Override 62 | public void clearDimensionCoordCache() { 63 | this.destinationCoordinateCache.clear(); 64 | } 65 | 66 | /** 67 | * @author embeddedt 68 | * @reason Speed up searching for existing portals by using the POI system instead of scanning blockstates. 69 | * Also fixes vanilla issues with being teleported outside the world border. 70 | */ 71 | @Inject(method = "placeInExistingPortal", at = @At("HEAD"), cancellable = true) 72 | private void overridePortalSearch(Entity entityIn, float rotationYaw, CallbackInfoReturnable cir) { 73 | int flooredEntityX = MathHelper.floor(entityIn.posX); 74 | int flooredEntityZ = MathHelper.floor(entityIn.posZ); 75 | long cachePosKey = ChunkPos.asLong(flooredEntityX, flooredEntityZ); 76 | Teleporter teleporterThis = (Teleporter) (Object) this; 77 | if(!this.destinationCoordinateCache.containsKey(cachePosKey)) { 78 | BlockPos entityPosition = new BlockPos(entityIn); 79 | WorldBorder border = world.getWorldBorder(); 80 | 81 | /* Use 1.16 logic */ 82 | int searchRadius = world.provider.getDimensionType().getId() == -1 ? 16 : 128; 83 | Optional potentialPortal = PointOfInterestHelper.getPOIsOfType(this.world, entityPosition, searchRadius, poi -> poi.getType() == PointOfInterestType.NETHER_PORTAL) 84 | /* check world border */ 85 | .filter(poi -> border.contains(poi.getPos())) 86 | /* sort for nearest and lowest block */ 87 | .sorted(Comparator.comparingDouble(poi -> poi.getPos().distanceSq(entityPosition)).thenComparingInt(poi -> poi.getPos().getY())) 88 | .filter(poi -> world.getBlockState(poi.getPos()).getBlock() == Blocks.PORTAL) 89 | .findFirst(); 90 | 91 | if(potentialPortal.isPresent()) 92 | { 93 | this.destinationCoordinateCache.put(cachePosKey, teleporterThis.new PortalPosition(potentialPortal.get().getPos(), this.world.getTotalWorldTime())); 94 | } else { 95 | // No portal found 96 | cir.cancel(); 97 | cir.setReturnValue(false); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/mixin/WorldMixin.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.mixin; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.network.POIUpdateMessage; 5 | import com.cleanroommc.millennium.poi.IPOICapability; 6 | import com.cleanroommc.millennium.poi.PointOfInterest; 7 | import com.cleanroommc.millennium.poi.PointOfInterestType; 8 | import net.minecraft.block.state.IBlockState; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.World; 11 | import net.minecraft.world.WorldProvider; 12 | import net.minecraft.world.chunk.Chunk; 13 | import net.minecraftforge.common.util.Constants; 14 | import net.minecraftforge.fml.common.network.NetworkRegistry; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | 22 | @Mixin(World.class) 23 | public abstract class WorldMixin { 24 | 25 | @Shadow @Final public boolean isRemote; 26 | 27 | @Shadow @Final public WorldProvider provider; 28 | 29 | @Shadow public abstract Chunk getChunk(BlockPos pos); 30 | 31 | @Inject(method = "markAndNotifyBlock", at = @At("TAIL"), remap = false) 32 | private void setPOIInfo(BlockPos pos, Chunk chunk, IBlockState state, IBlockState newState, int flags, CallbackInfo ci) { 33 | /* 'chunk' can be null sometimes */ 34 | chunk = this.getChunk(pos); 35 | IPOICapability cap = IPOICapability.get(chunk); 36 | if(cap != null) { 37 | PointOfInterestType poiType = PointOfInterestType.forState(newState); 38 | PointOfInterest oldPoi = cap.getPOIs().get(pos.toLong()); 39 | PointOfInterestType oldPoiType = oldPoi != null ? oldPoi.getType() : null; 40 | if(oldPoiType != poiType) { 41 | cap.setPOI(pos.toLong(), poiType, true); 42 | System.out.println(pos + " = " + poiType); 43 | chunk.markDirty(); 44 | if ((flags & Constants.BlockFlags.SEND_TO_CLIENTS) != 0 && !this.isRemote) { 45 | /* Send update to clients */ 46 | POIUpdateMessage msg = new POIUpdateMessage(pos.toLong(), poiType); 47 | Millennium.CHANNEL.sendToAllAround(msg, new NetworkRegistry.TargetPoint(this.provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 64)); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/network/POIBulkUpdateMessage.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.network; 2 | 3 | import com.cleanroommc.millennium.poi.IPOICapability; 4 | import com.cleanroommc.millennium.poi.PointOfInterest; 5 | import com.cleanroommc.millennium.poi.PointOfInterestType; 6 | import io.netty.buffer.ByteBuf; 7 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.util.math.ChunkPos; 10 | import net.minecraft.world.chunk.Chunk; 11 | import net.minecraftforge.fml.common.network.simpleimpl.IMessage; 12 | import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; 13 | import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; 14 | import org.apache.commons.lang3.tuple.Pair; 15 | 16 | import javax.annotation.Nonnull; 17 | import java.util.HashSet; 18 | import java.util.Set; 19 | 20 | public class POIBulkUpdateMessage implements IMessage { 21 | @Nonnull 22 | public Set> data = new HashSet<>(); 23 | public int chunkX, chunkZ; 24 | 25 | public POIBulkUpdateMessage() { 26 | 27 | } 28 | 29 | public POIBulkUpdateMessage(@Nonnull ChunkPos chunkPos, @Nonnull Long2ObjectMap map) { 30 | map.forEach((pos, poi) -> data.add(Pair.of(pos, poi != null ? poi.getType().serialize() : -1))); 31 | chunkX = chunkPos.x; 32 | chunkZ = chunkPos.z; 33 | } 34 | 35 | @Override 36 | public void fromBytes(ByteBuf buf) { 37 | chunkX = buf.readInt(); 38 | chunkZ = buf.readInt(); 39 | int size = buf.readInt(); 40 | for(int i = 0; i < size; i++) { 41 | long pos = buf.readLong(); 42 | int poiId = buf.readInt(); 43 | data.add(Pair.of(pos, poiId)); 44 | } 45 | } 46 | 47 | @Override 48 | public void toBytes(ByteBuf buf) { 49 | buf.writeInt(chunkX); 50 | buf.writeInt(chunkZ); 51 | buf.writeInt(data.size()); 52 | data.forEach(pair -> { 53 | buf.writeLong(pair.getLeft()); 54 | buf.writeInt(pair.getRight()); 55 | }); 56 | } 57 | 58 | public enum Handler implements IMessageHandler { 59 | INSTANCE; 60 | 61 | @Override 62 | public IMessage onMessage(POIBulkUpdateMessage message, MessageContext ctx) { 63 | Minecraft.getMinecraft().addScheduledTask(() -> { 64 | Chunk chunk = Minecraft.getMinecraft().world.getChunk(message.chunkX, message.chunkZ); 65 | IPOICapability cap = IPOICapability.get(chunk); 66 | if(cap != null) { 67 | cap.clearPOIs(); 68 | message.data.forEach(pair -> { 69 | long pos = pair.getLeft(); 70 | PointOfInterestType poi = PointOfInterestType.deserialize(pair.getRight()); 71 | cap.setPOI(pos, poi, false); 72 | }); 73 | chunk.markDirty(); 74 | } 75 | }); 76 | return null; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/network/POIUpdateMessage.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.network; 2 | 3 | import com.cleanroommc.millennium.poi.IPOICapability; 4 | import com.cleanroommc.millennium.poi.PointOfInterestType; 5 | import io.netty.buffer.ByteBuf; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.world.chunk.Chunk; 9 | import net.minecraftforge.fml.common.network.simpleimpl.IMessage; 10 | import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; 11 | import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; 12 | 13 | public class POIUpdateMessage implements IMessage { 14 | public long pos; 15 | public int poiId; 16 | 17 | public POIUpdateMessage() { 18 | 19 | } 20 | 21 | public POIUpdateMessage(long pos, PointOfInterestType poi) { 22 | this.pos = pos; 23 | this.poiId = poi != null ? poi.serialize() : -1; 24 | } 25 | 26 | @Override 27 | public void fromBytes(ByteBuf buf) { 28 | pos = buf.readLong(); 29 | poiId = buf.readInt(); 30 | } 31 | 32 | @Override 33 | public void toBytes(ByteBuf buf) { 34 | buf.writeLong(pos); 35 | buf.writeInt(poiId); 36 | } 37 | 38 | public enum Handler implements IMessageHandler { 39 | INSTANCE; 40 | 41 | @Override 42 | public IMessage onMessage(POIUpdateMessage message, MessageContext ctx) { 43 | Minecraft.getMinecraft().addScheduledTask(() -> { 44 | BlockPos pos = BlockPos.fromLong(message.pos); 45 | Chunk chunk = Minecraft.getMinecraft().world.getChunk(pos); 46 | IPOICapability cap = IPOICapability.get(chunk); 47 | if(cap != null) { 48 | PointOfInterestType poi = PointOfInterestType.deserialize(message.poiId); 49 | cap.setPOI(message.pos, poi, true); 50 | chunk.markDirty(); 51 | } 52 | }); 53 | return null; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/poi/IPOICapability.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.poi; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.poi.event.POIEvent; 5 | import com.google.common.collect.HashMultimap; 6 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 7 | import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; 8 | import net.minecraft.nbt.NBTBase; 9 | import net.minecraft.nbt.NBTTagCompound; 10 | import net.minecraft.nbt.NBTTagList; 11 | import net.minecraft.util.EnumFacing; 12 | import net.minecraft.util.ResourceLocation; 13 | import net.minecraft.util.math.BlockPos; 14 | import net.minecraft.world.chunk.Chunk; 15 | import net.minecraftforge.common.MinecraftForge; 16 | import net.minecraftforge.common.capabilities.Capability; 17 | import net.minecraftforge.common.capabilities.CapabilityInject; 18 | import net.minecraftforge.common.capabilities.ICapabilityProvider; 19 | import net.minecraftforge.common.capabilities.ICapabilitySerializable; 20 | import net.minecraftforge.common.util.Constants; 21 | import net.minecraftforge.fml.common.registry.GameRegistry; 22 | import net.minecraftforge.registries.IForgeRegistry; 23 | 24 | import javax.annotation.Nonnull; 25 | import javax.annotation.Nullable; 26 | import java.util.Map; 27 | import java.util.Set; 28 | import java.util.stream.Collectors; 29 | 30 | /** 31 | * Used to store the locations of points-of-interest in the world. 32 | */ 33 | public interface IPOICapability { 34 | @CapabilityInject(IPOICapability.class) 35 | Capability CAPABILITY = null; 36 | 37 | @Nonnull Long2ObjectMap getPOIs(); 38 | 39 | @Nonnull Set getAllLocationsOfType(PointOfInterestType poi); 40 | 41 | @Nonnull Set getAllPOIsOfType(PointOfInterestType poi); 42 | 43 | Chunk getChunk(); 44 | 45 | 46 | void setPOI(long pos, PointOfInterestType poiType, boolean fireEvents); 47 | 48 | void clearPOIs(); 49 | 50 | boolean needsRescan(); 51 | void setNeedsRescan(boolean flag); 52 | 53 | @SuppressWarnings("ConstantConditions") 54 | @Nullable 55 | static IPOICapability get(@Nullable Chunk p) { 56 | if(p == null) 57 | return null; 58 | return p.getCapability(CAPABILITY, null); 59 | } 60 | 61 | //default implementation 62 | class Impl implements IPOICapability 63 | { 64 | private final Chunk chunk; 65 | private boolean needsRescan = true; 66 | 67 | public Impl() { 68 | throw new IllegalStateException("Do not use this constructor"); 69 | } 70 | 71 | public Impl(Chunk chunk) { 72 | this.chunk = chunk; 73 | } 74 | @Nonnull 75 | protected final Long2ObjectMap pointsOfInterest = new Long2ObjectOpenHashMap<>(); 76 | 77 | protected final HashMultimap poiLocations = HashMultimap.create(); 78 | 79 | @Override 80 | public Chunk getChunk() { 81 | return chunk; 82 | } 83 | 84 | @Nonnull 85 | @Override 86 | public Long2ObjectMap getPOIs() { return pointsOfInterest; } 87 | 88 | @Override 89 | public void setPOI(long pos, PointOfInterestType poiType, boolean fireEvents) { 90 | if(poiType == null) { 91 | PointOfInterest oldPoi = pointsOfInterest.get(pos); 92 | if(fireEvents && oldPoi != null) { 93 | MinecraftForge.EVENT_BUS.post(new POIEvent.RemovedEvent(oldPoi)); 94 | } 95 | setPOIObject(pos, null, null); 96 | } else { 97 | PointOfInterest poi = poiType.createPOI(chunk.getWorld(), BlockPos.fromLong(pos), 0); 98 | setPOIObject(pos, poiType, poi); 99 | if(fireEvents) 100 | MinecraftForge.EVENT_BUS.post(new POIEvent.AddedEvent(poi)); 101 | } 102 | } 103 | 104 | @Override 105 | public void setNeedsRescan(boolean needsRescan) { 106 | this.needsRescan = needsRescan; 107 | } 108 | 109 | @Override 110 | public boolean needsRescan() { 111 | return this.needsRescan; 112 | } 113 | 114 | protected void setPOIObject(long pos, PointOfInterestType poiType, PointOfInterest poi) { 115 | if(poiType == null) { 116 | PointOfInterest oldPoi = pointsOfInterest.get(pos); 117 | if(oldPoi != null) { 118 | pointsOfInterest.remove(pos); 119 | poiLocations.remove(oldPoi.getType(), pos); 120 | } 121 | } else { 122 | pointsOfInterest.put(pos, poi); 123 | poiLocations.put(poiType, pos); 124 | } 125 | } 126 | 127 | @Override 128 | public void clearPOIs() { 129 | pointsOfInterest.clear(); 130 | poiLocations.clear(); 131 | } 132 | 133 | @Nonnull 134 | @Override 135 | public Set getAllLocationsOfType(PointOfInterestType poi) { 136 | return poiLocations.get(poi).stream().map(BlockPos::fromLong).collect(Collectors.toSet()); 137 | } 138 | 139 | @Nonnull 140 | @Override 141 | public Set getAllPOIsOfType(PointOfInterestType poi) { 142 | return poiLocations.get(poi).stream().map(pointsOfInterest::get).collect(Collectors.toSet()); 143 | } 144 | } 145 | 146 | @SuppressWarnings("ConstantConditions") 147 | final class Provider implements ICapabilitySerializable 148 | { 149 | final IPOICapability instance; 150 | final Chunk chunk; 151 | 152 | public Provider(Chunk chunk) { 153 | this.chunk = chunk; 154 | this.instance = new Impl(chunk); 155 | } 156 | 157 | @Override 158 | public boolean hasCapability(@Nullable Capability capabilityIn, @Nullable EnumFacing facing) { 159 | return capabilityIn == CAPABILITY; 160 | } 161 | 162 | @Nullable 163 | @Override 164 | public T getCapability(@Nullable Capability capabilityIn, @Nullable EnumFacing facing) { 165 | if(!hasCapability(capabilityIn, facing)) return null; 166 | else return CAPABILITY.cast(instance); 167 | } 168 | 169 | @Nonnull 170 | @Override 171 | public NBTBase serializeNBT() { return CAPABILITY.writeNBT(instance, null); } 172 | 173 | @Override 174 | public void deserializeNBT(@Nonnull NBTBase nbt) { CAPABILITY.readNBT(instance, null, nbt); } 175 | } 176 | 177 | enum Storage implements Capability.IStorage 178 | { 179 | INSTANCE; 180 | 181 | @Nonnull 182 | @Override 183 | public NBTBase writeNBT(@Nullable Capability capability, @Nonnull IPOICapability instance, @Nullable EnumFacing side) { 184 | final NBTTagCompound storage = new NBTTagCompound(); 185 | final NBTTagList list = new NBTTagList(); 186 | 187 | for(Map.Entry entry : instance.getPOIs().entrySet()) { 188 | NBTTagCompound nbt = new NBTTagCompound(); 189 | nbt = entry.getValue().writeToNBT(nbt); 190 | list.appendTag(nbt); 191 | } 192 | 193 | IForgeRegistry typeRegistry = GameRegistry.findRegistry(PointOfInterestType.class); 194 | storage.setInteger("Hash", typeRegistry.getKeys().hashCode()); 195 | storage.setTag("Entries", list); 196 | 197 | return storage; 198 | } 199 | 200 | @Override 201 | public void readNBT(@Nullable Capability capability, @Nonnull IPOICapability instance, @Nullable EnumFacing side, @Nullable NBTBase nbtIn) { 202 | boolean shouldRescan = true; 203 | if(nbtIn instanceof NBTTagCompound) { 204 | instance.clearPOIs(); 205 | shouldRescan = false; 206 | IForgeRegistry typeRegistry = GameRegistry.findRegistry(PointOfInterestType.class); 207 | NBTTagCompound storage = (NBTTagCompound)nbtIn; 208 | NBTTagList entries = storage.getTagList("Entries", Constants.NBT.TAG_COMPOUND); 209 | for(NBTBase tag : entries) { 210 | if(tag instanceof NBTTagCompound) { 211 | NBTTagCompound nbt = (NBTTagCompound)tag; 212 | if(nbt.hasKey("id", Constants.NBT.TAG_STRING)) { 213 | ResourceLocation id = new ResourceLocation(nbt.getString("id")); 214 | PointOfInterestType type = typeRegistry.getValue(id); 215 | if(type != null) { 216 | PointOfInterest poi = type.createPOIEmpty(); 217 | poi.readFromNBT(instance.getChunk().getWorld(), nbt); 218 | ((Impl)instance).setPOIObject(poi.getPos().toLong(), poi.getType(), poi); 219 | } 220 | } 221 | } 222 | } 223 | if(!storage.hasKey("Hash", Constants.NBT.TAG_INT) || storage.getInteger("Hash") != typeRegistry.getKeys().hashCode()) 224 | shouldRescan = true; 225 | } 226 | if(shouldRescan) { 227 | PointOfInterestHelper.rescanPOIs(instance.getChunk()); 228 | } 229 | instance.setNeedsRescan(false); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/poi/PointOfInterest.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.poi; 2 | 3 | import net.minecraft.nbt.NBTTagCompound; 4 | import net.minecraft.util.ResourceLocation; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.World; 7 | import net.minecraftforge.fml.common.registry.GameRegistry; 8 | 9 | import java.util.function.Predicate; 10 | 11 | /** 12 | * Represents a POI at a given location in the world. 13 | */ 14 | public class PointOfInterest { 15 | private World world; 16 | private PointOfInterestType poiType; 17 | private BlockPos pos; 18 | private int reservations; 19 | 20 | public static final Predicate ANY = poi -> true; 21 | 22 | public PointOfInterest(World world) { 23 | this.world = world; 24 | this.poiType = null; 25 | this.pos = null; 26 | } 27 | 28 | public PointOfInterest(PointOfInterestType type, World world, BlockPos pos) { 29 | this(type, world, pos, 0); 30 | } 31 | public PointOfInterest(PointOfInterestType type, World world, BlockPos pos, int reservations) { 32 | this.poiType = type; 33 | this.world = world; 34 | this.pos = pos; 35 | this.reservations = reservations; 36 | } 37 | 38 | public PointOfInterestType getType() { 39 | return poiType; 40 | } 41 | 42 | public BlockPos getPos() { 43 | return pos; 44 | } 45 | 46 | /** 47 | * Get the number of reservations currently held on this POI. 48 | */ 49 | public int getReservations() { 50 | return reservations; 51 | } 52 | 53 | /** 54 | * Try to reserve this POI. 55 | */ 56 | public boolean tryReserve() { 57 | if(this.reservations >= getType().getMaxReservations()) { 58 | return false; 59 | } 60 | this.reservations++; 61 | return true; 62 | } 63 | 64 | /** 65 | * Try to release this POI. 66 | */ 67 | public boolean tryRelease() { 68 | if(this.reservations <= 0) { 69 | return false; 70 | } 71 | this.reservations--; 72 | return true; 73 | } 74 | 75 | public NBTTagCompound writeToNBT(NBTTagCompound nbt) { 76 | nbt.setLong("pos", pos.toLong()); 77 | nbt.setString("id", String.valueOf(getType().getRegistryName())); 78 | nbt.setInteger("reserved", reservations); 79 | return nbt; 80 | } 81 | 82 | public void readFromNBT(World world, NBTTagCompound nbt) { 83 | pos = BlockPos.fromLong(nbt.getLong("pos")); 84 | ResourceLocation poiLoc = new ResourceLocation(nbt.getString("id")); 85 | PointOfInterestType poiType = GameRegistry.findRegistry(PointOfInterestType.class).getValue(poiLoc); 86 | if(poiType != null) 87 | this.poiType = poiType; 88 | else 89 | throw new IllegalArgumentException("Unexpected POI type: " + poiLoc); 90 | this.world = world; 91 | this.reservations = nbt.getInteger("reserved"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/poi/PointOfInterestHelper.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.poi; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.common.util.ChunkPosHelper; 5 | import com.cleanroommc.millennium.network.POIUpdateMessage; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.util.math.ChunkPos; 9 | import net.minecraft.world.World; 10 | import net.minecraft.world.chunk.Chunk; 11 | import net.minecraft.world.chunk.IChunkProvider; 12 | import net.minecraftforge.common.util.Constants; 13 | import net.minecraftforge.fml.common.network.NetworkRegistry; 14 | 15 | import java.util.function.Predicate; 16 | import java.util.stream.Stream; 17 | 18 | public class PointOfInterestHelper { 19 | public static Stream getInChunk(World world, ChunkPos chunkPos, Predicate filter) { 20 | IChunkProvider provider = world.getChunkProvider(); 21 | if(provider.isChunkGeneratedAt(chunkPos.x, chunkPos.z)) { 22 | IPOICapability cap = IPOICapability.get(provider.provideChunk(chunkPos.x, chunkPos.z)); 23 | if(cap != null) { 24 | return cap.getPOIs().values().stream().filter(filter); 25 | } 26 | } 27 | return Stream.empty(); 28 | } 29 | public static Stream getPOIsOfType(World world, BlockPos center, int radius, Predicate filter) { 30 | int chunkRadius = Math.floorDiv(radius, 16) + 1; 31 | return ChunkPosHelper.streamSquare(new ChunkPos(center), chunkRadius).flatMap(chunkPos -> getInChunk(world, chunkPos, filter)).filter(poi -> { 32 | BlockPos poiPos = poi.getPos(); 33 | return Math.abs(poiPos.getX() - center.getX()) <= radius && Math.abs(poiPos.getZ() - center.getZ()) <= radius; 34 | }); 35 | } 36 | 37 | /** 38 | * Rescan the POI information for all blocks in a chunk. 39 | * @param chunk The chunk to scan 40 | */ 41 | public static void rescanPOIs(Chunk chunk) { 42 | BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); 43 | for(int y = 0; y < chunk.getWorld().getHeight(); y++) { 44 | for(int z = 0; z < 16; z++) { 45 | for(int x = 0; x < 16; x++) { 46 | pos.setPos(x, y, z); 47 | rescanPOIAtLocation(chunk, pos, chunk.getBlockState(pos), true); 48 | } 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * Rescan the POI information at a given location in a chunk. 55 | * @param chunk The chunk to scan 56 | * @param pos A location at which the POI information should be recomputed 57 | * @param newState The state to consider the block at this location as having. 58 | * @param sendToClients Whether or not to send the POI update to clients. 59 | */ 60 | public static void rescanPOIAtLocation(Chunk chunk, BlockPos pos, IBlockState newState, boolean sendToClients) { 61 | IPOICapability cap = IPOICapability.get(chunk); 62 | if(cap != null) { 63 | PointOfInterestType poiType = PointOfInterestType.forState(newState); 64 | PointOfInterest oldPoi = cap.getPOIs().get(pos.toLong()); 65 | PointOfInterestType oldPoiType = oldPoi != null ? oldPoi.getType() : null; 66 | if(oldPoiType != poiType) { 67 | cap.setPOI(pos.toLong(), poiType, true); 68 | System.out.println(pos + " changed to " + poiType); 69 | chunk.markDirty(); 70 | if (sendToClients && !chunk.getWorld().isRemote) { 71 | /* Send update to clients */ 72 | POIUpdateMessage msg = new POIUpdateMessage(pos.toLong(), poiType); 73 | Millennium.CHANNEL.sendToAllAround(msg, new NetworkRegistry.TargetPoint(chunk.getWorld().provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 64)); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/poi/PointOfInterestType.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.poi; 2 | 3 | import com.cleanroommc.millennium.Millennium; 4 | import com.cleanroommc.millennium.network.POIBulkUpdateMessage; 5 | import com.google.common.base.Joiner; 6 | import com.google.common.base.Predicate; 7 | import com.google.common.collect.ImmutableSet; 8 | import net.minecraft.block.Block; 9 | import net.minecraft.block.state.IBlockState; 10 | import net.minecraft.init.Blocks; 11 | import net.minecraft.util.ResourceLocation; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.world.World; 14 | import net.minecraft.world.chunk.Chunk; 15 | import net.minecraftforge.event.AttachCapabilitiesEvent; 16 | import net.minecraftforge.event.RegistryEvent; 17 | import net.minecraftforge.event.world.ChunkEvent; 18 | import net.minecraftforge.event.world.ChunkWatchEvent; 19 | import net.minecraftforge.fml.common.Mod; 20 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 21 | import net.minecraftforge.fml.common.registry.GameRegistry; 22 | import net.minecraftforge.registries.*; 23 | 24 | import javax.annotation.Nonnull; 25 | import javax.annotation.Nullable; 26 | import java.util.HashMap; 27 | import java.util.Set; 28 | 29 | /** 30 | * Represents a block which needs to be found quickly within a world. 31 | */ 32 | @Mod.EventBusSubscriber 33 | public class PointOfInterestType extends IForgeRegistryEntry.Impl { 34 | private static IForgeRegistry REGISTRY = null; 35 | private static final ResourceLocation POI_CAP = new ResourceLocation(Millennium.MODID, "poi"); 36 | private static final ResourceLocation STATE_TO_POI = new ResourceLocation(Millennium.MODID, "state_to_poi"); 37 | 38 | @GameRegistry.ObjectHolder(Millennium.MODID + ":nether_portal") 39 | public static final PointOfInterestType NETHER_PORTAL = null; 40 | 41 | public static Set allStatesOfBlock(Block block) { 42 | return ImmutableSet.copyOf(block.getBlockState().getValidStates()); 43 | } 44 | 45 | private final Set validStates; 46 | private final int maxReservations; 47 | 48 | public PointOfInterestType(Set states, int maxReservations) { 49 | this.validStates = states; 50 | this.maxReservations = maxReservations; 51 | } 52 | 53 | public Set getStates() { 54 | return validStates; 55 | } 56 | 57 | public int getMaxReservations() { 58 | return maxReservations; 59 | } 60 | 61 | public PointOfInterest createPOI(World world, BlockPos pos, int reservations) { 62 | return new PointOfInterest(this, world, pos, reservations); 63 | } 64 | 65 | public PointOfInterest createPOIEmpty() { 66 | return new PointOfInterest(null); 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return getRegistryName().toString() + "[" + Joiner.on(",").join(validStates) + "]"; 72 | } 73 | 74 | public static PointOfInterestType forState(IBlockState state) { 75 | @SuppressWarnings("unchecked") 76 | HashMap poiMap = REGISTRY.getSlaveMap(STATE_TO_POI, HashMap.class); 77 | return poiMap.get(state); 78 | } 79 | 80 | public int serialize() { 81 | return ((ForgeRegistry)REGISTRY).getID(this); 82 | } 83 | 84 | public static PointOfInterestType deserialize(int id) { 85 | if(id == -1) 86 | return null; 87 | return ((ForgeRegistry)REGISTRY).getValue(id); 88 | } 89 | 90 | private static class POICallbacks implements IForgeRegistry.CreateCallback, IForgeRegistry.AddCallback, IForgeRegistry.ClearCallback { 91 | @Override 92 | public void onAdd(IForgeRegistryInternal owner, RegistryManager stage, int id, PointOfInterestType poi, @Nullable PointOfInterestType oldPoi) { 93 | @SuppressWarnings("unchecked") 94 | HashMap poiMap = owner.getSlaveMap(STATE_TO_POI, HashMap.class); 95 | if(oldPoi != null) { 96 | for(IBlockState state : oldPoi.getStates()) { 97 | poiMap.remove(state); 98 | } 99 | } 100 | for(IBlockState state : poi.getStates()) { 101 | poiMap.put(state, poi); 102 | } 103 | } 104 | 105 | @Override 106 | public void onCreate(IForgeRegistryInternal owner, RegistryManager stage) { 107 | owner.setSlaveMap(STATE_TO_POI, new HashMap()); 108 | } 109 | 110 | @Override 111 | public void onClear(IForgeRegistryInternal owner, RegistryManager stage) { 112 | owner.getSlaveMap(STATE_TO_POI, HashMap.class).clear(); 113 | } 114 | } 115 | 116 | 117 | @SubscribeEvent 118 | public static void setupRegistry(RegistryEvent.NewRegistry event) { 119 | REGISTRY = new RegistryBuilder() 120 | .setType(PointOfInterestType.class) 121 | .setName(POI_CAP) 122 | .addCallback(new POICallbacks()) 123 | .create(); 124 | } 125 | 126 | @SubscribeEvent 127 | public static void registerDefaults(RegistryEvent.Register event) { 128 | REGISTRY.register(new PointOfInterestType(PointOfInterestType.allStatesOfBlock(Blocks.PORTAL), 0).setRegistryName("nether_portal")); 129 | } 130 | 131 | @SubscribeEvent 132 | public static void attachCap(AttachCapabilitiesEvent event) { 133 | event.addCapability(POI_CAP, new IPOICapability.Provider(event.getObject())); 134 | } 135 | 136 | @SubscribeEvent 137 | public static void sendToPlayer(@Nonnull ChunkWatchEvent.Watch event) { 138 | final @Nullable IPOICapability cap = IPOICapability.get(event.getChunkInstance()); 139 | if(cap != null) Millennium.CHANNEL.sendTo(new POIBulkUpdateMessage(event.getChunk(), cap.getPOIs()), event.getPlayer()); 140 | } 141 | 142 | @SubscribeEvent 143 | public static void rescanIfNeeded(@Nonnull ChunkEvent.Load event) { 144 | final @Nullable IPOICapability cap = IPOICapability.get(event.getChunk()); 145 | if(cap != null) { 146 | if(cap.needsRescan()) { 147 | PointOfInterestHelper.rescanPOIs(event.getChunk()); 148 | cap.setNeedsRescan(false); 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/poi/event/POIEvent.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.poi.event; 2 | 3 | import com.cleanroommc.millennium.poi.PointOfInterest; 4 | import net.minecraftforge.fml.common.eventhandler.Event; 5 | 6 | public class POIEvent extends Event { 7 | private final PointOfInterest poi; 8 | 9 | public POIEvent(PointOfInterest poi) { 10 | this.poi = poi; 11 | } 12 | 13 | public static class AddedEvent extends POIEvent { 14 | public AddedEvent(PointOfInterest poi) { 15 | super(poi); 16 | } 17 | } 18 | public static class RemovedEvent extends POIEvent { 19 | public RemovedEvent(PointOfInterest poi) { 20 | super(poi); 21 | } 22 | } 23 | public static class UpdateEvent extends POIEvent { 24 | public UpdateEvent(PointOfInterest poi) { 25 | super(poi); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/proxy/ClientProxy.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.proxy; 2 | 3 | import com.cleanroommc.millennium.client.resource.MillenniumTextures; 4 | import com.cleanroommc.millennium.Millennium; 5 | import com.cleanroommc.millennium.client.gui.GuiHandler; 6 | import com.cleanroommc.millennium.client.resource.MillenniumSounds; 7 | import net.minecraft.client.renderer.block.model.ModelResourceLocation; 8 | import net.minecraft.item.Item; 9 | import net.minecraftforge.client.model.ModelLoader; 10 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 11 | import net.minecraftforge.fml.common.network.NetworkRegistry; 12 | 13 | public class ClientProxy extends CommonProxy { 14 | @Override 15 | public void preInit(FMLPreInitializationEvent event) { 16 | super.preInit(event); 17 | NetworkRegistry.INSTANCE.registerGuiHandler(Millennium.INSTANCE, new GuiHandler()); 18 | MillenniumSounds.initSounds(); 19 | MillenniumTextures.initTextures(); 20 | } 21 | 22 | @Override 23 | public void registerItemRenderer(Item item, int meta, String id) { 24 | ModelLoader.setCustomModelResourceLocation( 25 | item, meta, new ModelResourceLocation(item.getRegistryName(), id)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/cleanroommc/millennium/proxy/CommonProxy.java: -------------------------------------------------------------------------------- 1 | package com.cleanroommc.millennium.proxy; 2 | 3 | import com.cleanroommc.millennium.poi.IPOICapability; 4 | import net.minecraft.item.Item; 5 | import net.minecraftforge.common.capabilities.CapabilityManager; 6 | import net.minecraftforge.fml.common.event.FMLInitializationEvent; 7 | import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; 8 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 9 | 10 | public class CommonProxy { 11 | public void preInit(FMLPreInitializationEvent event) { 12 | CapabilityManager.INSTANCE.register(IPOICapability.class, IPOICapability.Storage.INSTANCE, IPOICapability.Impl::new); 13 | } 14 | 15 | public void init(FMLInitializationEvent event) {} 16 | 17 | public void postInit(FMLPostInitializationEvent event) {} 18 | 19 | public void registerItemRenderer(Item item, int meta, String id) {} 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/blockstates/barrel.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=down,open=false": { 4 | "x": 180, 5 | "model": "millennium:barrel" 6 | }, 7 | "facing=down,open=true": { 8 | "x": 180, 9 | "model": "millennium:barrel_open" 10 | }, 11 | "facing=east,open=false": { 12 | "x": 90, 13 | "y": 90, 14 | "model": "millennium:barrel" 15 | }, 16 | "facing=east,open=true": { 17 | "x": 90, 18 | "y": 90, 19 | "model": "millennium:barrel_open" 20 | }, 21 | "facing=north,open=false": { 22 | "x": 90, 23 | "model": "millennium:barrel" 24 | }, 25 | "facing=north,open=true": { 26 | "x": 90, 27 | "model": "millennium:barrel_open" 28 | }, 29 | "facing=south,open=false": { 30 | "x": 90, 31 | "y": 180, 32 | "model": "millennium:barrel" 33 | }, 34 | "facing=south,open=true": { 35 | "x": 90, 36 | "y": 180, 37 | "model": "millennium:barrel_open" 38 | }, 39 | "facing=up,open=false": { 40 | "model": "millennium:barrel" 41 | }, 42 | "facing=up,open=true": { 43 | "model": "millennium:barrel_open" 44 | }, 45 | "facing=west,open=false": { 46 | "x": 90, 47 | "y": 270, 48 | "model": "millennium:barrel" 49 | }, 50 | "facing=west,open=true": { 51 | "x": 90, 52 | "y": 270, 53 | "model": "millennium:barrel_open" 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/lang/en_us.lang: -------------------------------------------------------------------------------- 1 | item.bundle.name=Bundle 2 | item.glow_ink_sac.name=Glow Ink Sac 3 | 4 | tile.barrel.name=Barrel 5 | 6 | container.barrel=Barrel -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/block/barrel.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "millennium:block/cube_bottom_top", 3 | "textures": { 4 | "top": "millennium:block/barrel_top", 5 | "bottom": "millennium:block/barrel_bottom", 6 | "side": "millennium:block/barrel_side" 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/block/barrel_open.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "millennium:block/cube_bottom_top", 3 | "textures": { 4 | "top": "millennium:block/barrel_top_open", 5 | "bottom": "millennium:block/barrel_bottom", 6 | "side": "millennium:block/barrel_side" 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/block/cube_bottom_top.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube", 3 | "textures": { 4 | "particle": "#side", 5 | "down": "#bottom", 6 | "up": "#top", 7 | "north": "#side", 8 | "east": "#side", 9 | "south": "#side", 10 | "west": "#side" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/item/barrel.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "millennium:block/barrel" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/item/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "millennium:item/bundle" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "filled": 0.0000001 13 | }, 14 | "model": "millennium:item/bundle_filled" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/item/bundle_filled.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "millennium:item/bundle", 3 | "textures": 4 | { 5 | "layer0": "millennium:item/bundle_filled" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/models/item/glow_ink_sac.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "millennium:item/glow_ink_sac" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/recipes/barrel.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": [ 4 | "PSP", 5 | "P P", 6 | "PSP" 7 | ], 8 | "key": { 9 | "P": [ 10 | { 11 | "item": "minecraft:planks", 12 | "data": 0 13 | }, 14 | { 15 | "item": "minecraft:planks", 16 | "data": 1 17 | }, 18 | { 19 | "item": "minecraft:planks", 20 | "data": 2 21 | }, 22 | { 23 | "item": "minecraft:planks", 24 | "data": 3 25 | }, 26 | { 27 | "item": "minecraft:planks", 28 | "data": 4 29 | }, 30 | { 31 | "item": "minecraft:planks", 32 | "data": 5 33 | } 34 | ], 35 | "S": [ 36 | { 37 | "item": "minecraft:wooden_slab", 38 | "data": 0 39 | }, 40 | { 41 | "item": "minecraft:wooden_slab", 42 | "data": 1 43 | }, 44 | { 45 | "item": "minecraft:wooden_slab", 46 | "data": 2 47 | }, 48 | { 49 | "item": "minecraft:wooden_slab", 50 | "data": 3 51 | }, 52 | { 53 | "item": "minecraft:wooden_slab", 54 | "data": 4 55 | }, 56 | { 57 | "item": "minecraft:wooden_slab", 58 | "data": 5 59 | } 60 | ] 61 | }, 62 | "result": { 63 | "item": "millennium:barrel" 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/resources/assets/millennium/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "item.bundle.drop_contents": { 3 | "sounds": [ 4 | { 5 | "name": "millennium:item/bundle/drop_contents1", 6 | "pitch": 0.95 7 | }, 8 | { 9 | "name": "millennium:item/bundle/drop_contents2", 10 | "pitch": 0.95 11 | }, 12 | { 13 | "name": "millennium:item/bundle/drop_contents3", 14 | "pitch": 0.95 15 | } 16 | ] 17 | }, 18 | "item.bundle.insert": { 19 | "sounds": [ 20 | { 21 | "name": "millennium:item/bundle/insert1", 22 | "pitch": 0.95 23 | }, 24 | { 25 | "name": "millennium:item/bundle/insert1", 26 | "pitch": 1.05 27 | }, 28 | { 29 | "name": "millennium:item/bundle/insert2", 30 | "pitch": 0.95 31 | }, 32 | { 33 | "name": "millennium:item/bundle/insert2", 34 | "pitch": 1.05 35 | }, 36 | { 37 | "name": "millennium:item/bundle/insert3", 38 | "pitch": 0.95 39 | }, 40 | { 41 | "name": "millennium:item/bundle/insert3", 42 | "pitch": 1.05 43 | } 44 | ] 45 | }, 46 | "item.bundle.remove_one": { 47 | "sounds": [ 48 | { 49 | "name": "millennium:item/bundle/remove_one1", 50 | "pitch": 1.05 51 | }, 52 | { 53 | "name": "millennium:item/bundle/remove_one1", 54 | "pitch": 1.1 55 | }, 56 | { 57 | "name": "millennium:item/bundle/remove_one2", 58 | "pitch": 1.05 59 | }, 60 | { 61 | "name": "millennium:item/bundle/remove_one2", 62 | "pitch": 1.1 63 | }, 64 | { 65 | "name": "millennium:item/bundle/remove_one3", 66 | "pitch": 1.05 67 | }, 68 | { 69 | "name": "millennium:item/bundle/remove_one3", 70 | "pitch": 1.1 71 | } 72 | ] 73 | }, 74 | "block.barrel.open": { 75 | "sounds": [ 76 | { 77 | "name": "millennium:block/barrel/open1", 78 | "pitch": 1.0 79 | }, 80 | { 81 | "name": "millennium:block/barrel/open2", 82 | "pitch": 1.0 83 | } 84 | ] 85 | }, 86 | "block.barrel.close": { 87 | "sounds": [ 88 | { 89 | "name": "millennium:block/barrel/close", 90 | "pitch": 1.0 91 | } 92 | ] 93 | } 94 | } -------------------------------------------------------------------------------- /src/main/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "millennium", 4 | "name": "Millennium", 5 | "description": "Bringing vanilla features from 1.12+ => 1.12.", 6 | "version": "${version}", 7 | "mcversion": "${mcversion}", 8 | "url": "", 9 | "updateUrl": "", 10 | "authorList": ["CleanroomMC"], 11 | "credits": "Rongmario, ChAoS_UnItY", 12 | "logoFile": "", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /src/main/resources/mixin.millennium.bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "io.github.cleanroommc.millennium.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "refmap": "millennium.refmap.json", 7 | "mixins": [ 8 | "ContainerMixin", 9 | "EntityLivingBaseMixin", 10 | "SignBlockMixin" 11 | ], 12 | "client": [ 13 | "MinecraftClientMixin", 14 | "SignTileEntityRendererMixin" 15 | ] 16 | } -------------------------------------------------------------------------------- /src/main/resources/mixin.millennium.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.cleanroommc.millennium.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "refmap": "millennium.refmap.json", 7 | "mixins": [ 8 | "BlockMixin", 9 | "BlockPortalMixin", 10 | "BlockStateBaseMixin", 11 | "ContainerMixin", 12 | "EntityLivingBaseMixin", 13 | "ForgeRegistryEntryImplMixin", 14 | "ItemMixin", 15 | "ItemStackMixin", 16 | "OreDictionaryMixin", 17 | "SignBlockMixin", 18 | "TeleporterMixin", 19 | "WorldMixin" 20 | ], 21 | "client": [ 22 | "MinecraftClientMixin", 23 | "SignTileEntityRendererMixin" 24 | ] 25 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Millennium Resources", 4 | "pack_format": 3 5 | } 6 | } 7 | --------------------------------------------------------------------------------