Compare commits

..
1 Commits
Author SHA1 Message Date
Martin Robertz 99e35345d1 update 2024-07-02 23:29:08 +02:00
148 changed files with 448 additions and 15553 deletions

No files matched your search

-62
View File
@@ -1,62 +0,0 @@
name: Report Bug
description: "Report a bug."
body:
- type: markdown
attributes:
value: "A bug/crash report with sufficient information and logs to reproduce and track down."
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Fake#1234"
- type: input
id: version
attributes:
label: Mod Version
description: "What version of the Mod are you using?"
placeholder: "Example: 1.0.0"
validations:
required: true
- type: textarea
id: report
attributes:
label: Bug Report
description: "Relevant information, as well as relevant logs attached such as `logs/fml-client-latest.log."
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: dropdown
id: java
attributes:
label: Java Version
description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version.
options:
- Java 8
- Java 9
- Java 11
- Java 17
- Java 19
- Java 20
- Java 21
- Other (Please Specify)
validations:
required: true
- type: textarea
id: modlist
attributes:
label: Mod List or GTNH Pack Version
description: "List of mods, ideally a minimal reproducible set (can be retrieved from latest.log). If using GTNH please indicate pack version and any changed mods, not the entire modlist."
placeholder: "List of mods or GTNH Pack version goes here"
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
required: true
@@ -1,28 +0,0 @@
name: Feature Request
description: "Request a feature."
body:
- type: markdown
attributes:
value: "Please use this form to request features. We ask you check if it was already requested though"
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Wumpus#1234"
- type: textarea
id: request
attributes:
label: Feature Request
description: "Relevant information, as well as relevant images"
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
required: true
-1
View File
@@ -27,4 +27,3 @@ whitelist.json
*.iws
src/main/resources/mixins.*.json
*.bat
.DS_Store
-5
View File
@@ -3,8 +3,3 @@
plugins {
id 'com.gtnewhorizons.gtnhconvention'
}
minecraft {
//extraRunJvmArguments.add("-Dccc.dev.runtimePublic=true")
//extraRunJvmArguments.add("-Dccc.dev.deobfuscate=true")
}
+1
View File
@@ -1,4 +1,5 @@
// Add your dependencies here
dependencies {
api('com.github.GTNewHorizons:CodeChickenLib:1.3.0:dev')
}
+8 -57
View File
@@ -14,7 +14,7 @@ modId = CodeChickenCore
modGroup = codechicken
# Whether to use modGroup as the maven publishing group.
# When false, com.github.GTNewHorizons is used.
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
useModGroupForPublishing = false
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
@@ -39,37 +39,13 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = false
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
enableGenericInjection = true
enableGenericInjection = false
# Generate a class with a String field for the mod version named as defined below.
# If generateGradleTokenClass is empty or not missing, no such class will be generated.
@@ -79,9 +55,6 @@ generateGradleTokenClass = codechicken.core.asm.Tags
# Name of the token containing the project's current version to generate/replace.
gradleTokenVersion = VERSION
# [DEPRECATED] Mod Group replacement token.
gradleTokenGroupName =
# [DEPRECATED]
# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
# public static final String VERSION = "GRADLETOKEN_VERSION";
@@ -103,20 +76,13 @@ accessTransformersFile =
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
usesMixins = false
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
# Mixin classes will have access to "main" classes, but not the other way around.
separateMixinSourceSet =
# Adds some debug arguments like verbose output and class export.
usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
mixinsPackage =
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -142,15 +108,9 @@ minimizeShadowedDependencies = true
# If disabled, won't rename the shadowed classes.
relocateShadowedDependencies = true
# Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
# Adds the GTNH maven, CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
includeWellKnownRepositories = true
# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
# list of strings, with the acceptable keys being(case does not matter):
# cursemaven
# modrinth
excludeWellKnownRepositories =
# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
@@ -171,7 +131,7 @@ modrinthProjectId = codechickencore-unofficial
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations =
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
@@ -192,12 +152,6 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -227,6 +181,3 @@ curseForgeRelations =
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
-12
View File
@@ -1,12 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVersion=25
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+8 -7
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,7 +84,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,6 +112,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -171,6 +170,7 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -203,14 +203,15 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -3
View File
@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -70,10 +68,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-1
View File
@@ -1,5 +1,4 @@
// Add any additional repositiroes for your dependencies here
repositories {
mavenLocal()
}
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.23'
}
@@ -1,17 +1,16 @@
package codechicken.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import com.google.common.collect.ImmutableList;
@@ -23,7 +22,6 @@ import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.relauncher.CoreModManager;
@SuppressWarnings("unused")
public class ClassDiscoverer {
public IStringMatcher matcher;
@@ -37,12 +35,17 @@ public class ClassDiscoverer {
for (int i = 0; i < superclasses.length; i++)
this.superclasses[i] = superclasses[i].getName().replace('.', '/');
classes = new ArrayList<>();
classes = new ArrayList<Class<?>>();
modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader();
}
public ClassDiscoverer(Class<?>... superclasses) {
this(test -> true, superclasses);
this(new IStringMatcher() {
public boolean matches(String test) {
return true;
}
}, superclasses);
}
public ArrayList<Class<?>> findClasses() {
@@ -60,7 +63,7 @@ public class ClassDiscoverer {
byte[] bytes = Launch.classLoader.getClassBytes(classname);
if (bytes == null) return;
ClassNode cnode = ASMHelper.createClassNode(bytes, ClassReader.SKIP_CODE);
ClassNode cnode = ASMHelper.createClassNode(bytes);
for (String superclass : superclasses)
if (!cnode.interfaces.contains(superclass) && !cnode.superName.equals(superclass)) return;
@@ -83,7 +86,7 @@ public class ClassDiscoverer {
List<String> knownLibraries = ImmutableList.<String>builder().addAll(modClassLoader.getDefaultLibraries())
.addAll(CoreModManager.getLoadedCoremods()).build();
File[] minecraftSources = modClassLoader.getParentSources();
HashSet<String> searchedSources = new HashSet<>();
HashSet<String> searchedSources = new HashSet<String>();
for (File minecraftSource : minecraftSources) {
if (searchedSources.contains(minecraftSource.getAbsolutePath())) continue;
searchedSources.add(minecraftSource.getAbsolutePath());
@@ -111,18 +114,21 @@ public class ClassDiscoverer {
}
private void readFromZipFile(File file) throws IOException {
try (ZipFile zipFile = new ZipFile(file)) {
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipentry = zipEntries.nextElement();
String fullname = zipentry.getName().replace('\\', '/');
int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) {
checkAddClass(fullname);
}
FileInputStream fileinputstream = new FileInputStream(file);
ZipInputStream zipinputstream = new ZipInputStream(fileinputstream);
do {
ZipEntry zipentry = zipinputstream.getNextEntry();
if (zipentry == null) {
break;
}
}
String fullname = zipentry.getName().replace('\\', '/');
int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) {
checkAddClass(fullname);
}
} while (true);
fileinputstream.close();
}
private void readFromDirectory(File directory, File basedirectory) {
@@ -2,16 +2,12 @@ package codechicken.core;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.World;
import codechicken.core.internal.CCCEventHandler;
import codechicken.lib.colour.LocalizedColours;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.Side;
@@ -69,16 +65,4 @@ public class ClientUtils extends CommonUtils {
.replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:");
GuiModListScroll.register(mod);
}
@SideOnly(Side.CLIENT)
public static void registerLocalizedColourReloadListener() {
((IReloadableResourceManager) mc().getResourceManager())
.registerReloadListener(new IResourceManagerReloadListener() {
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
LocalizedColours.reloadLocalizedColours();
}
});
}
}
@@ -36,7 +36,6 @@ import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import cpw.mods.fml.client.GuiModList;
@@ -149,11 +148,10 @@ public class GuiModListScroll {
double dy = scroll % (height + 20);
GL11.glPushMatrix();
GL11.glTranslated(0, -dy, 0);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, 0xDDDDDD);
if (needsScroll) {
GL11.glTranslated(0, height + 20, 0);
GuiDraw.fontRenderer
.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, 0xDDDDDD);
}
GL11.glPopMatrix();
@@ -5,19 +5,32 @@ import java.lang.reflect.Field;
import com.google.common.collect.ImmutableBiMap;
import codechicken.lib.asm.ObfMapping;
import cpw.mods.fml.common.asm.transformers.AccessTransformer;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
// This AccessTransformer will make all minecraft classes public at runtime in MCP just as they are in modloader.
// You should ONLY use this when you are testing with a mod that relies on runtime publicity and doesn't include
// access transformers. Such mods are doing the wrong thing and should be fixed.
public class CodeChickenAccessTransformer extends AccessTransformer {
private static final Field f_classNameBiMap;
@SuppressWarnings("FieldMayBeFinal")
private static boolean makeAllPublic;
private static Field f_classNameBiMap;
private static Object emptyMap = ImmutableBiMap.of();
static {
public CodeChickenAccessTransformer() throws IOException {
super();
loadPublicConfig();
}
private void loadPublicConfig() {
if (ObfMapping.obfuscated) return;
makeAllPublic = CodeChickenCoreModContainer.config.getTag("dev.runtimePublic").setComment(
"Enabling this setting will make all minecraft classes public at runtime in MCP just as they are in modloader."
+ "\nYou should ONLY use this when you are testing with a mod that relies on runtime publicity and doesn't include access transformers."
+ "\nSuch mods are doing the wrong thing and should be fixed.")
.getBooleanValue(false);
if (!makeAllPublic) return;
try {
f_classNameBiMap = FMLDeobfuscatingRemapper.class.getDeclaredField("classNameBiMap");
f_classNameBiMap.setAccessible(true);
@@ -26,18 +39,16 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
}
}
public CodeChickenAccessTransformer() throws IOException {}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
boolean setPublic = name.startsWith("net.minecraft.");
boolean setPublic = makeAllPublic && name.startsWith("net.minecraft.");
if (setPublic) setClassMap(name);
bytes = super.transform(name, transformedName, bytes);
if (setPublic) restoreClassMap();
return bytes;
}
private static void restoreClassMap() {
private void restoreClassMap() {
try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap);
} catch (Exception e) {
@@ -45,7 +56,7 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
}
}
private static void setClassMap(String name) {
private void setClassMap(String name) {
try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, ImmutableBiMap.of(name.replace('.', '/'), ""));
} catch (Exception e) {
@@ -1,5 +1,6 @@
package codechicken.core.asm;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
@@ -12,6 +13,7 @@ import codechicken.core.ClientUtils;
import codechicken.core.featurehack.LiquidTextures;
import codechicken.core.internal.CCCEventHandler;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.config.ConfigFile;
import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.LoadController;
@@ -24,6 +26,14 @@ import cpw.mods.fml.common.versioning.VersionRange;
public class CodeChickenCoreModContainer extends DummyModContainer {
public static ConfigFile config;
public static void loadConfig() {
if (config == null)
config = new ConfigFile(new File(CodeChickenCorePlugin.minecraftDir, "config/CodeChickenCore.cfg"))
.setComment("CodeChickenCore configuration file.");
}
public CodeChickenCoreModContainer() {
super(getModMetadata());
}
@@ -65,7 +75,6 @@ public class CodeChickenCoreModContainer extends DummyModContainer {
public void init(FMLInitializationEvent event) {
if (event.getSide().isClient()) {
ClientUtils.enhanceSupportersList("CodeChickenCore");
ClientUtils.registerLocalizedColourReloadListener();
FMLCommonHandler.instance().bus().register(new CCCEventHandler());
MinecraftForge.EVENT_BUS.register(new CCCEventHandler());
@@ -7,50 +7,35 @@ import java.util.HashSet;
import java.util.LinkedList;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ClassHeirachyManager;
import codechicken.lib.asm.ObfMapping;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DefaultImplementationTransformer implements IClassTransformer {
private static final HashMap<String, InterfaceImpl> impls = new HashMap<>();
private static LaunchClassLoader cl = (LaunchClassLoader) ClassHeirachyManager.class.getClassLoader();
public static void registerDefaultImpl(String iname, String cname) {
if (impls.isEmpty()) {
String name = DefaultImplementationTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
private static ClassNode getClassNode(String name) {
try {
return ASMHelper.createClassNode(cl.getClassBytes(name.replace('/', '.')));
} catch (IOException e) {
throw new RuntimeException(e);
}
impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname));
}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (transformedName.startsWith("net.minecraft") || impls.isEmpty()) return bytes;
ClassNode cnode = ASMHelper.createClassNode(bytes);
boolean changed = false;
for (String iname : cnode.interfaces) {
InterfaceImpl impl = impls.get(iname);
if (impl != null) changed |= impl.patch(cnode);
}
return changed ? ASMHelper.createBytes(cnode, 0) : bytes;
}
static class InterfaceImpl {
public final String iname;
public ArrayList<MethodNode> impls = new ArrayList<>();
public ArrayList<MethodNode> impls = new ArrayList<MethodNode>();
public InterfaceImpl(String iname, String cname) {
this.iname = iname;
HashSet<String> names = new HashSet<>();
HashSet<String> names = new HashSet<String>();
ClassNode inode = getClassNode(iname);
for (MethodNode method : inode.methods) names.add(method.name + method.desc);
@@ -62,7 +47,7 @@ public class DefaultImplementationTransformer implements IClassTransformer {
}
public boolean patch(ClassNode cnode) {
LinkedList<String> names = new LinkedList<>();
LinkedList<String> names = new LinkedList<String>();
for (MethodNode method : cnode.methods) {
ObfMapping m = new ObfMapping(cnode.name, method.name, method.desc).toRuntime();
names.add(m.s_name + m.s_desc);
@@ -84,13 +69,25 @@ public class DefaultImplementationTransformer implements IClassTransformer {
}
return changed;
}
}
private static ClassNode getClassNode(String name) {
try {
return ASMHelper.createClassNode(Launch.classLoader.getClassBytes(name.replace('/', '.')));
} catch (IOException e) {
throw new RuntimeException(e);
}
private static HashMap<String, InterfaceImpl> impls = new HashMap<String, InterfaceImpl>();
public static void registerDefaultImpl(String iname, String cname) {
impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname));
}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (transformedName.startsWith("net.minecraft") || impls.isEmpty()) return bytes;
ClassNode cnode = ASMHelper.createClassNode(bytes);
boolean changed = false;
for (String iname : cnode.interfaces) {
InterfaceImpl impl = impls.get(iname);
if (impl != null) changed |= impl.patch(cnode);
}
return changed ? ASMHelper.createBytes(cnode, 0) : bytes;
}
}
@@ -1,5 +1,7 @@
package codechicken.core.asm;
import static codechicken.core.launch.CodeChickenCorePlugin.logger;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -9,9 +11,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.Stack;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import net.minecraft.launchwrapper.IClassTransformer;
@@ -21,18 +21,14 @@ import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import codechicken.core.launch.CodeChickenCorePlugin;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DelegatedTransformer implements IClassTransformer {
private static final ArrayList<IClassTransformer> delegatedTransformers;
private static final Method m_defineClass;
private static final Field f_cachedClasses;
private static IClassTransformer[] transformers;
private static ArrayList<IClassTransformer> delegatedTransformers;
private static Method m_defineClass;
private static Field f_cachedClasses;
static {
delegatedTransformers = new ArrayList<>();
public DelegatedTransformer() {
delegatedTransformers = new ArrayList<IClassTransformer>();
try {
m_defineClass = ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
@@ -45,75 +41,14 @@ public class DelegatedTransformer implements IClassTransformer {
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
for (IClassTransformer t : transformers) {
basicClass = t.transform(name, transformedName, basicClass);
}
return basicClass;
}
public static void load() {
scanModsForDelegatedTransformers();
final int size = delegatedTransformers.size();
if (size == 0) {
CodeChickenCorePlugin.logger
.debug("No delegated transformer found, skipping registration of main DelegatedTransformer.");
return;
}
CodeChickenCorePlugin.logger
.debug("Found " + size + " delegated transformers, registering main DelegatedTransformer.");
transformers = delegatedTransformers.toArray(new IClassTransformer[0]);
String name = DelegatedTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
}
private static void scanModsForDelegatedTransformers() {
File modsDir = new File(CodeChickenCorePlugin.minecraftDir, "mods");
if (modsDir.exists()) {
final File[] files = modsDir.listFiles();
if (files != null) {
for (File file : files) {
scanMod(file);
}
}
}
File versionModsDir = new File(
CodeChickenCorePlugin.minecraftDir,
"mods/" + CodeChickenCorePlugin.currentMcVersion);
if (versionModsDir.exists()) {
final File[] files = versionModsDir.listFiles();
if (files != null) {
for (File file : files) {
scanMod(file);
}
}
}
}
private static void scanMod(File file) {
if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip")) return;
try {
try (JarFile jar = new JarFile(file)) {
Manifest manifest = jar.getManifest();
if (manifest == null) return;
Attributes attr = manifest.getMainAttributes();
if (attr == null) return;
String transformer = attr.getValue("CCTransformer");
if (transformer != null) {
addTransformer(transformer, jar, file);
}
}
} catch (Exception e) {
CodeChickenCorePlugin.logger.error("CodeChickenCore: Failed to read jar file: " + file.getName(), e);
}
public byte[] transform(String name, String tname, byte[] bytes) {
if (bytes == null) return null;
for (IClassTransformer trans : delegatedTransformers) bytes = trans.transform(name, tname, bytes);
return bytes;
}
public static void addTransformer(String transformer, JarFile jar, File jarFile) {
CodeChickenCorePlugin.logger.debug("Adding CCTransformer: " + transformer);
logger.debug("Adding CCTransformer: " + transformer);
try {
byte[] bytes;
bytes = Launch.classLoader.getClassBytes(transformer);
@@ -172,7 +107,7 @@ public class DelegatedTransformer implements IClassTransformer {
byte[] depbytes = readFully(jar.getInputStream(entry));
defineDependancies(depbytes, jar, jarFile, depStack);
CodeChickenCorePlugin.logger.debug("Defining dependancy: " + dependancy);
logger.debug("Defining dependancy: " + dependancy);
defineClass(dependancy.replace('/', '.'), depbytes);
}
@@ -193,6 +128,7 @@ public class DelegatedTransformer implements IClassTransformer {
while ((r = stream.read()) != -1) {
bos.write(r);
}
return bos.toByteArray();
}
}
@@ -24,6 +24,7 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.CC_ClassWriter;
import codechicken.lib.asm.ObfMapping;
import codechicken.obfuscator.IHeirachyEvaluator;
@@ -34,11 +35,15 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes, IHeirachyEvaluator {
static {
ASMInit.init();
}
public static class LoadPlugin implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
return null;
return new String[0];
}
@Override
@@ -113,20 +118,26 @@ public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes,
}
public static void load() {
run = new ObfuscationRun(
false,
ObfMapping.MCPRemapper.getConfFiles(),
ObfuscationRun.fillDefaults(new HashMap<String, String>()));
run.obf.setHeirachyEvaluator(instance);
run.setQuiet().parseMappings();
Collections.addAll(excludedPackages, run.config.get("excludedPackages").split(";"));
CodeChickenCoreModContainer.loadConfig();
if (ObfMapping.obfuscated) {
ObfMapping.loadMCPRemapper();
run.setSeargeConstants();
getTransformers().add(instance);
} else {
getTransformers().add(0, instance); // insert transformer as first.
if (CodeChickenCoreModContainer.config.getTag("dev.deobfuscate")
.setComment("set to true to completely deobfuscate mcp names")
.getBooleanValue(!ObfMapping.obfuscated)) {
run = new ObfuscationRun(
false,
ObfMapping.MCPRemapper.getConfFiles(),
ObfuscationRun.fillDefaults(new HashMap<String, String>()));
run.obf.setHeirachyEvaluator(instance);
run.setQuiet().parseMappings();
Collections.addAll(excludedPackages, run.config.get("excludedPackages").split(";"));
if (ObfMapping.obfuscated) {
ObfMapping.loadMCPRemapper();
run.setSeargeConstants();
getTransformers().add(instance);
} else {
getTransformers().add(0, instance); // insert transformer as first.
}
}
}
@@ -5,15 +5,14 @@ import static codechicken.lib.asm.InsnComparator.findOnce;
import java.util.Map;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.asm.ASMBlock;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.ASMReader;
import codechicken.lib.asm.ModularASMTransformer;
import codechicken.lib.asm.ModularASMTransformer.MethodReplacer;
@@ -21,29 +20,20 @@ import codechicken.lib.asm.ModularASMTransformer.MethodTransformer;
import codechicken.lib.asm.ModularASMTransformer.MethodWriter;
import codechicken.lib.asm.ObfMapping;
import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class TweakTransformer implements IClassTransformer, Opcodes {
private static final ModularASMTransformer transformer = new ModularASMTransformer();
private static final Map<String, ASMBlock> blocks = ASMReader
.loadResource("/assets/codechickencore/asm/tweaks.asm");
static {
ASMInit.init();
}
private static ModularASMTransformer transformer = new ModularASMTransformer();
private static Map<String, ASMBlock> blocks = ASMReader.loadResource("/assets/codechickencore/asm/tweaks.asm");
public static ConfigTag tweaks;
public static void load() {
loadTransformersFromConfig();
if (transformer.isEmpty()) {
CodeChickenCorePlugin.logger
.debug("No tweaks parsed from config, skipping registration of TweakTransformer.");
return;
}
String name = TweakTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
}
private static void loadTransformersFromConfig() {
tweaks = CodeChickenCorePlugin.config.getTag("tweaks")
CodeChickenCoreModContainer.loadConfig();
tweaks = CodeChickenCoreModContainer.config.getTag("tweaks")
.setComment("Various tweaks that can be applied to game mechanics.").useBraces();
tweaks.removeTag("persistantLava");
@@ -113,7 +103,7 @@ public class TweakTransformer implements IClassTransformer, Opcodes {
}
@Override
public final byte[] transform(String name, String tname, byte[] bytes) {
public byte[] transform(String name, String tname, byte[] bytes) {
return transformer.transform(name, bytes);
}
}
@@ -112,12 +112,12 @@ public abstract class CoreCommand implements ICommand {
}
@Override
public List<String> getCommandAliases() {
public List<?> getCommandAliases() {
return null;
}
@Override
public List<String> addTabCompletionOptions(ICommandSender var1, String[] var2) {
public List<?> addTabCompletionOptions(ICommandSender var1, String[] var2) {
return null;
}
@@ -6,8 +6,6 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCButton extends GuiWidget {
public String text;
@@ -75,8 +73,7 @@ public class GuiCCButton extends GuiWidget {
}
public int getTextColour(int mousex, int mousey) {
return !isEnabled ? LocalizedColours.BUTTON_TEXT_DISABLED
: pointInside(mousex, mousey) ? LocalizedColours.BUTTON_TEXT_HOVER : LocalizedColours.BUTTON_TEXT;
return !isEnabled ? 0xFFA0A0A0 : pointInside(mousex, mousey) ? 0xFFFFFFA0 : 0xFFE0E0E0;
}
public GuiCCButton setActionCommand(String string) {
@@ -5,8 +5,6 @@ import net.minecraft.util.ChatAllowedCharacters;
import org.lwjgl.input.Keyboard;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCTextField extends GuiWidget {
private String text;
@@ -122,8 +120,8 @@ public class GuiCCTextField extends GuiWidget {
}
public void drawBackground() {
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, LocalizedColours.TEXT_FIELD_BORDER);
drawRect(x, y, x + width, y + height, LocalizedColours.TEXT_FIELD_BACKGROUND);
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0);
drawRect(x, y, x + width, y + height, 0xff000000);
}
public String getDrawText() {
@@ -137,7 +135,7 @@ public class GuiCCTextField extends GuiWidget {
}
public int getTextColour() {
return isEnabled ? LocalizedColours.TEXT_FIELD_TEXT : LocalizedColours.TEXT_FIELD_TEXT_DISABLED;
return isEnabled ? 0xe0e0e0 : 0x707070;
}
public GuiCCTextField setMaxStringLength(int i) {
@@ -3,7 +3,6 @@ package codechicken.core.gui;
import java.awt.Dimension;
import java.awt.Rectangle;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.Rectangle4i;
@@ -185,29 +184,28 @@ public abstract class GuiScrollPane extends GuiWidget {
}
public void drawBackground(float frame) {
drawRect(x, y, x + width, y + height, LocalizedColours.SCROLL_PANE_BACKGROUND);
drawRect(x, y, x + width, y + height, 0xff000000);
}
public abstract void drawContent(int mx, int my, float frame);
public void drawOverlay(float frame) {
// outlines
drawRect(x, y - 1, x + width, y, LocalizedColours.SCROLL_PANE_OVERLAY_TOP);
drawRect(x, y + height, x + width, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_BOTTOM);
drawRect(x - 1, y - 1, x, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_LEFT);
drawRect(x + width, y - 1, x + width + 1, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_RIGHT);
drawRect(x, y - 1, x + width, y, 0xffa0a0a0); // top
drawRect(x, y + height, x + width, y + height + 1, 0xffa0a0a0); // bottom
drawRect(x - 1, y - 1, x, y + height + 1, 0xffa0a0a0); // left
drawRect(x + width, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); // right
}
public void drawScrollbar(float frame) {
Rectangle r = scrollbarBounds();
drawRect(r.x, r.y, r.x + r.width, r.y + r.height, LocalizedColours.SCROLLBAR_CORNERS);
drawRect(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_TOP_LEFT);
drawRect(r.x + 1, r.y + 1, r.x + r.width, r.y + r.height, LocalizedColours.SCROLLBAR_BOTTOM_RIGHT);
drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_FILL);
drawRect(r.x, r.y, r.x + r.width, r.y + r.height, 0xFF8B8B8B); // corners
drawRect(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1, 0xFFF0F0F0); // topleft up
drawRect(r.x + 1, r.y + 1, r.x + r.width, r.y + r.height, 0xFF555555); // bottom right down
drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, 0xFFC6C6C6); // scrollbar
int algn = scrollbarGuideAlignment();
if (algn != 0)
drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, LocalizedColours.SCROLLBAR_GUIDE);
if (algn != 0) drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, 0xFF808080); // lineguide
}
}
@@ -9,6 +9,9 @@ import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
@@ -18,87 +21,48 @@ import javax.swing.event.HyperlinkListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import codechicken.core.asm.CodeChickenCoreModContainer;
import codechicken.core.asm.DelegatedTransformer;
import codechicken.core.asm.MCPDeobfuscationTransformer;
import codechicken.core.asm.Tags;
import codechicken.core.asm.TweakTransformer;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser;
import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions;
@IFMLLoadingPlugin.MCVersion("1.7.10")
@IFMLLoadingPlugin.TransformerExclusions({ "codechicken.core.asm", "codechicken.obfuscator", "codechicken.lib.asm",
"codechicken.lib.config" })
public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
@TransformerExclusions(value = { "codechicken.core.asm", "codechicken.obfuscator" })
@MCVersion("1.7.10")
public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
public static final String mcVersion = "[1.7.10]";
@Deprecated
public static final String version = Tags.VERSION;
public static ConfigFile config;
public static File minecraftDir;
public static String currentMcVersion;
public static Logger logger = LogManager.getLogger("CodeChickenCore");
public CodeChickenCorePlugin() {
if (minecraftDir != null) return;
if (minecraftDir != null) return; // get called twice, once for IFMLCallHook
minecraftDir = (File) FMLInjectionData.data()[6];
currentMcVersion = (String) FMLInjectionData.data()[4];
config = new ConfigFile(new File(minecraftDir, "config/CodeChickenCore.cfg"))
.setComment("CodeChickenCore configuration file.");
if (Boolean.getBoolean("ccc.dev.deobfuscate")) {
injectDeobfPlugin();
}
}
@Override
public String[] getASMTransformerClass() {
versionCheck(mcVersion, "CodeChickenCore");
return new String[] { "codechicken.lib.asm.ClassHeirachyManager", "codechicken.lib.asm.RedirectorTransformer" };
}
@Override
public String getAccessTransformerClass() {
if (Boolean.getBoolean("ccc.dev.runtimePublic")) {
return "codechicken.core.asm.CodeChickenAccessTransformer";
} else {
return null;
}
}
@Override
public String getModContainerClass() {
return "codechicken.core.asm.CodeChickenCoreModContainer";
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
ConfigTag checkRAM = CodeChickenCorePlugin.config.getTag("checks")
.setComment("Configuration options for checking various requirements for a modpack.").useBraces();
if (checkRAM.getTag("checkRAM")
.setComment("If set to true, check RAM available for Minecraft before continuing to load")
.getBooleanValue(false)) {
systemCheck(checkRAM);
}
TweakTransformer.load();
DelegatedTransformer.load();
injectDeobfPlugin();
}
private void injectDeobfPlugin() {
try {
Class<?> wrapperClass = Class.forName("cpw.mods.fml.relauncher.CoreModManager$FMLPluginWrapper");
Constructor<?> wrapperConstructor = wrapperClass
Constructor wrapperConstructor = wrapperClass
.getConstructor(String.class, IFMLLoadingPlugin.class, File.class, Integer.TYPE, String[].class);
Field f_loadPlugins = CoreModManager.class.getDeclaredField("loadPlugins");
wrapperConstructor.setAccessible(true);
@@ -112,7 +76,7 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
0,
new String[0]));
} catch (Exception e) {
logger.error("Failed to inject FMLPluginWrapper for MCPDeobfuscation Transformer", e);
logger.error("Failed to inject MCPDeobfuscation Transformer", e);
}
}
@@ -222,4 +186,74 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
FMLCommonHandler.instance().exitJava(-98, true);
}
}
@Override
public String[] getASMTransformerClass() {
versionCheck(mcVersion, "CodeChickenCore");
return new String[] { "codechicken.lib.asm.ClassHeirachyManager", "codechicken.core.asm.TweakTransformer",
"codechicken.core.asm.DelegatedTransformer", "codechicken.core.asm.DefaultImplementationTransformer",
"codechicken.lib.asm.RedirectorTransformer" };
}
@Override
public String getAccessTransformerClass() {
return "codechicken.core.asm.CodeChickenAccessTransformer";
}
@Override
public String getModContainerClass() {
return "codechicken.core.asm.CodeChickenCoreModContainer";
}
@Override
public String getSetupClass() {
return getClass().getName();
}
@Override
public void injectData(Map<String, Object> data) {}
@Override
public Void call() {
CodeChickenCoreModContainer.loadConfig();
ConfigTag checkRAM;
checkRAM = CodeChickenCoreModContainer.config.getTag("checks")
.setComment("Configuration options for checking various requirements for a modpack.").useBraces();
if (checkRAM.getTag("checkRAM")
.setComment("If set to true, check RAM available for Minecraft before continuing to load")
.getBooleanValue(false))
systemCheck(checkRAM);
TweakTransformer.load();
scanCodeChickenMods();
return null;
}
private void scanCodeChickenMods() {
File modsDir = new File(minecraftDir, "mods");
for (File file : modsDir.listFiles()) scanMod(file);
File versionModsDir = new File(minecraftDir, "mods/" + currentMcVersion);
if (versionModsDir.exists()) for (File file : versionModsDir.listFiles()) scanMod(file);
}
private void scanMod(File file) {
if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip")) return;
try {
JarFile jar = new JarFile(file);
try {
Manifest manifest = jar.getManifest();
if (manifest == null) return;
Attributes attr = manifest.getMainAttributes();
if (attr == null) return;
String transformer = attr.getValue("CCTransformer");
if (transformer != null) DelegatedTransformer.addTransformer(transformer, jar, file);
} finally {
jar.close();
}
} catch (Exception e) {
logger.error("CodeChickenCore: Failed to read jar file: " + file.getName(), e);
}
}
}
@@ -0,0 +1,186 @@
package codechicken.core.launch;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Box;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import cpw.mods.fml.common.versioning.ComparableVersion;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
/**
* For autodownloading stuff. This is really unoriginal, mostly ripped off FML, credits to cpw.
*/
@MCVersion("1.7.10")
public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
public interface IDownloadDisplay {
void resetProgress(int sizeGuess);
void setPokeThread(Thread currentThread);
void updateProgress(int fullLength);
boolean shouldStopIt();
void updateProgressString(String string, Object... data);
Object makeDialog();
void showErrorDialog(String name, String url);
}
@SuppressWarnings("serial")
public static class Downloader extends JOptionPane implements IDownloadDisplay {
boolean stopIt;
Thread pokeThread;
private Box makeProgressPanel() {
return null;
}
@Override
public JDialog makeDialog() {
return null;
}
protected void requestClose(String message) {}
@Override
public void updateProgressString(String progressUpdate, Object... data) {}
@Override
public void resetProgress(int sizeGuess) {}
@Override
public void updateProgress(int fullLength) {}
@Override
public void setPokeThread(Thread currentThread) {}
@Override
public boolean shouldStopIt() {
return true;
}
@Override
public void showErrorDialog(String name, String url) {}
}
public static class DummyDownloader implements IDownloadDisplay {
@Override
public void resetProgress(int sizeGuess) {}
@Override
public void setPokeThread(Thread currentThread) {}
@Override
public void updateProgress(int fullLength) {}
@Override
public boolean shouldStopIt() {
return false;
}
@Override
public void updateProgressString(String string, Object... data) {}
@Override
public Object makeDialog() {
return null;
}
@Override
public void showErrorDialog(String name, String url) {}
}
public static class VersionedFile {
public final Pattern pattern;
public final String filename;
public final ComparableVersion version;
public final String name;
public VersionedFile(String filename, Pattern pattern) {
this.pattern = pattern;
this.filename = filename;
Matcher m = pattern.matcher(filename);
if (m.matches()) {
name = m.group(1);
version = new ComparableVersion(m.group(2));
} else {
name = null;
version = null;
}
}
public boolean matches() {
return name != null;
}
}
public static class Dependency {
public String url;
public VersionedFile file;
public String existing;
/**
* Flag set to add this dep to the classpath immediately because it is required for a coremod.
*/
public boolean coreLib;
public Dependency(String url, VersionedFile file, boolean coreLib) {
this.url = url;
this.file = file;
this.coreLib = coreLib;
}
}
public static class DepLoadInst {
public DepLoadInst() {}
public void load() {}
}
public static void load() {}
@Override
public String[] getASMTransformerClass() {
return null;
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return getClass().getName();
}
@Override
public void injectData(Map<String, Object> data) {}
@Override
public Void call() {
load();
return null;
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
@@ -1,177 +0,0 @@
package codechicken.lib.asm;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
public class ASMBlock {
public InsnListSection list;
private BiMap<String, LabelNode> labels;
public ASMBlock(InsnListSection list, BiMap<String, LabelNode> labels) {
this.list = list;
this.labels = labels;
}
public ASMBlock(InsnListSection list) {
this(list, HashBiMap.<String, LabelNode>create());
}
public ASMBlock(InsnList list) {
this(new InsnListSection(list));
}
public ASMBlock() {
this(new InsnListSection());
}
public LabelNode getOrAdd(String s) {
LabelNode l = get(s);
if (l == null) labels.put(s, l = new LabelNode());
return l;
}
public LabelNode get(String s) {
return labels.get(s);
}
public void replaceLabels(Map<LabelNode, LabelNode> labelMap, Set<LabelNode> usedLabels) {
for (AbstractInsnNode insn : list) switch (insn.getType()) {
case LABEL:
AbstractInsnNode insn2 = insn.clone(labelMap);
if (insn2 == insn) // identity mapping
continue;
if (usedLabels.contains(insn2))
throw new IllegalStateException("LabelNode cannot be a part of two InsnLists");
list.replace(insn, insn2);
break;
case JUMP_INSN:
case FRAME:
case LOOKUPSWITCH_INSN:
case TABLESWITCH_INSN:
list.replace(insn, insn.clone(labelMap));
}
for (Entry<LabelNode, LabelNode> entry : labelMap.entrySet()) {
String key = labels.inverse().get(entry.getKey());
if (key != null) labels.put(key, entry.getValue());
}
}
public void replaceLabels(Map<LabelNode, LabelNode> labelMap) {
replaceLabels(labelMap, Collections.EMPTY_SET);
}
public void replaceLabel(String s, LabelNode l) {
LabelNode old = get(s);
if (old != null) replaceLabels(ImmutableMap.of(old, l));
}
/**
* Pulls all common labels from other into this
*
* @return this
*/
public ASMBlock mergeLabels(ASMBlock other) {
if (labels.isEmpty() || other.labels.isEmpty()) return this;
// common labels, give them our nodes
HashMap<LabelNode, LabelNode> labelMap = list.identityLabelMap();
for (Entry<String, LabelNode> entry : other.labels.entrySet()) {
LabelNode old = labels.get(entry.getKey());
if (old != null) labelMap.put(old, entry.getValue());
}
HashSet<LabelNode> usedLabels = new HashSet<LabelNode>();
for (AbstractInsnNode insn = other.list.list.getFirst(); insn != null; insn = insn.getNext())
if (insn.getType() == LABEL) usedLabels.add((LabelNode) insn);
replaceLabels(labelMap, usedLabels);
return this;
}
/**
* Like mergeLabels but pulls insns from other list into this so LabelNodes can be transferred
*
* @return this
*/
public ASMBlock pullLabels(ASMBlock other) {
other.list.remove();
return mergeLabels(other);
}
public ASMBlock copy() {
BiMap<String, LabelNode> labels = HashBiMap.create();
Map<LabelNode, LabelNode> labelMap = list.cloneLabels();
for (Entry<String, LabelNode> entry : this.labels.entrySet())
labels.put(entry.getKey(), labelMap.get(entry.getValue()));
return new ASMBlock(list.copy(labelMap), labels);
}
public ASMBlock applyLabels(InsnListSection list2) {
if (labels.isEmpty()) return new ASMBlock(list2);
Set<LabelNode> cFlowLabels1 = labels.values();
Set<LabelNode> cFlowLabels2 = InsnComparator.getControlFlowLabels(list2);
ASMBlock block = new ASMBlock(list2);
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for (int i = 0, k = 0; i < list.size() && k < list2.size();) {
AbstractInsnNode insn1 = list.get(i);
if (!InsnComparator.insnImportant(insn1, cFlowLabels1)) {
i++;
continue;
}
AbstractInsnNode insn2 = list2.get(k);
if (!InsnComparator.insnImportant(insn2, cFlowLabels2)) {
k++;
continue;
}
if (insn1.getOpcode() != insn2.getOpcode())
throw new IllegalArgumentException("Lists do not match:\n" + list + "\n\n" + list2);
switch (insn1.getType()) {
case LABEL:
labelMap.put((LabelNode) insn1, (LabelNode) insn2);
break;
case JUMP_INSN:
labelMap.put(((JumpInsnNode) insn1).label, ((JumpInsnNode) insn2).label);
break;
}
i++;
k++;
}
for (Entry<String, LabelNode> entry : labels.entrySet())
block.labels.put(entry.getKey(), labelMap.get(entry.getValue()));
return block;
}
public InsnList rawListCopy() {
return list.copy().list;
}
}
@@ -1,203 +0,0 @@
package codechicken.lib.asm;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.DefaultingConfigFile;
public class ASMHelper {
public static ConfigFile config = loadConfig();
public static Logger logger = LogManager.getLogger("CCL ASM");
private static ConfigFile loadConfig() {
try { // weak reference for environments without FML
File mcDir = (File) ((Object[]) Class.forName("cpw.mods.fml.relauncher.FMLInjectionData").getMethod("data")
.invoke(null))[6];
File file = new File(mcDir, "config/CodeChickenLib.cfg");
if (ObfMapping.obfuscated) return new DefaultingConfigFile(file);
else return new ConfigFile(file).setComment("CodeChickenLib development configuration file.");
} catch (Exception ignored) {
return null; // no config for these systems
}
}
public static interface Acceptor {
public void accept(ClassVisitor cv) throws IOException;
}
public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) {
for (MethodNode mnode : cnode.methods) if (methodmap.matches(mnode)) return mnode;
return null;
}
public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode) {
for (FieldNode fnode : cnode.fields) if (fieldmap.matches(fnode)) return fnode;
return null;
}
public static ClassNode createClassNode(byte[] bytes) {
return createClassNode(bytes, 0);
}
public static ClassNode createClassNode(byte[] bytes, int flags) {
ClassNode cnode = new ClassNode();
ClassReader reader = new ClassReader(bytes);
reader.accept(cnode, flags);
return cnode;
}
public static byte[] createBytes(ClassNode cnode, int flags) {
ClassWriter cw = new CC_ClassWriter(flags);
cnode.accept(cw);
return cw.toByteArray();
}
public static Map<LabelNode, LabelNode> cloneLabels(InsnList list) {
return new InsnListSection(list).cloneLabels();
}
public static InsnList cloneInsnList(InsnList list) {
return new InsnListSection(list).copy().list;
}
public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList list) {
return new InsnListSection(list).copy(labelMap).list;
}
public static List<TryCatchBlockNode> cloneTryCatchBlocks(Map<LabelNode, LabelNode> labelMap,
List<TryCatchBlockNode> tcblocks) {
ArrayList<TryCatchBlockNode> clone = new ArrayList<TryCatchBlockNode>();
for (TryCatchBlockNode node : tcblocks) clone.add(
new TryCatchBlockNode(
labelMap.get(node.start),
labelMap.get(node.end),
labelMap.get(node.handler),
node.type));
return clone;
}
public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap,
List<LocalVariableNode> locals) {
ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>(locals.size());
for (LocalVariableNode node : locals) clone.add(
new LocalVariableNode(
node.name,
node.desc,
node.signature,
labelMap.get(node.start),
labelMap.get(node.end),
node.index));
return clone;
}
public static void copy(MethodNode src, MethodNode dst) {
Map<LabelNode, LabelNode> labelMap = cloneLabels(src.instructions);
dst.instructions = cloneInsnList(labelMap, src.instructions);
dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks);
if (src.localVariables != null) dst.localVariables = cloneLocals(labelMap, src.localVariables);
dst.visibleAnnotations = src.visibleAnnotations;
dst.invisibleAnnotations = src.invisibleAnnotations;
dst.visitMaxs(src.maxStack, src.maxLocals);
}
public static String toString(InsnList list) {
return new InsnListSection(list).toString();
}
public static int getLocal(List<LocalVariableNode> list, String name) {
int found = -1;
for (LocalVariableNode node : list) {
if (node.name.equals(name)) {
if (found >= 0) throw new RuntimeException(
"Duplicate local variable: " + name + " not coded to handle this scenario.");
found = node.index;
}
}
return found;
}
public static void replaceMethod(MethodNode original, MethodNode replacement) {
original.instructions.clear();
if (original.localVariables != null) original.localVariables.clear();
if (original.tryCatchBlocks != null) original.tryCatchBlocks.clear();
replacement.accept(original);
}
public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals,
boolean textify) {
try {
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
if (!file.exists()) file.createNewFile();
PrintWriter pout = new PrintWriter(file);
ClassVisitor cv = new TraceClassVisitor(null, textify ? new Textifier() : new ASMifier(), pout);
if (filterImportant) cv = new ImportantInsnVisitor(cv);
if (sortLocals) cv = new LocalVariablesSorterVisitor(cv);
acceptor.accept(cv);
pout.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals) {
dump(acceptor, file, filterImportant, sortLocals, config.getTag("textify").getBooleanValue(true));
}
public static void dump(final byte[] bytes, File file, boolean filterImportant, boolean sortLocals) {
dump(new Acceptor() {
@Override
public void accept(ClassVisitor cv) {
new ClassReader(bytes).accept(cv, ClassReader.EXPAND_FRAMES);
}
}, file, filterImportant, sortLocals);
}
public static void dump(final InputStream is, File file, boolean filterImportant, boolean sortLocals) {
dump(new Acceptor() {
@Override
public void accept(ClassVisitor cv) throws IOException {
new ClassReader(is).accept(cv, ClassReader.EXPAND_FRAMES);
}
}, file, filterImportant, sortLocals);
}
public static void dump(final ClassNode cnode, File file, boolean filterImportant, boolean sortLocals) {
dump(new Acceptor() {
@Override
public void accept(ClassVisitor cv) {
cnode.accept(cv);
}
}, file, filterImportant, sortLocals);
}
}
@@ -1,19 +0,0 @@
package codechicken.lib.asm;
import net.minecraft.launchwrapper.Launch;
/**
* Initialisation class for using this package. Call this on coremod load
*/
public class ASMInit {
private static boolean initialised = false;
public static void init() {
if (!initialised) {
Launch.classLoader.addTransformerExclusion("codechicken.lib.asm");
Launch.classLoader.addTransformerExclusion("codechicken.lib.config");
initialised = true;
}
}
}
@@ -1,469 +0,0 @@
package codechicken.lib.asm;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BALOAD;
import static org.objectweb.asm.Opcodes.BASTORE;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CALOAD;
import static org.objectweb.asm.Opcodes.CASTORE;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.D2F;
import static org.objectweb.asm.Opcodes.D2I;
import static org.objectweb.asm.Opcodes.D2L;
import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.DASTORE;
import static org.objectweb.asm.Opcodes.DCMPG;
import static org.objectweb.asm.Opcodes.DCMPL;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DCONST_1;
import static org.objectweb.asm.Opcodes.DDIV;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DMUL;
import static org.objectweb.asm.Opcodes.DNEG;
import static org.objectweb.asm.Opcodes.DREM;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DSUB;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.DUP2;
import static org.objectweb.asm.Opcodes.DUP2_X1;
import static org.objectweb.asm.Opcodes.DUP2_X2;
import static org.objectweb.asm.Opcodes.DUP_X1;
import static org.objectweb.asm.Opcodes.DUP_X2;
import static org.objectweb.asm.Opcodes.F2D;
import static org.objectweb.asm.Opcodes.F2I;
import static org.objectweb.asm.Opcodes.F2L;
import static org.objectweb.asm.Opcodes.FADD;
import static org.objectweb.asm.Opcodes.FALOAD;
import static org.objectweb.asm.Opcodes.FASTORE;
import static org.objectweb.asm.Opcodes.FCMPG;
import static org.objectweb.asm.Opcodes.FCMPL;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FCONST_1;
import static org.objectweb.asm.Opcodes.FCONST_2;
import static org.objectweb.asm.Opcodes.FDIV;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FMUL;
import static org.objectweb.asm.Opcodes.FNEG;
import static org.objectweb.asm.Opcodes.FREM;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.FSTORE;
import static org.objectweb.asm.Opcodes.FSUB;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.I2B;
import static org.objectweb.asm.Opcodes.I2C;
import static org.objectweb.asm.Opcodes.I2D;
import static org.objectweb.asm.Opcodes.I2F;
import static org.objectweb.asm.Opcodes.I2L;
import static org.objectweb.asm.Opcodes.I2S;
import static org.objectweb.asm.Opcodes.IADD;
import static org.objectweb.asm.Opcodes.IALOAD;
import static org.objectweb.asm.Opcodes.IAND;
import static org.objectweb.asm.Opcodes.IASTORE;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_2;
import static org.objectweb.asm.Opcodes.ICONST_3;
import static org.objectweb.asm.Opcodes.ICONST_4;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.IDIV;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFGE;
import static org.objectweb.asm.Opcodes.IFGT;
import static org.objectweb.asm.Opcodes.IFLE;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.IFNONNULL;
import static org.objectweb.asm.Opcodes.IFNULL;
import static org.objectweb.asm.Opcodes.IF_ACMPEQ;
import static org.objectweb.asm.Opcodes.IF_ACMPNE;
import static org.objectweb.asm.Opcodes.IF_ICMPEQ;
import static org.objectweb.asm.Opcodes.IF_ICMPGE;
import static org.objectweb.asm.Opcodes.IF_ICMPGT;
import static org.objectweb.asm.Opcodes.IF_ICMPLE;
import static org.objectweb.asm.Opcodes.IF_ICMPLT;
import static org.objectweb.asm.Opcodes.IF_ICMPNE;
import static org.objectweb.asm.Opcodes.IINC;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.IMUL;
import static org.objectweb.asm.Opcodes.INEG;
import static org.objectweb.asm.Opcodes.INSTANCEOF;
import static org.objectweb.asm.Opcodes.INVOKEDYNAMIC;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IOR;
import static org.objectweb.asm.Opcodes.IREM;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISHL;
import static org.objectweb.asm.Opcodes.ISHR;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.ISUB;
import static org.objectweb.asm.Opcodes.IUSHR;
import static org.objectweb.asm.Opcodes.IXOR;
import static org.objectweb.asm.Opcodes.JSR;
import static org.objectweb.asm.Opcodes.L2D;
import static org.objectweb.asm.Opcodes.L2F;
import static org.objectweb.asm.Opcodes.L2I;
import static org.objectweb.asm.Opcodes.LADD;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.LAND;
import static org.objectweb.asm.Opcodes.LASTORE;
import static org.objectweb.asm.Opcodes.LCMP;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1;
import static org.objectweb.asm.Opcodes.LDC;
import static org.objectweb.asm.Opcodes.LDIV;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LMUL;
import static org.objectweb.asm.Opcodes.LNEG;
import static org.objectweb.asm.Opcodes.LOOKUPSWITCH;
import static org.objectweb.asm.Opcodes.LOR;
import static org.objectweb.asm.Opcodes.LREM;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.LSHL;
import static org.objectweb.asm.Opcodes.LSHR;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.LSUB;
import static org.objectweb.asm.Opcodes.LUSHR;
import static org.objectweb.asm.Opcodes.LXOR;
import static org.objectweb.asm.Opcodes.MONITORENTER;
import static org.objectweb.asm.Opcodes.MONITOREXIT;
import static org.objectweb.asm.Opcodes.MULTIANEWARRAY;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.NEWARRAY;
import static org.objectweb.asm.Opcodes.NOP;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RET;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SALOAD;
import static org.objectweb.asm.Opcodes.SASTORE;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;
import static org.objectweb.asm.Opcodes.TABLESWITCH;
import static org.objectweb.asm.tree.AbstractInsnNode.FIELD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.IINC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INT_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INVOKE_DYNAMIC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LDC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.MULTIANEWARRAY_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.VAR_INSN;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class ASMReader {
public static Map<String, Integer> opCodes = new HashMap<String, Integer>();
public static byte[] TYPE;
static {
opCodes.put("NOP", NOP);
opCodes.put("ACONST_NULL", ACONST_NULL);
opCodes.put("ICONST_M1", ICONST_M1);
opCodes.put("ICONST_0", ICONST_0);
opCodes.put("ICONST_1", ICONST_1);
opCodes.put("ICONST_2", ICONST_2);
opCodes.put("ICONST_3", ICONST_3);
opCodes.put("ICONST_4", ICONST_4);
opCodes.put("ICONST_5", ICONST_5);
opCodes.put("LCONST_0", LCONST_0);
opCodes.put("LCONST_1", LCONST_1);
opCodes.put("FCONST_0", FCONST_0);
opCodes.put("FCONST_1", FCONST_1);
opCodes.put("FCONST_2", FCONST_2);
opCodes.put("DCONST_0", DCONST_0);
opCodes.put("DCONST_1", DCONST_1);
opCodes.put("BIPUSH", BIPUSH);
opCodes.put("SIPUSH", SIPUSH);
opCodes.put("LDC", LDC);
opCodes.put("ILOAD", ILOAD);
opCodes.put("LLOAD", LLOAD);
opCodes.put("FLOAD", FLOAD);
opCodes.put("DLOAD", DLOAD);
opCodes.put("ALOAD", ALOAD);
opCodes.put("IALOAD", IALOAD);
opCodes.put("LALOAD", LALOAD);
opCodes.put("FALOAD", FALOAD);
opCodes.put("DALOAD", DALOAD);
opCodes.put("AALOAD", AALOAD);
opCodes.put("BALOAD", BALOAD);
opCodes.put("CALOAD", CALOAD);
opCodes.put("SALOAD", SALOAD);
opCodes.put("ISTORE", ISTORE);
opCodes.put("LSTORE", LSTORE);
opCodes.put("FSTORE", FSTORE);
opCodes.put("DSTORE", DSTORE);
opCodes.put("ASTORE", ASTORE);
opCodes.put("IASTORE", IASTORE);
opCodes.put("LASTORE", LASTORE);
opCodes.put("FASTORE", FASTORE);
opCodes.put("DASTORE", DASTORE);
opCodes.put("AASTORE", AASTORE);
opCodes.put("BASTORE", BASTORE);
opCodes.put("CASTORE", CASTORE);
opCodes.put("SASTORE", SASTORE);
opCodes.put("POP", POP);
opCodes.put("POP2", POP2);
opCodes.put("DUP", DUP);
opCodes.put("DUP_X1", DUP_X1);
opCodes.put("DUP_X2", DUP_X2);
opCodes.put("DUP2", DUP2);
opCodes.put("DUP2_X1", DUP2_X1);
opCodes.put("DUP2_X2", DUP2_X2);
opCodes.put("SWAP", SWAP);
opCodes.put("IADD", IADD);
opCodes.put("LADD", LADD);
opCodes.put("FADD", FADD);
opCodes.put("DADD", DADD);
opCodes.put("ISUB", ISUB);
opCodes.put("LSUB", LSUB);
opCodes.put("FSUB", FSUB);
opCodes.put("DSUB", DSUB);
opCodes.put("IMUL", IMUL);
opCodes.put("LMUL", LMUL);
opCodes.put("FMUL", FMUL);
opCodes.put("DMUL", DMUL);
opCodes.put("IDIV", IDIV);
opCodes.put("LDIV", LDIV);
opCodes.put("FDIV", FDIV);
opCodes.put("DDIV", DDIV);
opCodes.put("IREM", IREM);
opCodes.put("LREM", LREM);
opCodes.put("FREM", FREM);
opCodes.put("DREM", DREM);
opCodes.put("INEG", INEG);
opCodes.put("LNEG", LNEG);
opCodes.put("FNEG", FNEG);
opCodes.put("DNEG", DNEG);
opCodes.put("ISHL", ISHL);
opCodes.put("LSHL", LSHL);
opCodes.put("ISHR", ISHR);
opCodes.put("LSHR", LSHR);
opCodes.put("IUSHR", IUSHR);
opCodes.put("LUSHR", LUSHR);
opCodes.put("IAND", IAND);
opCodes.put("LAND", LAND);
opCodes.put("IOR", IOR);
opCodes.put("LOR", LOR);
opCodes.put("IXOR", IXOR);
opCodes.put("LXOR", LXOR);
opCodes.put("IINC", IINC);
opCodes.put("I2L", I2L);
opCodes.put("I2F", I2F);
opCodes.put("I2D", I2D);
opCodes.put("L2I", L2I);
opCodes.put("L2F", L2F);
opCodes.put("L2D", L2D);
opCodes.put("F2I", F2I);
opCodes.put("F2L", F2L);
opCodes.put("F2D", F2D);
opCodes.put("D2I", D2I);
opCodes.put("D2L", D2L);
opCodes.put("D2F", D2F);
opCodes.put("I2B", I2B);
opCodes.put("I2C", I2C);
opCodes.put("I2S", I2S);
opCodes.put("LCMP", LCMP);
opCodes.put("FCMPL", FCMPL);
opCodes.put("FCMPG", FCMPG);
opCodes.put("DCMPL", DCMPL);
opCodes.put("DCMPG", DCMPG);
opCodes.put("IFEQ", IFEQ);
opCodes.put("IFNE", IFNE);
opCodes.put("IFLT", IFLT);
opCodes.put("IFGE", IFGE);
opCodes.put("IFGT", IFGT);
opCodes.put("IFLE", IFLE);
opCodes.put("IF_ICMPEQ", IF_ICMPEQ);
opCodes.put("IF_ICMPNE", IF_ICMPNE);
opCodes.put("IF_ICMPLT", IF_ICMPLT);
opCodes.put("IF_ICMPGE", IF_ICMPGE);
opCodes.put("IF_ICMPGT", IF_ICMPGT);
opCodes.put("IF_ICMPLE", IF_ICMPLE);
opCodes.put("IF_ACMPEQ", IF_ACMPEQ);
opCodes.put("IF_ACMPNE", IF_ACMPNE);
opCodes.put("GOTO", GOTO);
opCodes.put("JSR", JSR);
opCodes.put("RET", RET);
opCodes.put("TABLESWITCH", TABLESWITCH);
opCodes.put("LOOKUPSWITCH", LOOKUPSWITCH);
opCodes.put("IRETURN", IRETURN);
opCodes.put("LRETURN", LRETURN);
opCodes.put("FRETURN", FRETURN);
opCodes.put("DRETURN", DRETURN);
opCodes.put("ARETURN", ARETURN);
opCodes.put("RETURN", RETURN);
opCodes.put("GETSTATIC", GETSTATIC);
opCodes.put("PUTSTATIC", PUTSTATIC);
opCodes.put("GETFIELD", GETFIELD);
opCodes.put("PUTFIELD", PUTFIELD);
opCodes.put("INVOKEVIRTUAL", INVOKEVIRTUAL);
opCodes.put("INVOKESPECIAL", INVOKESPECIAL);
opCodes.put("INVOKESTATIC", INVOKESTATIC);
opCodes.put("INVOKEINTERFACE", INVOKEINTERFACE);
opCodes.put("INVOKEDYNAMIC", INVOKEDYNAMIC);
opCodes.put("NEW", NEW);
opCodes.put("NEWARRAY", NEWARRAY);
opCodes.put("ANEWARRAY", ANEWARRAY);
opCodes.put("ARRAYLENGTH", ARRAYLENGTH);
opCodes.put("ATHROW", ATHROW);
opCodes.put("CHECKCAST", CHECKCAST);
opCodes.put("INSTANCEOF", INSTANCEOF);
opCodes.put("MONITORENTER", MONITORENTER);
opCodes.put("MONITOREXIT", MONITOREXIT);
opCodes.put("MULTIANEWARRAY", MULTIANEWARRAY);
opCodes.put("IFNULL", IFNULL);
opCodes.put("IFNONNULL", IFNONNULL);
// derived from classWriter, mapped to AbstractInsnNode
TYPE = new byte[200];
String s = "AAAAAAAAAAAAAAAABBJ__CCCCC____________________AAAAAAAACC"
+ "CCC____________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHCLMAA"
+ "AAAAEEEEFFFFGDBDAADDAA_NHH";
for (int i = 0; i < s.length(); i++) TYPE[i] = (byte) (s.charAt(i) - 'A');
}
public static Map<String, ASMBlock> loadResource(String res) {
return loadResource(ASMHelper.class.getResourceAsStream(res), res);
}
public static Map<String, ASMBlock> loadResource(InputStream in, String res) {
HashMap<String, ASMBlock> blocks = new HashMap<String, ASMBlock>();
String current = "unnamed";
ASMBlock block = new ASMBlock();
try {
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = r.readLine()) != null) {
{
int hpos = line.indexOf('#');
if (hpos >= 0) line = line.substring(0, hpos);
}
line = line.trim();
if (line.length() == 0) continue;
if (line.startsWith("list ")) {
if (block.list.size() > 0) blocks.put(current, block);
current = line.substring(5);
block = new ASMBlock();
continue;
}
try {
AbstractInsnNode insn = null;
String[] split = line.replace(" : ", ":").split(" ");
Integer i_opcode = opCodes.get(split[0]);
if (i_opcode == null) {
if (split[0].equals("LINENUMBER"))
insn = new LineNumberNode(Integer.parseInt(split[1]), block.getOrAdd(split[2]));
else if (split[0].startsWith("L")) insn = block.getOrAdd(split[0]);
else throw new Exception("Unknown opcode " + split[0]);
} else {
int opcode = i_opcode;
switch (TYPE[opcode]) {
case INSN:
insn = new InsnNode(opcode);
break;
case INT_INSN:
insn = new IntInsnNode(opcode, Integer.parseInt(split[1]));
break;
case VAR_INSN:
insn = new VarInsnNode(opcode, Integer.parseInt(split[1]));
break;
case TYPE_INSN:
insn = new ObfMapping(split[1]).toClassloading().toInsn(opcode);
break;
case FIELD_INSN:
case METHOD_INSN:
StringBuilder sb = new StringBuilder();
for (int i = 1; i < split.length; i++) sb.append(split[i]);
insn = ObfMapping.fromDesc(sb.toString()).toClassloading().toInsn(opcode);
break;
case INVOKE_DYNAMIC_INSN:
throw new Exception("Found INVOKEDYNAMIC while reading");
case JUMP_INSN:
insn = new JumpInsnNode(opcode, block.getOrAdd(split[1]));
break;
case LDC_INSN:
String cst = split[1];
if (cst.equals("*")) insn = new LdcInsnNode(null);
else if (cst.endsWith("\"")) insn = new LdcInsnNode(cst.substring(1, cst.length() - 1));
else if (cst.endsWith("L"))
insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.endsWith("F"))
insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.endsWith("D"))
insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.contains(".")) insn = new LdcInsnNode(Double.valueOf(cst));
else insn = new LdcInsnNode(Integer.valueOf(cst));
break;
case IINC_INSN:
insn = new IincInsnNode(opcode, Integer.parseInt(split[1]));
break;
case LABEL:
throw new Exception("Use L# for labels");
case TABLESWITCH_INSN:
case LOOKUPSWITCH_INSN:
throw new Exception("I don't know how to deal with this insn type");
case MULTIANEWARRAY_INSN:
insn = new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2]));
break;
case FRAME:
throw new Exception("Use ClassWriter.COMPUTE_FRAMES");
}
}
if (insn != null) block.list.add(insn);
} catch (Exception e) {
System.err.println("Error while reading ASM Block " + current + " from " + res + ", line: " + line);
e.printStackTrace();
}
}
r.close();
if (block.list.size() > 0) blocks.put(current, block);
} catch (IOException e) {
throw new RuntimeException("Failed to read ASM resource: " + res, e);
}
return blocks;
}
}
@@ -1,29 +0,0 @@
package codechicken.lib.asm;
import org.objectweb.asm.ClassWriter;
public class CC_ClassWriter extends ClassWriter {
private final boolean runtime;
public CC_ClassWriter(int flags) {
this(flags, false);
}
public CC_ClassWriter(int flags, boolean runtime) {
super(flags);
this.runtime = runtime;
}
@Override
protected String getCommonSuperClass(String type1, String type2) {
String c = type1.replace('/', '.');
String d = type2.replace('/', '.');
if (ClassHeirachyManager.classExtends(d, c)) return type1;
if (ClassHeirachyManager.classExtends(c, d)) return type2;
do {
c = ClassHeirachyManager.getSuperClass(c, runtime);
} while (!ClassHeirachyManager.classExtends(d, c));
return c.replace('.', '/');
}
}
@@ -1,109 +0,0 @@
package codechicken.lib.asm;
/***
* This Class is derived from the ASM ClassReader
* <p>
* ASM: a very small and fast Java bytecode manipulation framework Copyright (c) 2000-2011 INRIA, France Telecom All
* rights reserved.
* <p>
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
* distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
* <p>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.nio.charset.StandardCharsets;
import org.objectweb.asm.Opcodes;
/**
* Using this class to search for a (single) String reference is > 40 times faster than parsing a class with a
* ClassReader + ClassNode while using way less RAM
*/
public class ClassConstantPoolParser {
private static final int UTF8 = 1;
private static final int INT = 3;
private static final int FLOAT = 4;
private static final int LONG = 5;
private static final int DOUBLE = 6;
private static final int FIELD = 9;
private static final int METH = 10;
private static final int IMETH = 11;
private static final int NAME_TYPE = 12;
private static final int HANDLE = 15;
private static final int INDY = 18;
private final byte[][] BYTES_TO_SEARCH;
public ClassConstantPoolParser(String... strings) {
BYTES_TO_SEARCH = new byte[strings.length][];
for (int i = 0; i < BYTES_TO_SEARCH.length; i++) {
BYTES_TO_SEARCH[i] = strings[i].getBytes(StandardCharsets.UTF_8);
}
}
/**
* Returns true if the constant pool of the class represented by this byte array contains one of the Strings we are
* looking for
*/
public boolean find(byte[] basicClass) {
if (basicClass == null || basicClass.length == 0) {
return false;
}
// checks the class version
if (readShort(6, basicClass) > Opcodes.V1_8) {
return false;
}
// parses the constant pool
int n = readUnsignedShort(8, basicClass);
int index = 10;
for (int i = 1; i < n; ++i) {
int size;
switch (basicClass[index]) {
case FIELD, METH, IMETH, INT, FLOAT, NAME_TYPE, INDY -> size = 5;
case LONG, DOUBLE -> {
size = 9;
++i;
}
case UTF8 -> {
final int strLen = readUnsignedShort(index + 1, basicClass);
size = 3 + strLen;
label: for (byte[] bytes : BYTES_TO_SEARCH) {
if (strLen == bytes.length) {
for (int j = index + 3; j < index + 3 + strLen; j++) {
if (basicClass[j] != bytes[j - (index + 3)]) {
continue label;
}
}
return true;
}
}
}
case HANDLE -> size = 4;
default -> size = 3;
}
index += size;
}
return false;
}
private static short readShort(final int index, byte[] basicClass) {
return (short) (((basicClass[index] & 0xFF) << 8) | (basicClass[index + 1] & 0xFF));
}
private static int readUnsignedShort(final int index, byte[] basicClass) {
return ((basicClass[index] & 0xFF) << 8) | (basicClass[index + 1] & 0xFF);
}
}
@@ -1,176 +0,0 @@
package codechicken.lib.asm;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.ClassReader;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
/**
* This is added as a class transformer if CodeChickenCore is installed. Adding it as a class transformer will speed
* evaluation up slightly by automatically caching superclasses when they are first loaded.
*/
public class ClassHeirachyManager implements IClassTransformer {
private static final Map<String, ClassInfo> superclasses = new ConcurrentHashMap<>(1000);
static {
superclasses.put("java.lang.Object", ClassInfo.OBJECT);
}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) return null;
if (!superclasses.containsKey(transformedName)) {
declareASM(transformedName, bytes);
}
return bytes;
}
private static ClassInfo declareASM(String name, byte[] bytes) {
final ClassReader classReader = new ClassReader(bytes);
final ClassInfo classInfo = new ClassInfo(classReader);
superclasses.put(name, classInfo);
return classInfo;
}
private static ClassInfo declareReflection(String name) throws ClassNotFoundException {
final Class<?> aclass = Class.forName(name);
final ClassInfo classInfo = new ClassInfo(aclass);
superclasses.put(name, classInfo);
return classInfo;
}
private static ClassInfo declareClass(String name) {
try {
byte[] bytes = Launch.classLoader.getClassBytes(unKey(name));
if (bytes != null) {
return declareASM(name, bytes);
}
} catch (Exception ignored) {}
try {
return declareReflection(name);
} catch (ClassNotFoundException ignored) {}
return null;
}
private static String toKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
private static String unKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
/**
* @param name The class in question
* @param superclass The class being extended
* @return true if clazz extends, either directly or indirectly, superclass.
*/
public static boolean classExtends(String name, String superclass) {
name = toKey(name);
superclass = toKey(superclass);
if (name.equals(superclass)) return true;
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null) return false;
return classInfo.hasSuper(superclass);
}
public static String getSuperClass(String name, boolean runtime) {
name = toKey(name);
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null || classInfo.superclass == null) return "java.lang.Object";
final String s = classInfo.superclass;
if (!runtime) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
}
return s;
}
private static class ClassInfo {
public static final ClassInfo OBJECT = new ClassInfo();
public final String superclass;
public final String[] interfaces;
public ClassInfo parent;
private ClassInfo() {
this.superclass = null;
this.interfaces = null;
}
public ClassInfo(ClassReader classReader) {
final String superName = classReader.getSuperName();
final String[] interfaces = classReader.getInterfaces();
if (superName == null || "java/lang/Object".equals(superName)) {
superclass = "java.lang.Object";
parent = OBJECT;
} else {
superclass = toKey(superName);
}
if (interfaces == null || interfaces.length == 0) {
this.interfaces = null;
} else {
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaces[i] = toKey(interfaces[i]);
}
}
}
public ClassInfo(Class<?> aclass) {
if (aclass.isInterface()) {
this.superclass = "java.lang.Object";
this.parent = OBJECT;
} else {
final Class<?> superclass = aclass.getSuperclass();
if (superclass == null || "java.lang.Object".equals(superclass.getName())) {
this.superclass = "java.lang.Object";
this.parent = OBJECT;
} else {
this.superclass = toKey(superclass.getName());
}
}
final Class<?>[] interfaces = aclass.getInterfaces();
if (interfaces.length == 0) {
this.interfaces = null;
} else {
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaces[i] = toKey(interfaces[i].getName());
}
}
}
public boolean hasSuper(String superclass) {
if (this.superclass == null) return false;
if (this.superclass.equals(superclass)) return true;
ClassInfo parentInfo = this.parent;
if (parentInfo == null) parentInfo = superclasses.get(this.superclass);
if (parentInfo == null) parentInfo = declareClass(this.superclass);
if (parentInfo == null) return false;
this.parent = parentInfo;
return parentInfo.hasSuper(superclass);
}
}
}
@@ -1,36 +0,0 @@
package codechicken.lib.asm;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
public class ImportantInsnVisitor extends ClassVisitor {
public class ImportantInsnMethodVisitor extends MethodVisitor {
MethodVisitor delegate;
public ImportantInsnMethodVisitor(int access, String name, String desc, String signature, String[] exceptions) {
super(Opcodes.ASM4, new MethodNode(access, name, desc, signature, exceptions));
delegate = cv.visitMethod(access, name, desc, signature, exceptions);
}
@Override
public void visitEnd() {
super.visitEnd();
MethodNode mnode = (MethodNode) mv;
mnode.instructions = InsnComparator.getImportantList(mnode.instructions);
mnode.accept(delegate);
}
}
public ImportantInsnVisitor(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new ImportantInsnMethodVisitor(access, name, desc, signature, exceptions);
}
}
@@ -1,210 +0,0 @@
package codechicken.lib.asm;
import static org.objectweb.asm.tree.AbstractInsnNode.FIELD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.IINC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INT_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LDC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LINE;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.VAR_INSN;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
public class InsnComparator {
public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2) {
return insn1.var == -1 || insn2.var == -1 || insn1.var == insn2.var;
}
public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2) {
return insn1.cst == null || insn2.cst == null || insn1.cst.equals(insn2.cst);
}
public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2) {
return insn1.desc.equals("*") || insn2.desc.equals("*") || insn1.desc.equals(insn2.desc);
}
public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2) {
return node1.var == node2.var && node1.incr == node2.incr;
}
public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2) {
return node1.operand == -1 || node2.operand == -1 || node1.operand == node2.operand;
}
public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2) {
if (node1.getOpcode() != node2.getOpcode()) return false;
switch (node2.getType()) {
case VAR_INSN:
return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2);
case TYPE_INSN:
return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2);
case FIELD_INSN:
return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2);
case METHOD_INSN:
return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2);
case LDC_INSN:
return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2);
case IINC_INSN:
return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2);
case INT_INSN:
return intInsnEqual((IntInsnNode) node1, (IntInsnNode) node2);
default:
return true;
}
}
public static boolean insnImportant(AbstractInsnNode insn, Set<LabelNode> controlFlowLabels) {
switch (insn.getType()) {
case LINE:
case FRAME:
return false;
case LABEL:
return controlFlowLabels.contains(insn);
default:
return true;
}
}
public static Set<LabelNode> getControlFlowLabels(InsnListSection list) {
return getControlFlowLabels(list.list);
}
public static Set<LabelNode> getControlFlowLabels(InsnList list) {
HashSet<LabelNode> controlFlowLabels = new HashSet<LabelNode>();
for (AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext()) {
switch (insn.getType()) {
case JUMP_INSN:
JumpInsnNode jinsn = (JumpInsnNode) insn;
controlFlowLabels.add(jinsn.label);
break;
case TABLESWITCH_INSN:
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode) insn;
controlFlowLabels.add(tsinsn.dflt);
for (LabelNode label : tsinsn.labels) controlFlowLabels.add(label);
break;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode) insn;
controlFlowLabels.add(lsinsn.dflt);
for (LabelNode label : lsinsn.labels) controlFlowLabels.add(label);
break;
}
}
return controlFlowLabels;
}
public static InsnList getImportantList(InsnList list) {
return getImportantList(new InsnListSection(list)).list;
}
public static InsnListSection getImportantList(InsnListSection list) {
if (list.size() == 0) return list;
Set<LabelNode> controlFlowLabels = getControlFlowLabels(list);
Map<LabelNode, LabelNode> labelMap = Maps.asMap(controlFlowLabels, new Function<LabelNode, LabelNode>() {
@Override
public LabelNode apply(LabelNode input) {
return input;
}
});
InsnListSection importantNodeList = new InsnListSection();
for (AbstractInsnNode insn : list)
if (insnImportant(insn, controlFlowLabels)) importantNodeList.add(insn.clone(labelMap));
return importantNodeList;
}
public static List<InsnListSection> find(InsnListSection haystack, InsnListSection needle) {
Set<LabelNode> controlFlowLabels = getControlFlowLabels(haystack);
LinkedList<InsnListSection> list = new LinkedList<InsnListSection>();
for (int start = 0; start <= haystack.size() - needle.size(); start++) {
InsnListSection section = matches(haystack.drop(start), needle, controlFlowLabels);
if (section != null) {
list.add(section);
start = section.end - 1;
}
}
return list;
}
public static List<InsnListSection> find(InsnList haystack, InsnListSection needle) {
return find(new InsnListSection(haystack), needle);
}
public static InsnListSection matches(InsnListSection haystack, InsnListSection needle,
Set<LabelNode> controlFlowLabels) {
int h = 0, n = 0;
for (; h < haystack.size() && n < needle.size(); h++) {
AbstractInsnNode insn = haystack.get(h);
if (!insnImportant(insn, controlFlowLabels)) continue;
if (!insnEqual(haystack.get(h), needle.get(n))) return null;
n++;
}
if (n != needle.size()) return null;
return haystack.take(h);
}
public static InsnListSection findOnce(InsnListSection haystack, InsnListSection needle) {
List<InsnListSection> list = find(haystack, needle);
if (list.size() != 1) throw new RuntimeException(
"Needle found " + list.size() + " times in Haystack:\n" + haystack + "\n\n" + needle);
return list.get(0);
}
public static InsnListSection findOnce(InsnList haystack, InsnListSection needle) {
return findOnce(new InsnListSection(haystack), needle);
}
public static List<InsnListSection> findN(InsnListSection haystack, InsnListSection needle) {
List<InsnListSection> list = find(haystack, needle);
if (list.isEmpty()) throw new RuntimeException("Needle not found in Haystack:\n" + haystack + "\n\n" + needle);
return list;
}
public static List<InsnListSection> findN(InsnList haystack, InsnListSection needle) {
return findN(new InsnListSection(haystack), needle);
}
}
@@ -1,235 +0,0 @@
package codechicken.lib.asm;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
/**
* A section of an InsnList, may become invalid if the insn list is modified
*/
public class InsnListSection implements Iterable<AbstractInsnNode> {
private class InsnListSectionIterator implements Iterator<AbstractInsnNode> {
int i = 0;
@Override
public boolean hasNext() {
return i < size();
}
@Override
public AbstractInsnNode next() {
return get(i++);
}
@Override
public void remove() {
InsnListSection.this.remove(--i);
}
}
public InsnList list;
public int start;
public int end;
public InsnListSection(InsnList list, int start, int end) {
this.list = list;
this.start = start;
this.end = end;
}
public InsnListSection(InsnList list, AbstractInsnNode first, AbstractInsnNode last) {
this(list, list.indexOf(first), list.indexOf(last) + 1);
}
public InsnListSection(InsnList list) {
this(list, 0, list.size());
}
public InsnListSection() {
this(new InsnList());
}
public void accept(MethodVisitor mv) {
for (AbstractInsnNode insn : this) insn.accept(mv);
}
public AbstractInsnNode getFirst() {
return size() == 0 ? null : list.get(start);
}
public AbstractInsnNode getLast() {
return size() == 0 ? null : list.get(end - 1);
}
public int size() {
return end - start;
}
public AbstractInsnNode get(int i) {
return list.get(start + i);
}
public void set(int i, AbstractInsnNode insn) {
list.set(get(i), insn);
}
public void remove(int i) {
list.remove(get(i));
end--;
}
public void replace(AbstractInsnNode location, AbstractInsnNode insn) {
list.set(location, insn);
}
public void add(AbstractInsnNode insn) {
list.add(insn);
end++;
}
public void insertBefore(InsnList insns) {
int s = insns.size();
if (this.list.size() == 0) list.insert(insns);
else list.insertBefore(list.get(start), insns);
start += s;
end += s;
}
public void insert(InsnList insns) {
if (end == 0) list.insert(insns);
else list.insert(list.get(end - 1), insns);
}
public void replace(InsnList insns) {
int s = insns.size();
remove();
insert(insns);
end = start + s;
}
public void remove() {
while (end != start) remove(0);
}
public void setLast(AbstractInsnNode last) {
end = list.indexOf(last) + 1;
}
public void setFirst(AbstractInsnNode first) {
start = list.indexOf(first);
}
public InsnListSection drop(int n) {
return slice(n, size());
}
public InsnListSection take(int n) {
return slice(0, n);
}
public InsnListSection slice(int start, int end) {
return new InsnListSection(list, this.start + start, this.start + end);
}
/**
* Removes leading and trailing labels and line number nodes that don't affect control flow
*
* @return this
*/
public InsnListSection trim(Set<LabelNode> controlFlowLabels) {
while (start < end && !InsnComparator.insnImportant(getFirst(), controlFlowLabels)) start++;
while (start < end && !InsnComparator.insnImportant(getLast(), controlFlowLabels)) end--;
return this;
}
public String toString() {
Textifier t = new Textifier();
accept(new TraceMethodVisitor(t));
StringWriter sw = new StringWriter();
t.print(new PrintWriter(sw));
return sw.toString();
}
public void println() {
System.out.println(toString());
}
public HashMap<LabelNode, LabelNode> identityLabelMap() {
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for (AbstractInsnNode insn : this) switch (insn.getType()) {
case LABEL:
labelMap.put((LabelNode) insn, (LabelNode) insn);
break;
case JUMP_INSN:
labelMap.put(((JumpInsnNode) insn).label, ((JumpInsnNode) insn).label);
break;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode linsn = (LookupSwitchInsnNode) insn;
labelMap.put(linsn.dflt, linsn.dflt);
for (LabelNode label : linsn.labels) labelMap.put(label, label);
break;
case TABLESWITCH_INSN:
TableSwitchInsnNode tinsn = (TableSwitchInsnNode) insn;
labelMap.put(tinsn.dflt, tinsn.dflt);
for (LabelNode label : tinsn.labels) labelMap.put(label, label);
break;
case FRAME:
FrameNode fnode = (FrameNode) insn;
if (fnode.local != null)
for (Object o : fnode.local) if (o instanceof LabelNode) labelMap.put((LabelNode) o, (LabelNode) o);
if (fnode.stack != null)
for (Object o : fnode.stack) if (o instanceof LabelNode) labelMap.put((LabelNode) o, (LabelNode) o);
break;
}
return labelMap;
}
public Map<LabelNode, LabelNode> cloneLabels() {
Map<LabelNode, LabelNode> labelMap = identityLabelMap();
for (Entry<LabelNode, LabelNode> entry : labelMap.entrySet()) entry.setValue(new LabelNode());
return labelMap;
}
public InsnListSection copy() {
return copy(cloneLabels());
}
public InsnListSection copy(Map<LabelNode, LabelNode> labelMap) {
InsnListSection copy = new InsnListSection();
for (AbstractInsnNode insn : this) copy.add(insn.clone(labelMap));
return copy;
}
@Override
public Iterator<AbstractInsnNode> iterator() {
return new InsnListSectionIterator();
}
}
@@ -1,37 +0,0 @@
package codechicken.lib.asm;
import java.util.Set;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.LocalVariablesSorter;
public class LocalVariablesSorterVisitor extends ClassVisitor {
public Set<ObfMapping> methods;
public String owner;
public LocalVariablesSorterVisitor(Set<ObfMapping> methods, ClassVisitor cv) {
super(Opcodes.ASM4, cv);
this.methods = methods;
}
public LocalVariablesSorterVisitor(ClassVisitor cv) {
this(null, cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
owner = name;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return methods == null || methods.contains(new ObfMapping(owner, name, desc))
? new LocalVariablesSorter(access, desc, mv)
: mv;
}
}
@@ -1,297 +0,0 @@
package codechicken.lib.asm;
import static codechicken.lib.asm.ASMHelper.config;
import static codechicken.lib.asm.ASMHelper.createBytes;
import static codechicken.lib.asm.ASMHelper.dump;
import static codechicken.lib.asm.ASMHelper.findMethod;
import static codechicken.lib.asm.ASMHelper.logger;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
public class ModularASMTransformer {
public static class ClassNodeTransformerList {
List<ClassNodeTransformer> transformers = new LinkedList<ClassNodeTransformer>();
HashSet<ObfMapping> methodsToSort = new HashSet<ObfMapping>();
public void add(ClassNodeTransformer t) {
transformers.add(t);
t.addMethodsToSort(methodsToSort);
}
public byte[] transform(byte[] bytes) {
ClassNode cnode = new ClassNode();
ClassReader reader = new ClassReader(bytes);
ClassVisitor cv = cnode;
if (!methodsToSort.isEmpty()) cv = new LocalVariablesSorterVisitor(methodsToSort, cv);
reader.accept(cv, ClassReader.EXPAND_FRAMES);
try {
int writeFlags = 0;
for (ClassNodeTransformer t : transformers) {
t.transform(cnode);
writeFlags |= t.writeFlags;
}
bytes = createBytes(cnode, writeFlags);
if (config.getTag("dump_asm").getBooleanValue(false))
dump(bytes, new File("asm/ccl_modular/" + cnode.name.replace('/', '#') + ".txt"), false, false);
return bytes;
} catch (RuntimeException e) {
dump(bytes, new File("asm/ccl_modular/" + cnode.name.replace('/', '#') + ".txt"), false, false);
throw e;
}
}
}
public abstract static class ClassNodeTransformer {
public int writeFlags;
public ClassNodeTransformer(int writeFlags) {
this.writeFlags = writeFlags;
}
public ClassNodeTransformer() {
this(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
public abstract String className();
public abstract void transform(ClassNode cnode);
public void addMethodsToSort(Set<ObfMapping> set) {}
}
public abstract static class MethodTransformer extends ClassNodeTransformer {
public final ObfMapping method;
public MethodTransformer(ObfMapping method) {
this.method = method.toClassloading();
}
@Override
public String className() {
return method.javaClass();
}
@Override
public void transform(ClassNode cnode) {
MethodNode mv = findMethod(method, cnode);
if (mv == null) throw new RuntimeException("Method not found: " + method);
try {
transform(mv);
} catch (Exception e) {
throw new RuntimeException("Error transforming method: " + method, e);
}
}
public abstract void transform(MethodNode mv);
}
public static class MethodWriter extends ClassNodeTransformer {
public final int access;
public final ObfMapping method;
public final String[] exceptions;
public InsnList list;
public MethodWriter(int access, ObfMapping method) {
this(access, method, null, (InsnList) null);
}
public MethodWriter(int access, ObfMapping method, InsnList list) {
this(access, method, null, list);
}
public MethodWriter(int access, ObfMapping method, ASMBlock block) {
this(access, method, null, block);
}
public MethodWriter(int access, ObfMapping method, String[] exceptions) {
this(access, method, exceptions, (InsnList) null);
}
public MethodWriter(int access, ObfMapping method, String[] exceptions, InsnList list) {
this.access = access;
this.method = method.toClassloading();
this.exceptions = exceptions;
this.list = list;
}
public MethodWriter(int access, ObfMapping method, String[] exceptions, ASMBlock block) {
this(access, method, exceptions, block.rawListCopy());
}
@Override
public String className() {
return method.javaClass();
}
@Override
public void transform(ClassNode cnode) {
MethodNode mv = findMethod(method, cnode);
if (mv == null) mv = (MethodNode) method.visitMethod(cnode, access, exceptions);
else {
mv.access = access;
mv.instructions.clear();
if (mv.localVariables != null) mv.localVariables.clear();
if (mv.tryCatchBlocks != null) mv.tryCatchBlocks.clear();
}
write(mv);
}
public void write(MethodNode mv) {
logger.debug("Writing method " + method);
list.accept(mv);
}
}
public static class MethodInjector extends MethodTransformer {
public ASMBlock needle;
public ASMBlock injection;
public boolean before;
public MethodInjector(ObfMapping method, ASMBlock needle, ASMBlock injection, boolean before) {
super(method);
this.needle = needle;
this.injection = injection;
this.before = before;
}
public MethodInjector(ObfMapping method, ASMBlock injection, boolean before) {
this(method, null, injection, before);
}
public MethodInjector(ObfMapping method, InsnList needle, InsnList injection, boolean before) {
this(method, new ASMBlock(needle), new ASMBlock(injection), before);
}
public MethodInjector(ObfMapping method, InsnList injection, boolean before) {
this(method, null, new ASMBlock(injection), before);
}
@Override
public void addMethodsToSort(Set<ObfMapping> set) {
set.add(method);
}
@Override
public void transform(MethodNode mv) {
if (needle == null) {
logger.debug("Injecting " + (before ? "before" : "after") + " method " + method);
if (before) mv.instructions.insert(injection.rawListCopy());
else mv.instructions.add(injection.rawListCopy());
} else {
for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) {
logger.debug(
"Injecting " + (before ? "before" : "after")
+ " method "
+ method
+ " @ "
+ key.start
+ " - "
+ key.end);
ASMBlock injectBlock = injection.copy().mergeLabels(needle.applyLabels(key));
if (before) key.insertBefore(injectBlock.list.list);
else key.insert(injectBlock.list.list);
}
}
}
}
public static class MethodReplacer extends MethodTransformer {
public ASMBlock needle;
public ASMBlock replacement;
public MethodReplacer(ObfMapping method, ASMBlock needle, ASMBlock replacement) {
super(method);
this.needle = needle;
this.replacement = replacement;
}
public MethodReplacer(ObfMapping method, InsnList needle, InsnList replacement) {
this(method, new ASMBlock(needle), new ASMBlock(replacement));
}
@Override
public void addMethodsToSort(Set<ObfMapping> set) {
set.add(method);
}
@Override
public void transform(MethodNode mv) {
for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) {
logger.debug("Replacing method " + method + " @ " + key.start + " - " + key.end);
ASMBlock replaceBlock = replacement.copy().pullLabels(needle.applyLabels(key));
key.insert(replaceBlock.list.list);
}
}
}
public static class FieldWriter extends ClassNodeTransformer {
public final ObfMapping field;
public final int access;
public final Object value;
public FieldWriter(int access, ObfMapping field, Object value) {
this.field = field.toClassloading();
this.access = access;
this.value = value;
}
public FieldWriter(int access, ObfMapping field) {
this(access, field, null);
}
@Override
public String className() {
return field.javaClass();
}
@Override
public void transform(ClassNode cnode) {
field.visitField(cnode, access, value);
}
}
public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<>();
public void add(ClassNodeTransformer t) {
ClassNodeTransformerList list = transformers.get(t.className());
if (list == null) {
transformers.put(t.className(), list = new ClassNodeTransformerList());
}
list.add(t);
}
public boolean isEmpty() {
return transformers.isEmpty();
}
public byte[] transform(String name, byte[] bytes) {
if (bytes == null) return null;
ClassNodeTransformerList list = transformers.get(name);
return list == null ? bytes : list.transform(bytes);
}
}
@@ -1,401 +0,0 @@
package codechicken.lib.asm;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JFileChooser;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.common.ForgeVersion;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.io.LineProcessor;
import com.google.common.io.Resources;
import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
import cpw.mods.fml.relauncher.FMLInjectionData;
public class ObfMapping {
public static class ObfRemapper extends Remapper {
private final HashMap<String, String> fields = new HashMap<>();
private final HashMap<String, String> funcs = new HashMap<>();
public ObfRemapper() {
try {
Field rawFieldMapsField = FMLDeobfuscatingRemapper.class.getDeclaredField("rawFieldMaps");
Field rawMethodMapsField = FMLDeobfuscatingRemapper.class.getDeclaredField("rawMethodMaps");
rawFieldMapsField.setAccessible(true);
rawMethodMapsField.setAccessible(true);
Map<String, Map<String, String>> rawFieldMaps = (Map<String, Map<String, String>>) rawFieldMapsField
.get(FMLDeobfuscatingRemapper.INSTANCE);
Map<String, Map<String, String>> rawMethodMaps = (Map<String, Map<String, String>>) rawMethodMapsField
.get(FMLDeobfuscatingRemapper.INSTANCE);
if (rawFieldMaps == null) throw new IllegalStateException(
"codechicken.lib.asm.ObfMapping loaded too early. Make sure all references are in or after the asm transformer load stage");
for (Map<String, String> map : rawFieldMaps.values())
for (Entry<String, String> entry : map.entrySet()) if (entry.getValue().startsWith("field"))
fields.put(entry.getValue(), entry.getKey().substring(0, entry.getKey().indexOf(':')));
for (Map<String, String> map : rawMethodMaps.values())
for (Entry<String, String> entry : map.entrySet()) if (entry.getValue().startsWith("func"))
funcs.put(entry.getValue(), entry.getKey().substring(0, entry.getKey().indexOf('(')));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String mapMethodName(String owner, String name, String desc) {
String s = funcs.get(name);
return s == null ? name : s;
}
@Override
public String mapFieldName(String owner, String name, String desc) {
String s = fields.get(name);
return s == null ? name : s;
}
@Override
public String map(String typeName) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(typeName);
}
public String unmap(String typeName) {
return FMLDeobfuscatingRemapper.INSTANCE.map(typeName);
}
public boolean isObf(String typeName) {
return !map(typeName).equals(typeName) || !unmap(typeName).equals(typeName);
}
}
public static class MCPRemapper extends Remapper implements LineProcessor<Void> {
public static File[] getConfFiles() {
ConfigTag tag = ASMHelper.config.getTag("mappingDir")
.setComment("Path to directory holding packaged.srg, fields.csv and methods.csv for mcp remapping");
for (int i = 0; i < DIR_GUESSES + DIR_ASKS; i++) {
File dir = confDirectoryGuess(i, tag);
if (dir == null || dir.isFile()) continue;
File[] mappings;
try {
mappings = parseConfDir(dir);
} catch (Exception e) {
if (i >= DIR_GUESSES) e.printStackTrace();
continue;
}
tag.setValue(dir.getPath());
return mappings;
}
throw new RuntimeException("Failed to select mappings directory, set it manually in the config");
}
private static final int DIR_GUESSES = 6;
private static final int DIR_ASKS = 3;
public static File confDirectoryGuess(int i, ConfigTag tag) {
File mcDir = (File) FMLInjectionData.data()[6];
switch (i) {
case 0:
return tag.value != null ? new File(tag.getValue()) : null;
case 1:
return new File(mcDir, "../conf");
case 2:
return new File(mcDir, "../build/unpacked/conf");
case 3:
return new File(
System.getProperty("user.home"),
".gradle/caches/minecraft/net/minecraftforge/forge/" + FMLInjectionData.data()[4]
+ "-"
+ ForgeVersion.getVersion()
+ "/unpacked/conf");
case 4:
return new File(
System.getProperty("user.home"),
".gradle/caches/minecraft/net/minecraftforge/forge/" + FMLInjectionData.data()[4]
+ "-"
+ ForgeVersion.getVersion()
+ "-"
+ FMLInjectionData.data()[4]
+ "/unpacked/conf");
case 5:
final String gradleCsvDir = System.getProperty("net.minecraftforge.gradle.GradleStart.csvDir");
return gradleCsvDir != null ? new File(gradleCsvDir) : null;
default:
JFileChooser fc = new JFileChooser(mcDir);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fc.setDialogTitle("Select an mcp conf dir for the deobfuscator.");
int ret = fc.showDialog(null, "Select");
return ret == JFileChooser.APPROVE_OPTION ? fc.getSelectedFile() : null;
}
}
public static File[] parseConfDir(File confDir) {
File srgDir = new File(confDir, "conf");
if (!srgDir.exists()) srgDir = confDir;
File srgs = new File(srgDir, "packaged.srg");
if (!srgs.exists()) srgs = new File(srgDir, "joined.srg");
if (!srgs.exists()) throw new RuntimeException("Could not find packaged.srg or joined.srg");
File mapDir = new File(confDir, "mappings");
if (!mapDir.exists()) mapDir = confDir;
File methods = new File(mapDir, "methods.csv");
if (!methods.exists()) throw new RuntimeException("Could not find methods.csv");
File fields = new File(mapDir, "fields.csv");
if (!fields.exists()) throw new RuntimeException("Could not find fields.csv");
return new File[] { srgs, methods, fields };
}
private final HashMap<String, String> fields = new HashMap<>();
private final HashMap<String, String> funcs = new HashMap<>();
public MCPRemapper() {
File[] mappings = getConfFiles();
try {
Resources.readLines(mappings[1].toURI().toURL(), Charsets.UTF_8, this);
Resources.readLines(mappings[2].toURI().toURL(), Charsets.UTF_8, this);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String mapMethodName(String owner, String name, String desc) {
String s = funcs.get(name);
return s == null ? name : s;
}
@Override
public String mapFieldName(String owner, String name, String desc) {
String s = fields.get(name);
return s == null ? name : s;
}
@Override
public boolean processLine(String line) throws IOException {
int i = line.indexOf(',');
String srg = line.substring(0, i);
int i2 = i + 1;
i = line.indexOf(',', i2);
String mcp = line.substring(i2, i);
(srg.startsWith("func") ? funcs : fields).put(srg, mcp);
return true;
}
@Override
public Void getResult() {
return null;
}
}
public static ObfRemapper obfMapper = new ObfRemapper();
public static Remapper mcpMapper = null;
public static void loadMCPRemapper() {
if (mcpMapper == null) mcpMapper = new MCPRemapper();
}
public static final boolean obfuscated;
static {
boolean obf = true;
try {
obf = Launch.classLoader.getClassBytes("net.minecraft.world.World") == null;
} catch (IOException ignored) {}
obfuscated = obf;
if (!obf) loadMCPRemapper();
}
public String s_owner;
public String s_name;
public String s_desc;
public ObfMapping(String owner) {
this(owner, "", "");
}
public ObfMapping(String owner, String name, String desc) {
this.s_owner = owner;
this.s_name = name;
this.s_desc = desc;
if (s_owner.contains(".")) throw new IllegalArgumentException(s_owner);
}
public ObfMapping(ObfMapping descmap, String subclass) {
this(subclass, descmap.s_name, descmap.s_desc);
}
public static ObfMapping fromDesc(String s) {
int lastDot = s.lastIndexOf('.');
if (lastDot < 0) return new ObfMapping(s, "", "");
int sep = s.indexOf('('); // methods
int sep_end = sep;
if (sep < 0) {
sep = s.indexOf(' '); // some stuffs
sep_end = sep + 1;
}
if (sep < 0) {
sep = s.indexOf(':'); // fields
sep_end = sep + 1;
}
if (sep < 0) return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot + 1), "");
return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot + 1, sep), s.substring(sep_end));
}
public ObfMapping subclass(String subclass) {
return new ObfMapping(this, subclass);
}
public boolean matches(MethodNode node) {
return s_name.equals(node.name) && s_desc.equals(node.desc);
}
public boolean matches(MethodInsnNode node) {
return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc);
}
public AbstractInsnNode toInsn(int opcode) {
if (isClass()) return new TypeInsnNode(opcode, s_owner);
else if (isMethod()) return new MethodInsnNode(opcode, s_owner, s_name, s_desc);
else return new FieldInsnNode(opcode, s_owner, s_name, s_desc);
}
public void visitTypeInsn(MethodVisitor mv, int opcode) {
mv.visitTypeInsn(opcode, s_owner);
}
public void visitMethodInsn(MethodVisitor mv, int opcode) {
mv.visitMethodInsn(opcode, s_owner, s_name, s_desc);
}
public void visitFieldInsn(MethodVisitor mv, int opcode) {
mv.visitFieldInsn(opcode, s_owner, s_name, s_desc);
}
public MethodVisitor visitMethod(ClassVisitor visitor, int access, String[] exceptions) {
return visitor.visitMethod(access, s_name, s_desc, null, exceptions);
}
public FieldVisitor visitField(ClassVisitor visitor, int access, Object value) {
return visitor.visitField(access, s_name, s_desc, null, value);
}
public boolean isClass(String name) {
return name.replace('.', '/').equals(s_owner);
}
public boolean matches(String name, String desc) {
return s_name.equals(name) && s_desc.equals(desc);
}
public boolean matches(FieldNode node) {
return s_name.equals(node.name) && s_desc.equals(node.desc);
}
public boolean matches(FieldInsnNode node) {
return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc);
}
public String javaClass() {
return s_owner.replace('/', '.');
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ObfMapping)) return false;
ObfMapping desc = (ObfMapping) obj;
return s_owner.equals(desc.s_owner) && s_name.equals(desc.s_name) && s_desc.equals(desc.s_desc);
}
@Override
public int hashCode() {
return Objects.hashCode(s_desc, s_name, s_owner);
}
@Override
public String toString() {
if (s_name.length() == 0) return "[" + s_owner + "]";
if (s_desc.length() == 0) return "[" + s_owner + "." + s_name + "]";
return "[" + (isMethod() ? methodDesc() : fieldDesc()) + "]";
}
public String methodDesc() {
return s_owner + "." + s_name + s_desc;
}
public String fieldDesc() {
return s_owner + "." + s_name + ":" + s_desc;
}
public boolean isClass() {
return s_name.length() == 0;
}
public boolean isMethod() {
return s_desc.contains("(");
}
public boolean isField() {
return !isClass() && !isMethod();
}
public ObfMapping map(Remapper mapper) {
if (mapper == null) return this;
if (isMethod()) s_name = mapper.mapMethodName(s_owner, s_name, s_desc);
else if (isField()) s_name = mapper.mapFieldName(s_owner, s_name, s_desc);
s_owner = mapper.mapType(s_owner);
if (isMethod()) s_desc = mapper.mapMethodDesc(s_desc);
else if (s_desc.length() > 0) s_desc = mapper.mapDesc(s_desc);
return this;
}
public ObfMapping toRuntime() {
map(mcpMapper);
return this;
}
public ObfMapping toClassloading() {
if (!obfuscated) map(mcpMapper);
else if (obfMapper.isObf(s_owner)) map(obfMapper);
return this;
}
public ObfMapping copy() {
return new ObfMapping(s_owner, s_name, s_desc);
}
}
@@ -1,205 +0,0 @@
package codechicken.lib.asm;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
import codechicken.core.launch.CodeChickenCorePlugin;
public class RedirectorTransformer implements IClassTransformer, Opcodes {
private static final boolean DUMP_CLASSES = Boolean.parseBoolean(System.getProperty("ccl.dumpClass", "false"));
private static final String RenderStateClass = "codechicken/lib/render/CCRenderState";
private static final Set<String> redirectedFields = new HashSet<>();
private static final Set<String> redirectedSimpleMethods = new HashSet<>();
private static final Set<String> redirectedMethods = new HashSet<>();
private static final ClassConstantPoolParser cstPoolParser;
static {
Collections.addAll(
redirectedFields,
"pipeline",
"model",
"firstVertexIndex",
"lastVertexIndex",
"vertexIndex",
"baseColour",
"alphaOverride",
"useNormals",
"computeLighting",
"useColour",
"lightMatrix",
"vert",
"hasNormal",
"normal",
"hasColour",
"colour",
"hasBrightness",
"brightness",
"side",
"lc",
"normalAttrib",
"colourAttrib",
"lightCoordAttrib",
"sideAttrib",
"lightingAttrib"
);
Collections.addAll(redirectedSimpleMethods, "reset", "pullLightmap", "pushLightmap", "setDynamic", "draw");
Collections.addAll(
redirectedMethods,
"setPipeline",
"bindModel",
"setModel",
"setVertexRange",
"render",
"runPipeline",
"writeVert",
"setNormal",
"setColour",
"setBrightness",
"startDrawing");
cstPoolParser = new ClassConstantPoolParser(RenderStateClass);
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (!cstPoolParser.find(basicClass)) {
return basicClass;
}
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
boolean changed = false;
// spotless:off
for (MethodNode mn : cn.methods) {
for (AbstractInsnNode node : mn.instructions.toArray()) {
if (node instanceof FieldInsnNode fNode) {
if (node.getOpcode() == GETSTATIC && RenderStateClass.equals(fNode.owner) && redirectedFields.contains(fNode.name)) {
mn.instructions.insertBefore(fNode,
new MethodInsnNode(
INVOKESTATIC,
fNode.owner,
"instance",
"()Lcodechicken/lib/render/CCRenderState;",
false));
fNode.setOpcode(GETFIELD);
changed = true;
} else if (node.getOpcode() == PUTSTATIC && RenderStateClass.equals(fNode.owner) && redirectedFields.contains(fNode.name)) {
InsnList list = new InsnList();
list.add(new MethodInsnNode(
INVOKESTATIC,
fNode.owner,
"instance",
"()Lcodechicken/lib/render/CCRenderState;",
false));
list.add(new InsnNode(SWAP));
mn.instructions.insertBefore(fNode, list);
fNode.setOpcode(PUTFIELD);
changed = true;
}
} else if (node instanceof MethodInsnNode mNode) {
if (node.getOpcode() == INVOKESTATIC && RenderStateClass.equals(mNode.owner) && redirectedSimpleMethods.contains(mNode.name)) {
mn.instructions.insertBefore(mNode,
new MethodInsnNode(
INVOKESTATIC,
mNode.owner,
"instance",
"()Lcodechicken/lib/render/CCRenderState;",
false));
mNode.setOpcode(INVOKEVIRTUAL);
mNode.name = mNode.name + "Instance";
changed = true;
} else if (node.getOpcode() == INVOKEVIRTUAL && RenderStateClass.equals(mNode.owner)
&& (redirectedSimpleMethods.contains(mNode.name) || redirectedMethods.contains(mNode.name))) {
// Handle mods that updated to previously new API
mNode.name = mNode.name + "Instance";
changed = true;
}
}
}
}
// spotless:on
if (changed) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
if (DUMP_CLASSES) {
saveTransformedClass(basicClass, transformedName + "_PRE");
saveTransformedClass(bytes, transformedName + "_POST");
}
return bytes;
}
return basicClass;
}
private File outputDir = null;
private void saveTransformedClass(final byte[] data, final String classname) {
if (outputDir == null) {
outputDir = new File(Launch.minecraftHome, "ASM_CCL" + File.separatorChar + "REDIRECTOR");
try {
FileUtils.deleteDirectory(outputDir);
} catch (IOException ignored) {}
if (!outputDir.exists()) {
// noinspection ResultOfMethodCallIgnored
outputDir.mkdirs();
}
}
final String fileName = classname.replace('.', File.separatorChar);
final File classFile = new File(outputDir, fileName + ".class");
final File bytecodeFile = new File(outputDir, fileName + "_BYTE.txt");
final File outDir = classFile.getParentFile();
if (!outDir.exists()) {
// noinspection ResultOfMethodCallIgnored
outDir.mkdirs();
}
if (classFile.exists()) {
// noinspection ResultOfMethodCallIgnored
classFile.delete();
}
try (final OutputStream output = Files.newOutputStream(classFile.toPath())) {
output.write(data);
CodeChickenCorePlugin.logger.info("Saved class (byte[]) to " + classFile.toPath());
} catch (IOException e) {
CodeChickenCorePlugin.logger.error("Could not save class (byte[]) " + classname);
}
if (bytecodeFile.exists()) {
// noinspection ResultOfMethodCallIgnored
bytecodeFile.delete();
}
try (final OutputStream output = Files.newOutputStream(bytecodeFile.toPath())) {
final ClassReader classReader = new ClassReader(data);
classReader.accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(output)), 0);
CodeChickenCorePlugin.logger.info("Saved class (bytecode) to " + bytecodeFile.toPath());
} catch (IOException e) {
CodeChickenCorePlugin.logger.error("Could not save class (bytecode) " + classname);
}
}
}
@@ -1,158 +0,0 @@
package codechicken.lib.colour;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.lwjgl.opengl.GL11;
import codechicken.lib.config.ConfigTag.IConfigType;
import codechicken.lib.math.MathHelper;
import codechicken.lib.util.Copyable;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public abstract class Colour implements Copyable<Colour> {
public static IConfigType<Colour> configRGB = new IConfigType<Colour>() {
@Override
public String configValue(Colour entry) {
String s = Long.toString(((long) entry.rgb()) << 32 >>> 32, 16);
while (s.length() < 6) s = "0" + s;
return "0x" + s.toUpperCase();
}
private final Pattern patternRGB = Pattern.compile("(\\d+),(\\d+),(\\d+)");
@Override
public Colour valueOf(String text) throws Exception {
Matcher matcherRGB = patternRGB.matcher(text.replaceAll("\\s", ""));
if (matcherRGB.matches()) return new ColourRGBA(
Integer.parseInt(matcherRGB.group(1)),
Integer.parseInt(matcherRGB.group(2)),
Integer.parseInt(matcherRGB.group(3)),
0xFF);
int hex = (int) Long.parseLong(text.replace("0x", ""), 16);
return new ColourRGBA(hex << 8 | 0xFF);
}
};
public byte r;
public byte g;
public byte b;
public byte a;
public Colour(int r, int g, int b, int a) {
this.r = (byte) r;
this.g = (byte) g;
this.b = (byte) b;
this.a = (byte) a;
}
public Colour(Colour colour) {
r = colour.r;
g = colour.g;
b = colour.b;
a = colour.a;
}
@SideOnly(Side.CLIENT)
public void glColour() {
GL11.glColor4ub(r, g, b, a);
}
@SideOnly(Side.CLIENT)
public void glColour(int a) {
GL11.glColor4ub(r, g, b, (byte) a);
}
public abstract int pack();
@Override
public String toString() {
return getClass().getSimpleName() + "[0x" + Integer.toHexString(pack()).toUpperCase() + "]";
}
public Colour add(Colour colour2) {
a += colour2.a;
r += colour2.r;
g += colour2.g;
b += colour2.b;
return this;
}
public Colour sub(Colour colour2) {
int ia = (a & 0xFF) - (colour2.a & 0xFF);
int ir = (r & 0xFF) - (colour2.r & 0xFF);
int ig = (g & 0xFF) - (colour2.g & 0xFF);
int ib = (b & 0xFF) - (colour2.b & 0xFF);
a = (byte) (ia < 0 ? 0 : ia);
r = (byte) (ir < 0 ? 0 : ir);
g = (byte) (ig < 0 ? 0 : ig);
b = (byte) (ib < 0 ? 0 : ib);
return this;
}
public Colour invert() {
a = (byte) (0xFF - (a & 0xFF));
r = (byte) (0xFF - (r & 0xFF));
g = (byte) (0xFF - (g & 0xFF));
b = (byte) (0xFF - (b & 0xFF));
return this;
}
public Colour multiply(Colour colour2) {
a = (byte) ((a & 0xFF) * ((colour2.a & 0xFF) / 255D));
r = (byte) ((r & 0xFF) * ((colour2.r & 0xFF) / 255D));
g = (byte) ((g & 0xFF) * ((colour2.g & 0xFF) / 255D));
b = (byte) ((b & 0xFF) * ((colour2.b & 0xFF) / 255D));
return this;
}
public Colour scale(double d) {
a = (byte) ((a & 0xFF) * d);
r = (byte) ((r & 0xFF) * d);
g = (byte) ((g & 0xFF) * d);
b = (byte) ((b & 0xFF) * d);
return this;
}
public Colour interpolate(Colour colour2, double d) {
return this.add(colour2.copy().sub(this).scale(d));
}
public Colour multiplyC(double d) {
r = (byte) MathHelper.clip((r & 0xFF) * d, 0, 255);
g = (byte) MathHelper.clip((g & 0xFF) * d, 0, 255);
b = (byte) MathHelper.clip((b & 0xFF) * d, 0, 255);
return this;
}
public abstract Colour copy();
public int rgb() {
return (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
}
public int argb() {
return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
}
public int rgba() {
return (r & 0xFF) << 24 | (g & 0xFF) << 16 | (b & 0xFF) << 8 | (a & 0xFF);
}
public Colour set(Colour colour) {
r = colour.r;
g = colour.g;
b = colour.b;
a = colour.a;
return this;
}
public boolean equals(Colour colour) {
return colour != null && rgba() == colour.rgba();
}
}
@@ -1,28 +0,0 @@
package codechicken.lib.colour;
public class ColourARGB extends Colour {
public ColourARGB(int colour) {
super((colour >> 16) & 0xFF, (colour >> 8) & 0xFF, colour & 0xFF, (colour >> 24) & 0xFF);
}
public ColourARGB(int a, int r, int g, int b) {
super(r, g, b, a);
}
public ColourARGB(ColourARGB colour) {
super(colour);
}
public ColourARGB copy() {
return new ColourARGB(this);
}
public int pack() {
return pack(this);
}
public static int pack(Colour colour) {
return (colour.a & 0xFF) << 24 | (colour.r & 0xFF) << 16 | (colour.g & 0xFF) << 8 | (colour.b & 0xFF);
}
}
@@ -1,50 +0,0 @@
package codechicken.lib.colour;
public class ColourRGBA extends Colour {
public ColourRGBA(int colour) {
super((colour >> 24) & 0xFF, (colour >> 16) & 0xFF, (colour >> 8) & 0xFF, colour & 0xFF);
}
public ColourRGBA(double r, double g, double b, double a) {
super((int) (255 * r), (int) (255 * g), (int) (255 * b), (int) (255 * a));
}
public ColourRGBA(int r, int g, int b, int a) {
super(r, g, b, a);
}
public ColourRGBA(ColourRGBA colour) {
super(colour);
}
public int pack() {
return pack(this);
}
@Override
public Colour copy() {
return new ColourRGBA(this);
}
public static int pack(Colour colour) {
return (colour.r & 0xFF) << 24 | (colour.g & 0xFF) << 16 | (colour.b & 0xFF) << 8 | (colour.a & 0xFF);
}
public static int multiply(int c1, int c2) {
if (c1 == -1) return c2;
if (c2 == -1) return c1;
int r = (((c1 >>> 24) * (c2 >>> 24)) & 0xFF00) << 16;
int g = (((c1 >> 16 & 0xFF) * (c2 >> 16 & 0xFF)) & 0xFF00) << 8;
int b = ((c1 >> 8 & 0xFF) * (c2 >> 8 & 0xFF)) & 0xFF00;
int a = ((c1 & 0xFF) * (c2 & 0xFF)) >> 8;
return r | g | b | a;
}
public static int multiplyC(int c, float f) {
int r = (int) ((c >>> 24) * f);
int g = (int) ((c >> 16 & 0xFF) * f);
int b = (int) ((c >> 8 & 0xFF) * f);
return r << 24 | g << 16 | b << 8 | c & 0xFF;
}
}
@@ -1,32 +0,0 @@
package codechicken.lib.colour;
import java.awt.image.BufferedImage;
import net.minecraft.util.ResourceLocation;
import codechicken.lib.math.MathHelper;
import codechicken.lib.render.TextureUtils;
public class CustomGradient {
public int[] gradient;
public CustomGradient(ResourceLocation textureFile) {
BufferedImage img = TextureUtils.loadBufferedImage(textureFile);
if (img == null) throw new RuntimeException("File not found: " + textureFile.toString());
int[] data = new int[img.getWidth()];
img.getRGB(0, 0, img.getWidth(), 1, data, 0, img.getWidth());
gradient = new int[img.getWidth()];
for (int i = 0; i < data.length; i++) gradient[i] = (data[i] << 8) | (((data[i]) >> 24) & 0xFF);
}
public ColourRGBA getColour(double position) {
return new ColourRGBA(getColourI(position));
}
public int getColourI(double position) {
int off = (int) MathHelper.clip(gradient.length * position, 0, gradient.length - 1);
return gradient[off];
}
}
@@ -1,86 +0,0 @@
package codechicken.lib.colour;
import net.minecraft.util.StatCollector;
public final class LocalizedColours {
private static final String KEY_PREFIX = "codechickencore.color.";
public static int BUTTON_TEXT_DISABLED = 0xFFA0A0A0;
public static int BUTTON_TEXT_HOVER = 0xFFFFFFA0;
public static int BUTTON_TEXT = 0xFFE0E0E0;
public static int TEXT_FIELD_BORDER = 0xFFA0A0A0;
public static int TEXT_FIELD_BACKGROUND = 0xFF000000;
public static int TEXT_FIELD_TEXT = 0xE0E0E0;
public static int TEXT_FIELD_TEXT_DISABLED = 0x707070;
public static int SCROLL_PANE_BACKGROUND = 0xFF000000;
public static int SCROLL_PANE_OVERLAY_TOP = 0xFFA0A0A0;
public static int SCROLL_PANE_OVERLAY_BOTTOM = 0xFFA0A0A0;
public static int SCROLL_PANE_OVERLAY_LEFT = 0xFFA0A0A0;
public static int SCROLL_PANE_OVERLAY_RIGHT = 0xFFA0A0A0;
public static int SCROLLBAR_CORNERS = 0xFF8B8B8B;
public static int SCROLLBAR_TOP_LEFT = 0xFFF0F0F0;
public static int SCROLLBAR_BOTTOM_RIGHT = 0xFF555555;
public static int SCROLLBAR_FILL = 0xFFC6C6C6;
public static int SCROLLBAR_GUIDE = 0xFF808080;
public static int TOOLTIP_BG_START = 0xF0100010;
public static int TOOLTIP_BG_END = 0xF0100010;
public static int TOOLTIP_BORDER_START = 0x505000FF;
public static int TOOLTIP_BORDER_END = 0x5028007F;
public static int TOOLTIP_TEXT = 0xFFFFFFFF;
public static int ITEM_QUANTITY_TEXT = 0xFFFFFF;
public static int MOD_DESCRIPTION_TEXT = 0xDDDDDD;
private LocalizedColours() {}
public static void reloadLocalizedColours() {
BUTTON_TEXT_DISABLED = getLocalizedColor("buttonTextDisabled", BUTTON_TEXT_DISABLED);
BUTTON_TEXT_HOVER = getLocalizedColor("buttonTextHover", BUTTON_TEXT_HOVER);
BUTTON_TEXT = getLocalizedColor("buttonText", BUTTON_TEXT);
TEXT_FIELD_BORDER = getLocalizedColor("textFieldBorder", TEXT_FIELD_BORDER);
TEXT_FIELD_BACKGROUND = getLocalizedColor("textFieldBackground", TEXT_FIELD_BACKGROUND);
TEXT_FIELD_TEXT = getLocalizedColor("textFieldText", TEXT_FIELD_TEXT);
TEXT_FIELD_TEXT_DISABLED = getLocalizedColor("textFieldTextDisabled", TEXT_FIELD_TEXT_DISABLED);
SCROLL_PANE_BACKGROUND = getLocalizedColor("background", SCROLL_PANE_BACKGROUND);
SCROLL_PANE_OVERLAY_TOP = getLocalizedColor("overlayTop", SCROLL_PANE_OVERLAY_TOP);
SCROLL_PANE_OVERLAY_BOTTOM = getLocalizedColor("overlayBottom", SCROLL_PANE_OVERLAY_BOTTOM);
SCROLL_PANE_OVERLAY_LEFT = getLocalizedColor("overlayLeft", SCROLL_PANE_OVERLAY_LEFT);
SCROLL_PANE_OVERLAY_RIGHT = getLocalizedColor("overlayRight", SCROLL_PANE_OVERLAY_RIGHT);
SCROLLBAR_CORNERS = getLocalizedColor("scrollbarCorners", SCROLLBAR_CORNERS);
SCROLLBAR_TOP_LEFT = getLocalizedColor("scrollbarTopLeft", SCROLLBAR_TOP_LEFT);
SCROLLBAR_BOTTOM_RIGHT = getLocalizedColor("scrollbarBottomRight", SCROLLBAR_BOTTOM_RIGHT);
SCROLLBAR_FILL = getLocalizedColor("scrollbarFill", SCROLLBAR_FILL);
SCROLLBAR_GUIDE = getLocalizedColor("scrollbarGuide", SCROLLBAR_GUIDE);
TOOLTIP_BG_START = getLocalizedColor("tooltipBgStart", TOOLTIP_BG_START);
TOOLTIP_BG_END = getLocalizedColor("tooltipBgEnd", TOOLTIP_BG_END);
TOOLTIP_BORDER_START = getLocalizedColor("tooltipBorderStart", TOOLTIP_BORDER_START);
TOOLTIP_BORDER_END = getLocalizedColor("tooltipBorderEnd", TOOLTIP_BORDER_END);
TOOLTIP_TEXT = getLocalizedColor("tooltipText", TOOLTIP_TEXT);
ITEM_QUANTITY_TEXT = getLocalizedColor("itemQuantityText", ITEM_QUANTITY_TEXT);
MOD_DESCRIPTION_TEXT = getLocalizedColor("modDescriptionText", MOD_DESCRIPTION_TEXT);
}
/**
* Resolve a optional localized ARGB color from lang files. Expected value format: AARRGGBB (for example FF555555).
*/
private static int getLocalizedColor(String key, int defaultColor) {
String fullKey = KEY_PREFIX + key;
if (!StatCollector.canTranslate(fullKey)) {
return defaultColor;
}
String raw = StatCollector.translateToLocal(fullKey);
String hex = raw.trim();
if (hex.startsWith("0x") || hex.startsWith("0X")) {
hex = hex.substring(2);
} else if (hex.startsWith("#")) {
hex = hex.substring(1);
}
try {
return (int) Long.parseLong(hex, 16);
} catch (NumberFormatException ignored) {
return defaultColor;
}
}
}
@@ -1,110 +0,0 @@
package codechicken.lib.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
public class ConfigFile extends ConfigTagParent {
public static final byte[] crlf = new byte[] { 0xD, 0xA };
public File file;
private boolean loading;
public ConfigFile(File file) {
newlinemode = 2;
load(file);
}
protected ConfigFile() {}
protected void load(File file) {
try {
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
if (!file.exists()) file.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
this.file = file;
loadConfig();
}
protected void loadConfig() {
loading = true;
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(file));
while (true) {
reader.mark(2000);
String line = reader.readLine();
if (line != null && line.startsWith("#")) {
if (comment == null || comment.equals("")) comment = line.substring(1);
else comment = comment + "\n" + line.substring(1);
} else {
reader.reset();
break;
}
}
loadChildren(reader);
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
loading = false;
}
@Override
public ConfigFile setComment(String header) {
super.setComment(header);
return this;
}
@Override
public ConfigFile setSortMode(int mode) {
super.setSortMode(mode);
return this;
}
@Override
public String getNameQualifier() {
return "";
}
public static String readLine(BufferedReader reader) throws IOException {
String line = reader.readLine();
return line == null ? null : line.replace("\t", "");
}
public static void writeLine(PrintWriter writer, String line, int tabs) {
for (int i = 0; i < tabs; i++) writer.print('\t');
writer.println(line);
}
public void saveConfig() {
if (loading) return;
PrintWriter writer;
try {
writer = new PrintWriter(file);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
writeComment(writer, 0);
ConfigFile.writeLine(writer, "", 0);
saveTagTree(writer, 0, "");
writer.flush();
writer.close();
}
public boolean isLoading() {
return loading;
}
}
@@ -1,225 +0,0 @@
package codechicken.lib.config;
import java.io.PrintWriter;
public class ConfigTag extends ConfigTagParent {
public interface IConfigType<T> {
public String configValue(T entry);
public T valueOf(String text) throws Exception;
}
public ConfigTag(ConfigTagParent parent, String name) {
this.parent = parent;
this.name = name;
qualifiedname = parent.getNameQualifier() + name;
newline = parent.newlinemode == 2;
parent.addChild(this);
}
@Override
public String getNameQualifier() {
return qualifiedname + ".";
}
@Override
public void saveConfig() {
parent.saveConfig();
}
/**
* Called when the tag is loaded from a config file as opposed to constructed by a mod
*
* @return this
*/
public ConfigTag onLoaded() {
return this;
}
public void setValue(String value) {
this.value = value;
saveConfig();
}
public void setDefaultValue(String defaultValue) {
if (value == null) {
value = defaultValue;
saveConfig();
}
}
public void setIntValue(int i) {
setValue(Integer.toString(i));
}
public void setBooleanValue(boolean b) {
setValue(Boolean.toString(b));
}
public void setHexValue(int i) {
setValue("0x" + Long.toString(((long) i) << 32 >>> 32, 16));
}
public <T> void set(IConfigType<T> type, T entry) {
setValue(type.configValue(entry));
}
public String getValue() {
return value;
}
public String getValue(String defaultValue) {
setDefaultValue(defaultValue);
return value;
}
public int getIntValue() {
return Integer.parseInt(getValue());
}
public int getIntValue(int defaultValue) {
try {
if (value != null) return getIntValue();
} catch (NumberFormatException ignored) {}
setIntValue(defaultValue);
return defaultValue;
}
public boolean getBooleanValue() {
String value = getValue();
if (value != null && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes"))) return true;
else if (value != null && (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no"))) return false;
throw new NumberFormatException(qualifiedname + ".value=" + value);
}
public boolean getBooleanValue(boolean defaultValue) {
try {
if (value != null) return getBooleanValue();
} catch (NumberFormatException ignored) {}
setBooleanValue(defaultValue);
return defaultValue;
}
public int getHexValue() {
return (int) Long.parseLong(getValue().replace("0x", ""), 16);
}
public int getHexValue(int defaultValue) {
try {
if (value != null) return getHexValue();
} catch (NumberFormatException ignored) {}
setHexValue(defaultValue);
return defaultValue;
}
public <T> T get(IConfigType<T> type) {
try {
return type.valueOf(getValue());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> T get(IConfigType<T> type, T defaultValue) {
try {
if (value != null) return get(type);
} catch (Exception ignored) {}
set(type, defaultValue);
return defaultValue;
}
public void save(PrintWriter writer, int tabs, String bracequalifier, boolean first) {
String vname;
if (qualifiedname.contains(".") && bracequalifier.length() > 0)
vname = qualifiedname.substring(bracequalifier.length() + 1);
else vname = qualifiedname;
if (newline && !first) ConfigFile.writeLine(writer, "", tabs);
writeComment(writer, tabs);
if (value != null) ConfigFile.writeLine(writer, vname + "=" + value, tabs);
if (!hasChildTags()) return;
if (brace) {
if (value == null) ConfigFile.writeLine(writer, vname, tabs);
ConfigFile.writeLine(writer, "{", tabs);
saveTagTree(writer, tabs + 1, qualifiedname);
ConfigFile.writeLine(writer, "}", tabs);
} else {
saveTagTree(writer, tabs, bracequalifier);
}
}
@Override
public ConfigTag setComment(String comment) {
super.setComment(comment);
return this;
}
@Override
public ConfigTag setSortMode(int mode) {
super.setSortMode(mode);
return this;
}
public ConfigTag setNewLine(boolean b) {
newline = b;
saveConfig();
return this;
}
public ConfigTag useBraces() {
brace = true;
if (parent.newlinemode == 1) newline = true;
saveConfig();
return this;
}
public ConfigTag setPosition(int pos) {
position = pos;
saveConfig();
return this;
}
public boolean containsTag(String tagname) {
return getTag(tagname, false) != null;
}
public int getId(String name, int defaultValue) {
return getTag(name).getIntValue(defaultValue);
}
public int getId(String name) {
int ret = getId(name, IDBase);
IDBase = ret + 1;
return ret;
}
public int getAcheivementId(String name, int defaultValue) {
return getTag(name).getIntValue(defaultValue);
}
public ConfigTag setBaseID(int i) {
IDBase = i;
return this;
}
public ConfigTagParent parent;
public String name;
public String qualifiedname;
public String value;
public boolean brace;
public boolean newline;
public int position = Integer.MAX_VALUE;
private int IDBase;
}
@@ -1,220 +0,0 @@
package codechicken.lib.config;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap;
public abstract class ConfigTagParent {
public static class TagOrderComparator implements Comparator<ConfigTag> {
int sortMode;
public TagOrderComparator(int sortMode) {
this.sortMode = sortMode;
}
public int compare(ConfigTag o1, ConfigTag o2) {
if (o1.position != o2.position) return Integer.compare(o1.position, o2.position);
if (o1.brace != o2.brace) return o1.brace ? 1 : -1; // braced one goes after
if (sortMode == 1) {
if (Objects.equals(o1.value, o2.value)) return 0;
if (o1.value == null) return 1;
if (o2.value == null) return -1;
return o1.value.compareTo(o2.value);
} else {
return o1.name.compareTo(o2.name);
}
}
}
/**
* The immediate child tags of this tag
*/
private final TreeMap<String, ConfigTag> childtags = new TreeMap<>();
/**
* A map of all tags in the tree, meaning all tags in this tag and all tags in child tags (recursively). Allows for
* quick access to any tag in the tree, without the need to traverse the tree part by part.
*/
private final Map<String, ConfigTag> allTagsInTree = new HashMap<>();
public String comment;
/**
* 0 = name, 1 = value
*/
public int sortMode = 0;
/**
* The mode for determining when child tags should leave a blank line between them and the one above 0 = never, 1 =
* when braced, 2 = always
*/
public int newlinemode = 1;
public abstract void saveConfig();
public abstract String getNameQualifier();
public ConfigTagParent setComment(String comment) {
this.comment = comment;
saveConfig();
return this;
}
public ConfigTagParent setSortMode(int mode) {
sortMode = mode;
saveConfig();
return this;
}
public ConfigTagParent setNewLineMode(int mode) {
newlinemode = mode;
for (Entry<String, ConfigTag> entry : childtags.entrySet()) {
ConfigTag tag = entry.getValue();
if (newlinemode == 0) tag.newline = false;
else if (newlinemode == 1) tag.newline = tag.brace;
else if (newlinemode == 2) tag.newline = true;
}
saveConfig();
return this;
}
public Map<String, ConfigTag> childTagMap() {
return childtags;
}
public boolean hasChildTags() {
return !childtags.isEmpty();
}
public boolean containsTag(String tagname) {
return allTagsInTree.get(tagname) != null;
}
public ConfigTag getNewTag(String tagname) {
return new ConfigTag(this, tagname);
}
public ConfigTag getTag(String tagname, boolean create) {
ConfigTag tag = allTagsInTree.get(tagname);
if (tag == null && create) return getTagRecursive(tagname, create);
return tag;
}
public ConfigTag getTagRecursive(String tagname, boolean create) {
int dotpos = tagname.indexOf(".");
String basetagname = dotpos == -1 ? tagname : tagname.substring(0, dotpos);
ConfigTag basetag = childtags.get(basetagname);
if (basetag == null) {
if (!create) return null;
basetag = getNewTag(basetagname);
saveConfig();
}
if (dotpos == -1) return basetag;
return basetag.getTag(tagname.substring(dotpos + 1), create);
}
public ConfigTag getTag(String tagname) {
return getTag(tagname, true);
}
public boolean removeTag(String tagname) {
removeChildFromFlatMap(tagname);
ConfigTag tag = getTag(tagname, false);
if (tag == null) return false;
int dotpos = tagname.lastIndexOf(".");
String lastpart = dotpos == -1 ? tagname : tagname.substring(dotpos + 1, tagname.length());
if (tag.parent != null) {
boolean ret = tag.parent.childtags.remove(lastpart) != null;
if (ret) saveConfig();
return ret;
}
return false;
}
public void addChild(ConfigTag tag) {
childtags.put(tag.name, tag);
addChildToFlatMap(tag.name, tag);
}
private void addChildToFlatMap(String path, ConfigTag tag) {
allTagsInTree.put(path, tag);
// Stupid cast but these two garbage classes are interlinked anyway
if ((this instanceof ConfigTag t) && t.parent != null) t.parent.addChildToFlatMap(t.name + "." + path, tag);
}
private void removeChildFromFlatMap(String path) {
allTagsInTree.remove(path);
// Stupid cast but these two garbage classes are interlinked anyway
if ((this instanceof ConfigTag t) && t.parent != null) t.parent.removeChildFromFlatMap(t.name + "." + path);
}
public <T extends ConfigTag> ArrayList<T> getSortedTagList() {
ArrayList<T> taglist = new ArrayList<T>(childtags.size());
for (Entry<String, ConfigTag> tag : childtags.entrySet()) taglist.add((T) tag.getValue());
Collections.sort(taglist, new TagOrderComparator(sortMode));
return taglist;
}
public void loadChildren(BufferedReader reader) {
String comment = "";
String bracequalifier = "";
try {
while (true) {
String line = ConfigFile.readLine(reader);
if (line == null) break;
if (line.startsWith("#")) {
if (comment.equals("")) comment = line.substring(1);
else comment = comment + "\n" + line.substring(1);
} else if (line.contains("=")) {
String qualifiedname = line.substring(0, line.indexOf("="));
getTag(qualifiedname).onLoaded().setComment(comment)
.setValue(line.substring(line.indexOf("=") + 1));
comment = "";
bracequalifier = qualifiedname;
} else if (line.equals("{")) {
getTag(bracequalifier).setComment(comment).useBraces().loadChildren(reader);
comment = "";
bracequalifier = "";
} else if (line.equals("}")) {
break;
} else {
bracequalifier = line;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void saveTagTree(PrintWriter writer, int tabs, String bracequalifier) {
boolean first = true;
for (ConfigTag tag : getSortedTagList()) {
tag.save(writer, tabs, bracequalifier, first);
first = false;
}
}
public void writeComment(PrintWriter writer, int tabs) {
if (comment != null && !comment.equals("")) {
String[] comments = comment.split("\n");
for (int i = 0; i < comments.length; i++) ConfigFile.writeLine(writer, "#" + comments[i], tabs);
}
}
}
@@ -1,16 +0,0 @@
package codechicken.lib.config;
import java.io.File;
public class DefaultingConfigFile extends ConfigFile {
public DefaultingConfigFile(File file) {
super();
if (file.exists()) load(file);
}
@Override
public void saveConfig() {
if (file != null) super.saveConfig();
}
}
@@ -1,125 +0,0 @@
package codechicken.lib.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map.Entry;
public class SimpleProperties {
public HashMap<String, String> propertyMap = new HashMap<String, String>();
public File propertyFile;
public boolean saveOnChange = false;
public String encoding;
private boolean loading = false;
public SimpleProperties(File file, boolean saveOnChange, String encoding) {
propertyFile = file;
this.saveOnChange = saveOnChange;
this.encoding = encoding;
}
public SimpleProperties(File file, boolean saveOnChange) {
this(file, saveOnChange, Charset.defaultCharset().name());
}
public SimpleProperties(File file) {
this(file, true);
}
public void load() {
clear();
loading = true;
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(propertyFile), encoding));
while (true) {
String read = reader.readLine();
if (read == null) break;
int equalIndex = read.indexOf('=');
if (equalIndex == -1) continue;
setProperty(read.substring(0, equalIndex), read.substring(equalIndex + 1));
}
reader.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
loading = false;
}
public void save() {
try {
PrintStream writer = new PrintStream(propertyFile);
for (Entry<String, String> entry : propertyMap.entrySet()) {
writer.println(entry.getKey() + "=" + entry.getValue());
}
writer.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void clear() {
propertyMap.clear();
}
public boolean hasProperty(String key) {
return propertyMap.containsKey(key);
}
public void removeProperty(String key) {
if (propertyMap.remove(key) != null && saveOnChange && !loading) save();
}
public void setProperty(String key, int value) {
setProperty(key, Integer.toString(value));
}
public void setProperty(String key, boolean value) {
setProperty(key, Boolean.toString(value));
}
public void setProperty(String key, String value) {
propertyMap.put(key, value);
if (saveOnChange && !loading) save();
}
public int getProperty(String property, int defaultvalue) {
try {
return Integer.parseInt(getProperty(property, Integer.toString(defaultvalue)));
} catch (NumberFormatException nfe) {
return defaultvalue;
}
}
public boolean getProperty(String property, boolean defaultvalue) {
try {
return Boolean.parseBoolean(getProperty(property, Boolean.toString(defaultvalue)));
} catch (NumberFormatException nfe) {
return defaultvalue;
}
}
public String getProperty(String property, String defaultvalue) {
String value = propertyMap.get(property);
if (value == null) {
setProperty(property, defaultvalue);
return defaultvalue;
}
return value;
}
public String getProperty(String property) {
return propertyMap.get(property);
}
}
@@ -1,46 +0,0 @@
package codechicken.lib.data;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import codechicken.lib.vec.BlockCoord;
public interface MCDataInput {
public long readLong();
public int readInt();
public short readShort();
public int readUShort();
public byte readByte();
public short readUByte();
public double readDouble();
public float readFloat();
public boolean readBoolean();
public char readChar();
public int readVarShort();
public int readVarInt();
public byte[] readByteArray(int length);
public String readString();
public BlockCoord readCoord();
public NBTTagCompound readNBTTagCompound();
public ItemStack readItemStack();
public FluidStack readFluidStack();
}
@@ -1,17 +0,0 @@
package codechicken.lib.data;
import java.io.InputStream;
public class MCDataInputStream extends InputStream {
private MCDataInput in;
public MCDataInputStream(MCDataInput in) {
this.in = in;
}
@Override
public int read() {
return in.readByte() & 0xFF;
}
}
@@ -1,44 +0,0 @@
package codechicken.lib.data;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import codechicken.lib.vec.BlockCoord;
public interface MCDataOutput {
public MCDataOutput writeLong(long l);
public MCDataOutput writeInt(int i);
public MCDataOutput writeShort(int s);
public MCDataOutput writeByte(int b);
public MCDataOutput writeDouble(double d);
public MCDataOutput writeFloat(float f);
public MCDataOutput writeBoolean(boolean b);
public MCDataOutput writeChar(char c);
public MCDataOutput writeVarInt(int i);
public MCDataOutput writeVarShort(int s);
public MCDataOutput writeByteArray(byte[] array);
public MCDataOutput writeString(String s);
public MCDataOutput writeCoord(int x, int y, int z);
public MCDataOutput writeCoord(BlockCoord coord);
public MCDataOutput writeNBTTagCompound(NBTTagCompound tag);
public MCDataOutput writeItemStack(ItemStack stack);
public MCDataOutput writeFluidStack(FluidStack liquid);
}
@@ -1,17 +0,0 @@
package codechicken.lib.data;
import java.io.OutputStream;
public class MCDataOutputStream extends OutputStream {
private MCDataOutput out;
public MCDataOutputStream(MCDataOutput out) {
this.out = out;
}
@Override
public void write(int b) {
out.writeByte(b);
}
}
@@ -1,240 +0,0 @@
package codechicken.lib.data;
import java.io.DataOutput;
import java.io.IOException;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import org.apache.commons.lang3.Validate;
import com.google.common.base.Charsets;
import codechicken.lib.vec.BlockCoord;
import cpw.mods.fml.common.network.ByteBufUtils;
public class MCDataOutputWrapper implements MCDataOutput {
/**
* Mimics ByteBufUtils Write an integer using variable length encoding.
*
* @param to The buffer to write to
* @param i The integer to write
*/
public static void writeVarInt(DataOutput to, int i) throws IOException {
while ((i & 0x80) != 0) {
to.writeByte(i & 0x7F | 0x80);
i >>>= 7;
}
to.writeByte(i);
}
/**
* Mimics ByteBufUtils Write an extended short using a short and a byte if necessary
*
* @param to The buffer to write to
* @param s The short to write, less than 0x7FFFFF
*/
public static void writeVarShort(DataOutput to, int s) throws IOException {
int low = s & 0x7FFF;
int high = (s & 0x7F8000) >> 15;
if (high != 0) low |= 0x8000;
to.writeShort(low);
if (high != 0) to.writeByte(high);
}
/**
* Mimics ByteBufUtils Write a String with UTF8 byte encoding to the buffer. It is encoded as <varint length>[<UTF8
* char bytes>]
*
* @param to the data output to write to
* @param string The string to write
*/
public static void writeUTF8String(DataOutput to, String string) throws IOException {
byte[] utf8Bytes = string.getBytes(Charsets.UTF_8);
Validate.isTrue(
ByteBufUtils.varIntByteCount(utf8Bytes.length) < 3,
"The string is too long for this encoding.");
writeVarInt(to, utf8Bytes.length);
to.write(utf8Bytes);
}
public DataOutput dataout;
public MCDataOutputWrapper(DataOutput out) {
dataout = out;
}
public MCDataOutputWrapper writeBoolean(boolean b) {
try {
dataout.writeBoolean(b);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeByte(int b) {
try {
dataout.writeByte(b);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeShort(int s) {
try {
dataout.writeShort(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeInt(int i) {
try {
dataout.writeInt(i);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeFloat(float f) {
try {
dataout.writeFloat(f);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeDouble(double d) {
try {
dataout.writeDouble(d);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeLong(long l) {
try {
dataout.writeLong(l);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public MCDataOutputWrapper writeChar(char c) {
try {
dataout.writeChar(c);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public MCDataOutput writeVarInt(int i) {
try {
writeVarInt(dataout, i);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public MCDataOutput writeVarShort(int s) {
try {
writeVarShort(dataout, s);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeByteArray(byte[] barray) {
try {
dataout.write(barray);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeCoord(int x, int y, int z) {
writeInt(x);
writeInt(y);
writeInt(z);
return this;
}
public MCDataOutputWrapper writeCoord(BlockCoord coord) {
writeInt(coord.x);
writeInt(coord.y);
writeInt(coord.z);
return this;
}
public MCDataOutputWrapper writeString(String s) {
try {
writeUTF8String(dataout, s);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeItemStack(ItemStack stack) {
writeItemStack(stack, false);
return this;
}
public MCDataOutputWrapper writeItemStack(ItemStack stack, boolean large) {
if (stack == null) {
writeInt(-1);
} else {
writeInt(Item.getIdFromItem(stack.getItem()));
if (large) writeInt(stack.stackSize);
else writeByte(stack.stackSize);
writeShort(stack.getItemDamage());
writeNBTTagCompound(stack.stackTagCompound);
}
return this;
}
public MCDataOutputWrapper writeNBTTagCompound(NBTTagCompound compound) {
try {
if (compound == null) {
writeShort(-1);
} else {
byte[] bytes = CompressedStreamTools.compress(compound);
writeShort((short) bytes.length);
writeByteArray(bytes);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public MCDataOutputWrapper writeFluidStack(FluidStack fluid) {
if (fluid == null) {
writeShort(-1);
} else {
writeShort(fluid.getFluidID());
writeVarInt(fluid.amount);
writeNBTTagCompound(fluid.tag);
}
return this;
}
}
@@ -1,81 +0,0 @@
package codechicken.lib.gui;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.render.TextureDataHolder;
import codechicken.lib.render.TextureUtils;
public class Canvas9Seg {
public final ResourceLocation tex;
public float[] seg_u = new float[4];
public float[] seg_v = new float[4];
public int[] seg_w = new int[3];
public int[] seg_h = new int[3];
public Canvas9Seg(ResourceLocation tex) {
this.tex = tex;
load();
}
private int[] readMarkers(TextureDataHolder data, int stride, int size) {
int[] markers = new int[4];
int marker = 1;
int prev_col = data.data[0];
for (int i = 1; i < size; i++) {
if (data.data[i * stride] != prev_col) {
markers[marker] = i;
prev_col = data.data[i * stride];
if (++marker == 4) break;
}
}
markers[0] += 1;
markers[3] -= 1;
return markers;
}
private void parseMarkers(TextureDataHolder data, int stride, int size, int[] sizes, float[] texcoords) {
int[] markers = readMarkers(data, stride, size);
for (int i = 0; i < 4; i++) {
texcoords[i] = markers[i] / (float) size;
if (i > 0) sizes[i - 1] = markers[i] - markers[i - 1];
}
}
private void load() {
TextureDataHolder data = TextureUtils.loadTexture(tex);
parseMarkers(data, 1, data.width, seg_w, seg_u);
parseMarkers(data, data.width, data.height, seg_h, seg_v);
}
private void drawSeg(int[] sw, int[] sh, int seg) {
Tessellator t = Tessellator.instance;
int u = seg % 3;
int v = seg / 3;
t.addVertexWithUV(sw[u], sh[v], 0, seg_u[u], seg_v[v]);
t.addVertexWithUV(sw[u], sh[v + 1], 0, seg_u[u], seg_v[v + 1]);
t.addVertexWithUV(sw[u + 1], sh[v + 1], 0, seg_u[u + 1], seg_v[v + 1]);
t.addVertexWithUV(sw[u + 1], sh[v], 0, seg_u[u + 1], seg_v[v]);
}
public void draw(CCRenderState state, int x, int y, int w, int h) {
CCRenderState.changeTexture(tex);
state.resetInstance();
state.startDrawingInstance();
int[] sw = new int[] { x, x + seg_w[0], x + w - seg_w[2], x + w };
int[] sh = new int[] { y, y + seg_h[0], y + h - seg_h[2], y + h };
for (int seg = 0; seg < 9; seg++) drawSeg(sw, sh, seg);
state.drawInstance();
}
public void draw(int x, int y, int w, int h) {
draw(CCRenderState.instance(), x, y, w, h);
}
}
@@ -1,254 +0,0 @@
package codechicken.lib.gui;
import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper;
import codechicken.lib.render.CCRenderState;
public class GuiDraw {
public static class GuiHook extends Gui {
public void setZLevel(float f) {
zLevel = f;
}
public float getZLevel() {
return zLevel;
}
public void incZLevel(float f) {
zLevel += f;
}
@Override
public void drawGradientRect(int par1, int par2, int par3, int par4, int par5, int par6) {
super.drawGradientRect(par1, par2, par3, par4, par5, par6);
}
}
public static final GuiHook gui = new GuiHook();
public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
public static TextureManager renderEngine = Minecraft.getMinecraft().renderEngine;
public static void drawRect(int x, int y, int w, int h, int colour) {
drawGradientRect(x, y, w, h, colour, colour);
}
public static void drawGradientRect(int x, int y, int w, int h, int colour1, int colour2) {
gui.drawGradientRect(x, y, x + w, y + h, colour1, colour2);
}
public static void drawTexturedModalRect(int x, int y, int tx, int ty, int w, int h) {
gui.drawTexturedModalRect(x, y, tx, ty, w, h);
}
public static void drawString(String text, int x, int y, int colour, boolean shadow) {
if (shadow) fontRenderer.drawStringWithShadow(text, x, y, colour);
else fontRenderer.drawString(text, x, y, colour);
}
public static void drawString(String text, int x, int y, int colour) {
drawString(text, x, y, colour, true);
}
public static void drawStringC(String text, int x, int y, int w, int h, int colour, boolean shadow) {
drawString(text, x + (w - getStringWidth(text)) / 2, y + (h - 8) / 2, colour, shadow);
}
public static void drawStringC(String text, int x, int y, int w, int h, int colour) {
drawStringC(text, x, y, w, h, colour, true);
}
public static void drawStringC(String text, int x, int y, int colour, boolean shadow) {
drawString(text, x - getStringWidth(text) / 2, y, colour, shadow);
}
public static void drawStringC(String text, int x, int y, int colour) {
drawStringC(text, x, y, colour, true);
}
public static void drawStringR(String text, int x, int y, int colour, boolean shadow) {
drawString(text, x - getStringWidth(text), y, colour, shadow);
}
public static void drawStringR(String text, int x, int y, int colour) {
drawStringR(text, x, y, colour, true);
}
public static int getStringWidth(String s) {
return fontRenderer.getStringWidth(s);
}
public static Dimension displaySize() {
Minecraft mc = Minecraft.getMinecraft();
ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
return new Dimension(res.getScaledWidth(), res.getScaledHeight());
}
public static Dimension displayRes() {
Minecraft mc = Minecraft.getMinecraft();
return new Dimension(mc.displayWidth, mc.displayHeight);
}
public static Point getMousePosition(int eventX, int eventY) {
Dimension size = displaySize();
Dimension res = displayRes();
return new Point(eventX * size.width / res.width, size.height - eventY * size.height / res.height - 1);
}
public static Point getMousePosition() {
return getMousePosition(Mouse.getX(), Mouse.getY());
}
public static void changeTexture(String s) {
CCRenderState.changeTexture(s);
}
public static void changeTexture(ResourceLocation r) {
CCRenderState.changeTexture(r);
}
public static void drawTip(int x, int y, String text) {
drawMultilineTip(x, y, Arrays.asList(text));
}
/**
* Append a string in the tooltip list with TOOLTIP_LINESPACE to have a small gap between it and the next line
*/
public static final String TOOLTIP_LINESPACE = "\u00A7h";
/**
* Have a string in the tooltip list with TOOLTIP_HANDLER + getTipLineId(handler) for a custom handler
*/
public static final String TOOLTIP_HANDLER = "\u00A7x";
private static List<ITooltipLineHandler> tipLineHandlers = new ArrayList<ITooltipLineHandler>();
public static interface ITooltipLineHandler {
public Dimension getSize();
public void draw(int x, int y);
}
public static int getTipLineId(ITooltipLineHandler handler) {
tipLineHandlers.add(handler);
return tipLineHandlers.size() - 1;
}
public static ITooltipLineHandler getTipLine(String line) {
if (!line.startsWith(TOOLTIP_HANDLER)) return null;
return tipLineHandlers.get(Integer.parseInt(line.substring(2)));
}
public static void drawMultilineTip(int x, int y, List<String> list) {
drawMultilineTip(fontRenderer, x, y, list);
}
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list) {
drawMultilineTip(
font,
x,
y,
list,
LocalizedColours.TOOLTIP_BG_START,
LocalizedColours.TOOLTIP_BG_END,
LocalizedColours.TOOLTIP_BORDER_START,
LocalizedColours.TOOLTIP_BORDER_END);
}
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list, int bgStart, int bgEnd,
int borderStart, int borderEnd) {
if (list.isEmpty()) return;
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
GL11.glDisable(GL11.GL_DEPTH_TEST);
RenderHelper.disableStandardItemLighting();
int w = 0;
int h = -2;
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
ITooltipLineHandler line = getTipLine(s);
Dimension d = line != null ? line.getSize()
: new Dimension(
font.getStringWidth(s),
list.get(i).endsWith(TOOLTIP_LINESPACE) && i + 1 < list.size() ? 12 : 10);
w = Math.max(w, d.width);
h += d.height;
}
if (x < 8) x = 8;
else if (x > displaySize().width - w - 8) {
x -= 24 + w; // flip side of cursor
if (x < 8) x = 8;
}
y = (int) MathHelper.clip(y, 8, displaySize().height - 8 - h);
gui.incZLevel(300);
drawTooltipBox(x - 4, y - 4, w + 7, h + 7, bgStart, bgEnd, borderStart, borderEnd);
for (String s : list) {
ITooltipLineHandler line = getTipLine(s);
if (line != null) {
line.draw(x, y);
y += line.getSize().height;
} else {
font.drawStringWithShadow(s, x, y, LocalizedColours.TOOLTIP_TEXT);
y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10;
}
}
tipLineHandlers.clear();
gui.incZLevel(-300);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
RenderHelper.enableGUIStandardItemLighting();
}
public static void drawTooltipBox(int x, int y, int w, int h) {
drawTooltipBox(
x,
y,
w,
h,
LocalizedColours.TOOLTIP_BG_START,
LocalizedColours.TOOLTIP_BG_END,
LocalizedColours.TOOLTIP_BORDER_START,
LocalizedColours.TOOLTIP_BORDER_END);
}
public static void drawTooltipBox(int x, int y, int w, int h, int bgStart, int bgEnd, int borderStart,
int borderEnd) {
// spotless:off
// draw background
drawGradientRect(x + 1, y, w - 1, 1, bgStart, bgStart); // top
drawGradientRect(x + 1, y + h, w - 1, 1, bgEnd, bgEnd); // bottom
drawGradientRect(x + 1, y + 1, w - 1, h - 1, bgStart, bgEnd); // center
drawGradientRect(x, y + 1, 1, h - 1, bgStart, bgEnd); // left
drawGradientRect(x + w, y + 1, 1, h - 1, bgStart, bgEnd); // right
// draw inner border
drawGradientRect(x + 1, y + 2, 1, h - 3, borderStart, borderEnd); // left
drawGradientRect(x + w - 1, y + 2, 1, h - 3, borderStart, borderEnd); // right
drawGradientRect(x + 1, y + 1, w - 1, 1, borderStart, borderStart); // top
drawGradientRect(x + 1, y + h - 1, w - 1, 1, borderEnd, borderEnd); // bottom
// spotless:on
}
}
@@ -1,274 +0,0 @@
package codechicken.lib.inventory;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ICrafting;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.INetHandlerPlayClient;
import net.minecraft.network.play.INetHandlerPlayServer;
import org.apache.logging.log4j.LogManager;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.relauncher.Side;
/**
* Clean container implementation with a few extra features. Easy shift-click handling. Hooks for slot clicks, large
* stack sizes and some networking.
*/
public abstract class ContainerExtended extends Container implements ICrafting {
private static final String netChannel = "CCL:Container";
private static int nextNetworkID = 0;
static {
PacketCustom.assignHandler(netChannel, new IClientPacketHandler() {
@Override
public void handlePacket(PacketCustom packet, Minecraft mc, INetHandlerPlayClient handler) {
Container cont = mc.thePlayer.openContainer;
if (!(cont instanceof ContainerExtended)) return;
ContainerExtended c = (ContainerExtended) cont;
if (packet.getType() == 1) c.netID = packet.readInt();
else if (c.netID == packet.readInt()) c.handleClientPacket(packet);
}
});
PacketCustom.assignHandler(netChannel, new IServerPacketHandler() {
@Override
public void handlePacket(PacketCustom packet, EntityPlayerMP sender, INetHandlerPlayServer handler) {
Container cont = sender.openContainer;
if (!(cont instanceof ContainerExtended)) return;
ContainerExtended c = (ContainerExtended) cont;
if (c.netID == packet.readInt()) c.handleServerPacket(packet);
}
});
}
public LinkedList<EntityPlayerMP> playerCrafters = new LinkedList<EntityPlayerMP>();
private int netID;
public ContainerExtended() {
crafters.add(this);
if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) netID = ++nextNetworkID;
}
@Override
public void addCraftingToCrafters(ICrafting icrafting) {
if (icrafting instanceof EntityPlayerMP) {
playerCrafters.add((EntityPlayerMP) icrafting);
sendNetID((EntityPlayerMP) icrafting);
sendContainerAndContentsToPlayer(this, getInventory(), Arrays.asList((EntityPlayerMP) icrafting));
detectAndSendChanges();
} else super.addCraftingToCrafters(icrafting);
}
private void sendNetID(EntityPlayerMP player) {
if (netID == 0) LogManager.getLogger("CodeChickenLib").error("Player added to container with 0 network ID");
else new PacketCustom(netChannel, 1).writeInt(netID).sendToPlayer(player);
}
@Override
public void removeCraftingFromCrafters(ICrafting icrafting) {
if (icrafting instanceof EntityPlayerMP) playerCrafters.remove(icrafting);
else super.removeCraftingFromCrafters(icrafting);
}
@Override
public void sendContainerAndContentsToPlayer(Container container, List list) {
sendContainerAndContentsToPlayer(container, list, playerCrafters);
}
public void sendContainerAndContentsToPlayer(Container container, List<ItemStack> list,
List<EntityPlayerMP> playerCrafters) {
LinkedList<ItemStack> largeStacks = new LinkedList<ItemStack>();
for (int i = 0; i < list.size(); i++) {
ItemStack stack = list.get(i);
if (stack != null && stack.stackSize > Byte.MAX_VALUE) {
list.set(i, null);
largeStacks.add(stack);
} else largeStacks.add(null);
}
for (EntityPlayerMP player : playerCrafters) player.sendContainerAndContentsToPlayer(container, list);
for (int i = 0; i < largeStacks.size(); i++) {
ItemStack stack = largeStacks.get(i);
if (stack != null) sendLargeStack(stack, i, playerCrafters);
}
}
public void sendLargeStack(ItemStack stack, int slot, List<EntityPlayerMP> players) {}
@Override
public void sendProgressBarUpdate(Container container, int i, int j) {
for (EntityPlayerMP player : playerCrafters) player.sendProgressBarUpdate(container, i, j);
}
@Override
public void sendSlotContents(Container container, int slot, ItemStack stack) {
if (stack != null && stack.stackSize > Byte.MAX_VALUE) sendLargeStack(stack, slot, playerCrafters);
else for (EntityPlayerMP player : playerCrafters) player.sendSlotContents(container, slot, stack);
}
@Override
public ItemStack slotClick(int par1, int par2, int par3, EntityPlayer player) {
if (par1 >= 0 && par1 < inventorySlots.size()) {
Slot slot = getSlot(par1);
if (slot instanceof SlotHandleClicks) return ((SlotHandleClicks) slot).slotClick(this, player, par2, par3);
}
return super.slotClick(par1, par2, par3, player);
}
@Override
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int slotIndex) {
ItemStack transferredStack = null;
Slot slot = (Slot) inventorySlots.get(slotIndex);
if (slot != null && slot.getHasStack()) {
ItemStack stack = slot.getStack();
transferredStack = stack.copy();
if (!doMergeStackAreas(slotIndex, stack)) return null;
if (stack.stackSize == 0) slot.putStack(null);
else slot.onSlotChanged();
}
return transferredStack;
}
@Override
public boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverse) {
boolean merged = false;
int slotIndex = reverse ? endIndex - 1 : startIndex;
if (stack == null) return false;
if (stack.isStackable()) // search for stacks to increase
{
while (stack.stackSize > 0 && (reverse ? slotIndex >= startIndex : slotIndex < endIndex)) {
Slot slot = (Slot) inventorySlots.get(slotIndex);
ItemStack slotStack = slot.getStack();
if (slotStack != null && slotStack.getItem() == stack.getItem()
&& (!stack.getHasSubtypes() || stack.getItemDamage() == slotStack.getItemDamage())
&& ItemStack.areItemStackTagsEqual(stack, slotStack)) {
int totalStackSize = slotStack.stackSize + stack.stackSize;
int maxStackSize = Math.min(stack.getMaxStackSize(), slot.getSlotStackLimit());
if (totalStackSize <= maxStackSize) {
stack.stackSize = 0;
slotStack.stackSize = totalStackSize;
slot.onSlotChanged();
merged = true;
} else if (slotStack.stackSize < maxStackSize) {
stack.stackSize -= maxStackSize - slotStack.stackSize;
slotStack.stackSize = maxStackSize;
slot.onSlotChanged();
merged = true;
}
}
slotIndex += reverse ? -1 : 1;
}
}
if (stack.stackSize > 0) // normal transfer :)
{
slotIndex = reverse ? endIndex - 1 : startIndex;
while (stack.stackSize > 0 && (reverse ? slotIndex >= startIndex : slotIndex < endIndex)) {
Slot slot = (Slot) this.inventorySlots.get(slotIndex);
if (!slot.getHasStack() && slot.isItemValid(stack)) {
int maxStackSize = Math.min(stack.getMaxStackSize(), slot.getSlotStackLimit());
if (stack.stackSize <= maxStackSize) {
slot.putStack(stack.copy());
slot.onSlotChanged();
stack.stackSize = 0;
merged = true;
} else {
slot.putStack(stack.splitStack(maxStackSize));
slot.onSlotChanged();
merged = true;
}
}
slotIndex += reverse ? -1 : 1;
}
}
return merged;
}
/**
* Called when slotIndex is shift clicked on. Recommended implementation is to call mergeItemStack based on
* slotIndex
*
* @param stack The stack in the clicked slot
* @return True if one or more items were moved from this slots into other slots
*/
public boolean doMergeStackAreas(int slotIndex, ItemStack stack) {
return false;
}
protected void bindPlayerInventory(InventoryPlayer inventoryPlayer) {
bindPlayerInventory(inventoryPlayer, 8, 84);
}
protected void bindPlayerInventory(InventoryPlayer inventoryPlayer, int x, int y) {
for (int row = 0; row < 3; row++) for (int col = 0; col < 9; col++)
addSlotToContainer(new Slot(inventoryPlayer, col + row * 9 + 9, x + col * 18, y + row * 18));
for (int slot = 0; slot < 9; slot++) addSlotToContainer(new Slot(inventoryPlayer, slot, x + slot * 18, y + 58));
}
@Override
public boolean canInteractWith(EntityPlayer var1) {
return true;
}
public void sendContainerPacket(PacketCustom packet) {
for (EntityPlayerMP player : playerCrafters) packet.sendToPlayer(player);
}
/**
* @param type An identifying number for the packet type between 2 and 0x79 inclusive. 1 is reserved for
* synchronising networkIDs
* @return A packet on the CCL inventory channel that will be recieved by this container on the other network side
*/
public PacketCustom getPacket(int type) {
if (netID == 0)
LogManager.getLogger("CodeChickenLib").error("Tried to get packet for container with 0 network ID");
if (type == 1) throw new IllegalArgumentException(
"Packet type 1 is reserved for network synchronisation in ContainerExtended");
return new PacketCustom(netChannel, type).writeInt(netID);
}
/**
* Handle a packet from the server obtained by getPacket.
*/
public void handleClientPacket(PacketCustom packet) {}
/**
* Handle a packet from the client obtained by getPacket.
*/
public void handleServerPacket(PacketCustom packet) {}
public void sendProgressBarUpdate(int barID, int value) {
for (ICrafting crafting : (List<ICrafting>) crafters) crafting.sendProgressBarUpdate(this, barID, value);
}
}
@@ -1,67 +0,0 @@
package codechicken.lib.inventory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import codechicken.lib.packet.PacketCustom;
public abstract class ContainerSynchronised extends ContainerExtended {
private ArrayList<IContainerSyncVar> syncVars = new ArrayList<IContainerSyncVar>();
/**
* Create a packet to be used to send a synced variable update. Calls getPacket. Can be overriden to add extra
* identifying data or change the type (default 2)
*/
public PacketCustom createSyncPacket() {
return getPacket(2);
}
@Override
public final void detectAndSendChanges() {
super.detectAndSendChanges();
for (int i = 0; i < syncVars.size(); i++) {
IContainerSyncVar var = syncVars.get(i);
if (var.changed()) {
PacketCustom packet = createSyncPacket();
packet.writeByte(i);
var.writeChange(packet);
sendContainerPacket(packet);
var.reset();
}
}
}
@Override
public void sendContainerAndContentsToPlayer(Container container, List<ItemStack> list,
List<EntityPlayerMP> playerCrafters) {
super.sendContainerAndContentsToPlayer(container, list, playerCrafters);
for (int i = 0; i < syncVars.size(); i++) {
IContainerSyncVar var = syncVars.get(i);
PacketCustom packet = createSyncPacket();
packet.writeByte(i);
var.writeChange(packet);
var.reset();
for (EntityPlayerMP player : playerCrafters) packet.sendToPlayer(player);
}
}
public void addSyncVar(IContainerSyncVar var) {
syncVars.add(var);
}
@Override
public final void handleClientPacket(PacketCustom packet) {
syncVars.get(packet.readUByte()).readChange(packet);
}
public List<IContainerSyncVar> getSyncedVars() {
return Collections.unmodifiableList(syncVars);
}
}
@@ -1,14 +0,0 @@
package codechicken.lib.inventory;
import codechicken.lib.packet.PacketCustom;
public interface IContainerSyncVar {
public boolean changed();
public void reset();
public void writeChange(PacketCustom packet);
public void readChange(PacketCustom packet);
}
@@ -1,30 +0,0 @@
package codechicken.lib.inventory;
import codechicken.lib.packet.PacketCustom;
public abstract class IntegerSync implements IContainerSyncVar {
public int c_value;
@Override
public boolean changed() {
return getValue() != c_value;
}
@Override
public void reset() {
c_value = getValue();
}
@Override
public void writeChange(PacketCustom packet) {
packet.writeInt(getValue());
}
@Override
public void readChange(PacketCustom packet) {
c_value = packet.readInt();
}
public abstract int getValue();
}
@@ -1,103 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
/**
* Creates a copy of an IInventory for extended simulation
*/
public class InventoryCopy implements IInventory {
public boolean[] accessible;
public ItemStack[] items;
public IInventory inv;
public InventoryCopy(IInventory inv) {
items = new ItemStack[inv.getSizeInventory()];
accessible = new boolean[inv.getSizeInventory()];
this.inv = inv;
update();
}
public void update() {
for (int i = 0; i < items.length; i++) {
ItemStack stack = inv.getStackInSlot(i);
if (stack != null) items[i] = stack.copy();
}
}
public InventoryCopy open(InventoryRange access) {
int lslot = access.lastSlot();
if (lslot > accessible.length) {
boolean[] l_accessible = new boolean[lslot];
ItemStack[] l_items = new ItemStack[lslot];
System.arraycopy(accessible, 0, l_accessible, 0, accessible.length);
System.arraycopy(items, 0, l_items, 0, items.length);
accessible = l_accessible;
items = l_items;
}
for (int slot : access.slots) accessible[slot] = true;
return this;
}
@Override
public int getSizeInventory() {
return items.length;
}
@Override
public ItemStack getStackInSlot(int slot) {
return items[slot];
}
public ItemStack decrStackSize(int slot, int amount) {
return InventoryUtils.decrStackSize(this, slot, amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
return InventoryUtils.getStackInSlotOnClosing(this, slot);
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
items[slot] = stack;
markDirty();
}
@Override
public String getInventoryName() {
return "copy";
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return true;
}
@Override
public void openInventory() {}
@Override
public void closeInventory() {}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public void markDirty() {}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
return inv.isItemValidForSlot(i, itemstack);
}
@Override
public boolean hasCustomInventoryName() {
return true;
}
}
@@ -1,91 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
/**
* IInventory implementation which saves and loads from an NBT tag
*/
public class InventoryNBT implements IInventory {
protected ItemStack[] items;
protected NBTTagCompound tag;
public InventoryNBT(int size, NBTTagCompound tag) {
this.tag = tag;
items = new ItemStack[size];
readNBT();
}
private void writeNBT() {
tag.setTag("items", InventoryUtils.writeItemStacksToTag(items, getInventoryStackLimit()));
}
private void readNBT() {
if (tag.hasKey("items")) InventoryUtils.readItemStacksFromTag(items, tag.getTagList("items", 10));
}
@Override
public int getSizeInventory() {
return items.length;
}
@Override
public ItemStack getStackInSlot(int slot) {
return items[slot];
}
@Override
public ItemStack decrStackSize(int slot, int amount) {
return InventoryUtils.decrStackSize(this, slot, amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
return InventoryUtils.getStackInSlotOnClosing(this, slot);
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
items[slot] = stack;
markDirty();
}
@Override
public String getInventoryName() {
return "NBT";
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public void markDirty() {
writeNBT();
}
@Override
public boolean isUseableByPlayer(EntityPlayer var1) {
return true;
}
@Override
public void openInventory() {}
@Override
public void closeInventory() {}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
return true;
}
@Override
public boolean hasCustomInventoryName() {
return true;
}
}
@@ -1,59 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
/**
* Inventory wrapper for unified ISided/IInventory access
*/
public class InventoryRange {
public IInventory inv;
public int side;
public ISidedInventory sidedInv;
public int[] slots;
public InventoryRange(IInventory inv, int side) {
this.inv = inv;
this.side = side;
if (inv instanceof ISidedInventory) {
sidedInv = (ISidedInventory) inv;
slots = sidedInv.getAccessibleSlotsFromSide(side);
} else {
slots = new int[inv.getSizeInventory()];
for (int i = 0; i < slots.length; i++) slots[i] = i;
}
}
public InventoryRange(IInventory inv) {
this(inv, 0);
}
public InventoryRange(IInventory inv, int fslot, int size) {
this.inv = inv;
slots = new int[size];
for (int i = 0; i < slots.length; i++) slots[i] = fslot + i;
}
public InventoryRange(IInventory inv, InventoryRange access) {
this.inv = inv;
this.slots = access.slots;
this.side = access.side;
if (inv instanceof ISidedInventory) sidedInv = (ISidedInventory) inv;
}
public boolean canInsertItem(int slot, ItemStack item) {
return sidedInv == null ? inv.isItemValidForSlot(slot, item) : sidedInv.canInsertItem(slot, item, side);
}
public boolean canExtractItem(int slot, ItemStack item) {
return sidedInv == null ? inv.isItemValidForSlot(slot, item) : sidedInv.canExtractItem(slot, item, side);
}
public int lastSlot() {
int last = 0;
for (int slot : slots) if (slot > last) last = slot;
return last;
}
}
@@ -1,109 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
/**
* Simple IInventory implementation with an array of items, name and maximum stack size
*/
public class InventorySimple implements IInventory {
public ItemStack[] items;
public int limit;
public String name;
public InventorySimple(ItemStack[] items, int limit, String name) {
this.items = items;
this.limit = limit;
this.name = name;
}
public InventorySimple(ItemStack[] items, String name) {
this(items, 64, name);
}
public InventorySimple(ItemStack[] items, int limit) {
this(items, limit, "inv");
}
public InventorySimple(ItemStack[] items) {
this(items, 64, "inv");
}
public InventorySimple(int size, int limit, String name) {
this(new ItemStack[size], limit, name);
}
public InventorySimple(int size, int limit) {
this(size, limit, "inv");
}
public InventorySimple(int size, String name) {
this(size, 64, name);
}
public InventorySimple(int size) {
this(size, 64, "inv");
}
@Override
public int getSizeInventory() {
return items.length;
}
@Override
public ItemStack getStackInSlot(int slot) {
return items[slot];
}
@Override
public ItemStack decrStackSize(int slot, int amount) {
return InventoryUtils.decrStackSize(this, slot, amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
return InventoryUtils.getStackInSlotOnClosing(this, slot);
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
items[slot] = stack;
markDirty();
}
@Override
public String getInventoryName() {
return name;
}
@Override
public int getInventoryStackLimit() {
return limit;
}
@Override
public boolean isUseableByPlayer(EntityPlayer var1) {
return true;
}
@Override
public void openInventory() {}
@Override
public void closeInventory() {}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
return true;
}
@Override
public boolean hasCustomInventoryName() {
return true;
}
@Override
public void markDirty() {}
}
@@ -1,323 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryLargeChest;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import com.google.common.base.Objects;
import codechicken.lib.vec.Vector3;
public class InventoryUtils {
/**
* Constructor for ItemStack with tag
*/
public static ItemStack newItemStack(Item item, int size, int damage, NBTTagCompound tag) {
ItemStack stack = new ItemStack(item, size, damage);
stack.stackTagCompound = tag;
return stack;
}
/**
* Gets the actual damage of an item without asking the Item
*/
public static int actualDamage(ItemStack stack) {
return Items.diamond.getDamage(stack);
}
/**
* Static default implementation for IInventory method
*/
public static ItemStack decrStackSize(IInventory inv, int slot, int size) {
ItemStack item = inv.getStackInSlot(slot);
if (item != null) {
if (item.stackSize <= size) {
inv.setInventorySlotContents(slot, null);
inv.markDirty();
return item;
}
ItemStack itemstack1 = item.splitStack(size);
if (item.stackSize == 0) inv.setInventorySlotContents(slot, null);
else inv.setInventorySlotContents(slot, item);
inv.markDirty();
return itemstack1;
}
return null;
}
/**
* Static default implementation for IInventory method
*/
public static ItemStack getStackInSlotOnClosing(IInventory inv, int slot) {
ItemStack stack = inv.getStackInSlot(slot);
inv.setInventorySlotContents(slot, null);
return stack;
}
/**
* @return The quantity of items from addition that can be added to base
*/
public static int incrStackSize(ItemStack base, ItemStack addition) {
if (canStack(base, addition)) return incrStackSize(base, addition.stackSize);
return 0;
}
/**
* @return The quantity of items from addition that can be added to base
*/
public static int incrStackSize(ItemStack base, int addition) {
int totalSize = base.stackSize + addition;
if (totalSize <= base.getMaxStackSize()) return addition;
else if (base.stackSize < base.getMaxStackSize()) return base.getMaxStackSize() - base.stackSize;
return 0;
}
/**
* NBT item saving function
*/
public static NBTTagList writeItemStacksToTag(ItemStack[] items) {
return writeItemStacksToTag(items, 64);
}
/**
* NBT item saving function with support for stack sizes > 32K
*/
public static NBTTagList writeItemStacksToTag(ItemStack[] items, int maxQuantity) {
NBTTagList tagList = new NBTTagList();
for (int i = 0; i < items.length; i++) {
if (items[i] != null) {
NBTTagCompound tag = new NBTTagCompound();
tag.setShort("Slot", (short) i);
items[i].writeToNBT(tag);
if (maxQuantity > Short.MAX_VALUE) tag.setInteger("Quantity", items[i].stackSize);
else if (maxQuantity > Byte.MAX_VALUE) tag.setShort("Quantity", (short) items[i].stackSize);
tagList.appendTag(tag);
}
}
return tagList;
}
/**
* NBT item loading function with support for stack sizes > 32K
*/
public static void readItemStacksFromTag(ItemStack[] items, NBTTagList tagList) {
for (int i = 0; i < tagList.tagCount(); i++) {
NBTTagCompound tag = tagList.getCompoundTagAt(i);
int b = tag.getShort("Slot");
items[b] = ItemStack.loadItemStackFromNBT(tag);
if (tag.hasKey("Quantity"))
items[b].stackSize = ((NBTBase.NBTPrimitive) tag.getTag("Quantity")).func_150287_d();
}
}
/**
* Spawns an itemstack in the world at a location
*/
public static void dropItem(ItemStack stack, World world, Vector3 dropLocation) {
EntityItem item = new EntityItem(world, dropLocation.x, dropLocation.y, dropLocation.z, stack);
item.motionX = world.rand.nextGaussian() * 0.05;
item.motionY = world.rand.nextGaussian() * 0.05 + 0.2F;
item.motionZ = world.rand.nextGaussian() * 0.05;
world.spawnEntityInWorld(item);
}
/**
* Copies an itemstack with a new quantity
*/
public static ItemStack copyStack(ItemStack stack, int quantity) {
if (stack == null) return null;
stack = stack.copy();
stack.stackSize = quantity;
return stack;
}
/**
* Gets the maximum quantity of an item that can be inserted into inv
*/
public static int getInsertibleQuantity(InventoryRange inv, ItemStack stack) {
int quantity = 0;
stack = copyStack(stack, Integer.MAX_VALUE);
for (int slot : inv.slots) quantity += fitStackInSlot(inv, slot, stack);
return quantity;
}
public static int getInsertibleQuantity(IInventory inv, ItemStack stack) {
return getInsertibleQuantity(new InventoryRange(inv), stack);
}
public static int fitStackInSlot(InventoryRange inv, int slot, ItemStack stack) {
ItemStack base = inv.inv.getStackInSlot(slot);
if (!canStack(base, stack) || !inv.canInsertItem(slot, stack)) return 0;
int fit = base != null ? incrStackSize(base, inv.inv.getInventoryStackLimit() - base.stackSize)
: inv.inv.getInventoryStackLimit();
return Math.min(fit, stack.stackSize);
}
public static int fitStackInSlot(IInventory inv, int slot, ItemStack stack) {
return fitStackInSlot(new InventoryRange(inv), slot, stack);
}
/**
* @param simulate If set to true, no items will actually be inserted
* @return The number of items unable to be inserted
*/
public static int insertItem(InventoryRange inv, ItemStack stack, boolean simulate) {
stack = stack.copy();
for (int pass = 0; pass < 2; pass++) {
for (int slot : inv.slots) {
ItemStack base = inv.inv.getStackInSlot(slot);
if ((pass == 0) == (base == null)) continue;
int fit = fitStackInSlot(inv, slot, stack);
if (fit == 0) continue;
if (base != null) {
stack.stackSize -= fit;
if (!simulate) {
base.stackSize += fit;
inv.inv.setInventorySlotContents(slot, base);
}
} else {
if (!simulate) inv.inv.setInventorySlotContents(slot, copyStack(stack, fit));
stack.stackSize -= fit;
}
if (stack.stackSize == 0) return 0;
}
}
return stack.stackSize;
}
public static int insertItem(IInventory inv, ItemStack stack, boolean simulate) {
return insertItem(new InventoryRange(inv), stack, simulate);
}
/**
* Gets the stack in slot if it can be extracted
*/
public static ItemStack getExtractableStack(InventoryRange inv, int slot) {
ItemStack stack = inv.inv.getStackInSlot(slot);
if (stack == null || !inv.canExtractItem(slot, stack)) return null;
return stack;
}
public static ItemStack getExtractableStack(IInventory inv, int slot) {
return getExtractableStack(new InventoryRange(inv), slot);
}
public static boolean areStacksIdentical(ItemStack stack1, ItemStack stack2) {
if (stack1 == null || stack2 == null) return stack1 == stack2;
return stack1.getItem() == stack2.getItem() && stack1.getItemDamage() == stack2.getItemDamage()
&& stack1.stackSize == stack2.stackSize
&& Objects.equal(stack1.getTagCompound(), stack2.getTagCompound());
}
/**
* Gets an IInventory from a coordinate with support for double chests
*/
public static IInventory getInventory(World world, int x, int y, int z) {
TileEntity tile = world.getTileEntity(x, y, z);
if (!(tile instanceof IInventory)) return null;
if (tile instanceof TileEntityChest) return getChest((TileEntityChest) tile);
return (IInventory) tile;
}
public static final ForgeDirection[] chestSides = new ForgeDirection[] { ForgeDirection.WEST, ForgeDirection.EAST,
ForgeDirection.NORTH, ForgeDirection.SOUTH };
public static IInventory getChest(TileEntityChest chest) {
for (ForgeDirection fside : chestSides) {
if (chest.getWorldObj()
.getBlock(chest.xCoord + fside.offsetX, chest.yCoord + fside.offsetY, chest.zCoord + fside.offsetZ)
== chest.getBlockType())
return new InventoryLargeChest(
"container.chestDouble",
(TileEntityChest) chest.getWorldObj().getTileEntity(
chest.xCoord + fside.offsetX,
chest.yCoord + fside.offsetY,
chest.zCoord + fside.offsetZ),
chest);
}
return chest;
}
public static boolean canStack(ItemStack stack1, ItemStack stack2) {
return stack1 == null || stack2 == null
|| (stack1.getItem() == stack2.getItem()
&& (!stack2.getHasSubtypes() || stack2.getItemDamage() == stack1.getItemDamage())
&& ItemStack.areItemStackTagsEqual(stack2, stack1)) && stack1.isStackable();
}
/**
* Consumes one item from slot in inv with support for containers.
*/
public static void consumeItem(IInventory inv, int slot) {
ItemStack stack = inv.getStackInSlot(slot);
Item item = stack.getItem();
if (item.hasContainerItem(stack)) {
ItemStack container = item.getContainerItem(stack);
inv.setInventorySlotContents(slot, container);
} else {
inv.decrStackSize(slot, 1);
}
}
/**
* Gets the size of the stack in a slot. Returns 0 on null stacks
*/
public static int stackSize(IInventory inv, int slot) {
ItemStack stack = inv.getStackInSlot(slot);
return stack == null ? 0 : stack.stackSize;
}
/**
* Drops all items from inv using getStackInSlotOnClosing
*/
public static void dropOnClose(EntityPlayer player, IInventory inv) {
for (int i = 0; i < inv.getSizeInventory(); i++) {
ItemStack stack = inv.getStackInSlotOnClosing(i);
if (stack != null) player.dropPlayerItemWithRandomChoice(stack, false);
}
}
public static NBTTagCompound savePersistant(ItemStack stack, NBTTagCompound tag) {
stack.writeToNBT(tag);
tag.removeTag("id");
tag.setString("name", Item.itemRegistry.getNameForObject(stack.getItem()));
return tag;
}
public static ItemStack loadPersistant(NBTTagCompound tag) {
String name = tag.getString("name");
Item item = (Item) Item.itemRegistry.getObject(name);
if (item == null) return null;
int count = tag.hasKey("Count") ? tag.getByte("Count") : 1;
int damage = tag.hasKey("Damage") ? tag.getShort("Damage") : 0;
ItemStack stack = new ItemStack(item, count, damage);
if (tag.hasKey("tag", 10)) stack.stackTagCompound = tag.getCompoundTag("tag");
return stack;
}
}
@@ -1,63 +0,0 @@
package codechicken.lib.inventory;
import static codechicken.lib.inventory.InventoryUtils.actualDamage;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.oredict.OreDictionary;
import com.google.common.base.Objects;
/**
* Comparable ItemStack with a hashCode implementation.
*/
public class ItemKey implements Comparable<ItemKey> {
public ItemStack stack;
private int hashcode = 0;
public ItemKey(ItemStack k) {
stack = k;
}
public ItemKey(Item item, int damage) {
this(new ItemStack(item, 1, damage));
}
public ItemKey(Item item, NBTTagCompound tag) {
this(item, OreDictionary.WILDCARD_VALUE, tag);
}
public ItemKey(Item item, int damage, NBTTagCompound tag) {
this(item, damage);
stack.setTagCompound(tag);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ItemKey)) return false;
ItemKey k = (ItemKey) obj;
return stack.getItem() == k.stack.getItem() && actualDamage(stack) == actualDamage(k.stack)
&& Objects.equal(stack.stackTagCompound, k.stack.stackTagCompound);
}
@Override
public int hashCode() {
return hashcode != 0 ? hashcode
: (hashcode = Objects.hashCode(stack.getItem(), actualDamage(stack), stack.stackTagCompound));
}
public int compareInt(int a, int b) {
return a == b ? 0 : a < b ? -1 : 1;
}
@Override
public int compareTo(ItemKey o) {
if (stack.getItem() != o.stack.getItem())
return compareInt(Item.getIdFromItem(stack.getItem()), Item.getIdFromItem(o.stack.getItem()));
if (actualDamage(stack) != actualDamage(o.stack)) return compareInt(actualDamage(stack), actualDamage(o.stack));
return 0;
}
}
@@ -1,55 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
public class SlotDummy extends SlotHandleClicks {
public final int stackLimit;
public SlotDummy(IInventory inv, int slot, int x, int y) {
this(inv, slot, x, y, 64);
}
public SlotDummy(IInventory inv, int slot, int x, int y, int limit) {
super(inv, slot, x, y);
stackLimit = limit;
}
@Override
public ItemStack slotClick(ContainerExtended container, EntityPlayer player, int button, int modifier) {
ItemStack held = player.inventory.getItemStack();
boolean shift = modifier == 1;
slotClick(held, button, shift);
return null;
}
public void slotClick(ItemStack held, int button, boolean shift) {
ItemStack tstack = getStack();
if (held != null && (tstack == null || !InventoryUtils.canStack(held, tstack))) {
int quantity = Math.min(held.stackSize, stackLimit);
if (shift) quantity = Math.min(stackLimit, held.getMaxStackSize() * 16);
if (button == 1) quantity = 1;
putStack(InventoryUtils.copyStack(held, quantity));
} else if (tstack != null) {
int inc;
if (held != null) {
inc = button == 1 ? -held.stackSize : held.stackSize;
if (shift) inc *= 16;
} else {
inc = button == 1 ? -1 : 1;
if (shift) inc *= 16;
}
int quantity = tstack.stackSize + inc;
if (quantity <= 0) putStack(null);
else putStack(InventoryUtils.copyStack(tstack, quantity));
}
}
@Override
public void putStack(ItemStack stack) {
if (stack != null && stack.stackSize > stackLimit) stack = InventoryUtils.copyStack(stack, stackLimit);
super.putStack(stack);
}
}
@@ -1,17 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
public class SlotDummyOutput extends SlotHandleClicks {
public SlotDummyOutput(IInventory inv, int slot, int x, int y) {
super(inv, slot, x, y);
}
@Override
public ItemStack slotClick(ContainerExtended container, EntityPlayer player, int button, int modifier) {
return null;
}
}
@@ -1,15 +0,0 @@
package codechicken.lib.inventory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
public abstract class SlotHandleClicks extends Slot {
public SlotHandleClicks(IInventory inv, int slot, int x, int y) {
super(inv, slot, x, y);
}
public abstract ItemStack slotClick(ContainerExtended container, EntityPlayer player, int button, int modifier);
}
@@ -1,87 +0,0 @@
package codechicken.lib.lighting;
import codechicken.lib.render.CCModel;
import codechicken.lib.util.Copyable;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Vector3;
public class LC implements Copyable<LC> {
public int side;
public float fa;
public float fb;
public float fc;
public float fd;
public LC() {
this(0, 0, 0, 0, 0);
}
public LC(int s, float a, float b, float c, float d) {
side = s;
fa = a;
fb = b;
fc = c;
fd = d;
}
public LC set(int s, float a, float b, float c, float d) {
side = s;
fa = a;
fb = b;
fc = c;
fd = d;
return this;
}
public LC set(LC lc) {
return set(lc.side, lc.fa, lc.fb, lc.fc, lc.fd);
}
public LC compute(Vector3 vec, Vector3 normal) {
int side = CCModel.findSide(normal);
if (side < 0) return set(12, 1, 0, 0, 0);
return compute(vec, side);
}
public LC compute(Vector3 vec, int side) {
boolean offset = false;
switch (side) {
case 0:
offset = vec.y <= 0;
break;
case 1:
offset = vec.y >= 1;
break;
case 2:
offset = vec.z <= 0;
break;
case 3:
offset = vec.z >= 1;
break;
case 4:
offset = vec.x <= 0;
break;
case 5:
offset = vec.x >= 1;
break;
}
if (!offset) side += 6;
return computeO(vec, side);
}
public LC computeO(Vector3 vec, int side) {
Vector3 v1 = Rotation.axes[((side & 0xE) + 3) % 6];
Vector3 v2 = Rotation.axes[((side & 0xE) + 5) % 6];
float d1 = (float) vec.scalarProject(v1);
float d2 = 1 - d1;
float d3 = (float) vec.scalarProject(v2);
float d4 = 1 - d3;
return set(side, d2 * d4, d2 * d3, d1 * d4, d1 * d3);
}
@Override
public LC copy() {
return new LC(side, fa, fb, fc, fd);
}
}
@@ -1,139 +0,0 @@
package codechicken.lib.lighting;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.world.IBlockAccess;
import codechicken.lib.colour.ColourRGBA;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.BlockCoord;
/**
* Note that when using the class as a vertex transformer, the vertices are assumed to be within the BB (x, y, z) ->
* (x+1, y+1, z+1)
*/
public class LightMatrix implements CCRenderState.IVertexOperation {
public static final int operationIndex = CCRenderState.registerOperation();
public int computed = 0;
public float[][] ao = new float[13][4];
public int[][] brightness = new int[13][4];
public IBlockAccess access;
public BlockCoord pos = new BlockCoord();
private int sampled = 0;
private float[] aSamples = new float[27];
private int[] bSamples = new int[27];
/**
* The 9 positions in the sample array for each side, sides >= 6 are centered on sample 13 (the block itself)
*/
public static final int[][] ssamplem = new int[][] { { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
{ 18, 19, 20, 21, 22, 23, 24, 25, 26 }, { 0, 9, 18, 1, 10, 19, 2, 11, 20 },
{ 6, 15, 24, 7, 16, 25, 8, 17, 26 }, { 0, 3, 6, 9, 12, 15, 18, 21, 24 },
{ 2, 5, 8, 11, 14, 17, 20, 23, 26 }, { 9, 10, 11, 12, 13, 14, 15, 16, 17 },
{ 9, 10, 11, 12, 13, 14, 15, 16, 17 }, { 3, 12, 21, 4, 13, 22, 5, 14, 23 },
{ 3, 12, 21, 4, 13, 22, 5, 14, 23 }, { 1, 4, 7, 10, 13, 16, 19, 22, 25 },
{ 1, 4, 7, 10, 13, 16, 19, 22, 25 }, { 13, 13, 13, 13, 13, 13, 13, 13, 13 } };
public static final int[][] qsamplem = new int[][] { // the positions in the side sample array for each corner
{ 0, 1, 3, 4 }, { 5, 1, 2, 4 }, { 6, 7, 3, 4 }, { 5, 7, 8, 4 } };
public static final float[] sideao = new float[] { 0.5F, 1F, 0.8F, 0.8F, 0.6F, 0.6F, 0.5F, 1F, 0.8F, 0.8F, 0.6F,
0.6F, 1F };
/*
* static { int[][] os = new int[][]{ {0,-1,0}, {0, 1,0}, {0,0,-1}, {0,0, 1}, {-1,0,0}, { 1,0,0}}; for(int s = 0; s
* < 12; s++) { int[] d0 = s < 6 ? new int[]{os[s][0]+1, os[s][1]+1, os[s][2]+1} : new int[]{1, 1, 1}; int[] d1 =
* os[((s&0xE)+3)%6]; int[] d2 = os[((s&0xE)+5)%6]; for(int a = -1; a <= 1; a++) for(int b = -1; b <= 1; b++)
* ssamplem[s][(a+1)*3+b+1] = (d0[1]+d1[1]*a+d2[1]*b)*9+(d0[2]+d1[2]*a+d2[2]*b)*3+(d0[0]+d1[0]*a+d2[0]*b); }
* System.out.println(Arrays.deepToString(ssamplem)); }
*/
public void locate(IBlockAccess a, int x, int y, int z) {
access = a;
pos.set(x, y, z);
computed = 0;
sampled = 0;
}
public void sample(int i) {
if ((sampled & 1 << i) == 0) {
int x = pos.x + (i % 3) - 1;
int y = pos.y + (i / 9) - 1;
int z = pos.z + (i / 3 % 3) - 1;
Block b = access.getBlock(x, y, z);
bSamples[i] = access.getLightBrightnessForSkyBlocks(x, y, z, b.getLightValue(access, x, y, z));
aSamples[i] = b.getAmbientOcclusionLightValue();
sampled |= 1 << i;
}
}
public int[] brightness(int side) {
sideSample(side);
return brightness[side];
}
public float[] ao(int side) {
sideSample(side);
return ao[side];
}
public void sideSample(int side) {
if ((computed & 1 << side) == 0) {
int[] ssample = ssamplem[side];
for (int q = 0; q < 4; q++) {
int[] qsample = qsamplem[q];
if (Minecraft.isAmbientOcclusionEnabled())
interp(side, q, ssample[qsample[0]], ssample[qsample[1]], ssample[qsample[2]], ssample[qsample[3]]);
else interp(side, q, ssample[4], ssample[4], ssample[4], ssample[4]);
}
computed |= 1 << side;
}
}
private void interp(int s, int q, int a, int b, int c, int d) {
sample(a);
sample(b);
sample(c);
sample(d);
ao[s][q] = interpAO(aSamples[a], aSamples[b], aSamples[c], aSamples[d]) * sideao[s];
brightness[s][q] = interpBrightness(bSamples[a], bSamples[b], bSamples[c], bSamples[d]);
}
public static float interpAO(float a, float b, float c, float d) {
return (a + b + c + d) / 4F;
}
public static int interpBrightness(int a, int b, int c, int d) {
if (a == 0) a = d;
if (b == 0) b = d;
if (c == 0) c = d;
return (a + b + c + d) >> 2 & 0xFF00FF;
}
@Override
public boolean load(CCRenderState state) {
if (!state.computeLighting) return false;
state.pipeline.addDependency(state.colourAttrib);
state.pipeline.addDependency(state.lightCoordAttrib);
return true;
}
@Override
public void operate(CCRenderState state) {
LC lc = state.lc;
float[] a = ao(lc.side);
float f = (a[0] * lc.fa + a[1] * lc.fb + a[2] * lc.fc + a[3] * lc.fd);
int[] b = brightness(lc.side);
state.setColourInstance(ColourRGBA.multiplyC(state.colour, f));
state.setBrightnessInstance((int) (b[0] * lc.fa + b[1] * lc.fb + b[2] * lc.fc + b[3] * lc.fd) & 0xFF00FF);
}
@Override
public int operationID() {
return operationIndex;
}
}
@@ -1,104 +0,0 @@
package codechicken.lib.lighting;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Vector3;
public class LightModel implements CCRenderState.IVertexOperation {
public static final int operationIndex = CCRenderState.registerOperation();
public static class Light {
public Vector3 ambient = new Vector3();
public Vector3 diffuse = new Vector3();
public Vector3 position;
public Light(Vector3 pos) {
position = pos.copy().normalize();
}
public Light setDiffuse(Vector3 vec) {
diffuse.set(vec);
return this;
}
public Light setAmbient(Vector3 vec) {
ambient.set(vec);
return this;
}
}
public static LightModel standardLightModel;
static {
standardLightModel = new LightModel().setAmbient(new Vector3(0.4, 0.4, 0.4))
.addLight(new Light(new Vector3(0.2, 1, -0.7)).setDiffuse(new Vector3(0.6, 0.6, 0.6)))
.addLight(new Light(new Vector3(-0.2, 1, 0.7)).setDiffuse(new Vector3(0.6, 0.6, 0.6)));
}
private Vector3 ambient = new Vector3();
private Light[] lights = new Light[8];
private int lightCount;
public LightModel addLight(Light light) {
lights[lightCount++] = light;
return this;
}
public LightModel setAmbient(Vector3 vec) {
ambient.set(vec);
return this;
}
/**
* @param colour The pre-lighting vertex colour. RGBA format
* @param normal The normal at the vertex
* @return The lighting applied colour
*/
public int apply(int colour, Vector3 normal) {
Vector3 n_colour = ambient.copy();
for (int l = 0; l < lightCount; l++) {
Light light = lights[l];
double n_l = light.position.dotProduct(normal);
double f = n_l > 0 ? 1 : 0;
n_colour.x += light.ambient.x + f * light.diffuse.x * n_l;
n_colour.y += light.ambient.y + f * light.diffuse.y * n_l;
n_colour.z += light.ambient.z + f * light.diffuse.z * n_l;
}
if (n_colour.x > 1) n_colour.x = 1;
if (n_colour.y > 1) n_colour.y = 1;
if (n_colour.z > 1) n_colour.z = 1;
n_colour.multiply((colour >>> 24) / 255D, (colour >> 16 & 0xFF) / 255D, (colour >> 8 & 0xFF) / 255D);
return (int) (n_colour.x * 255) << 24 | (int) (n_colour.y * 255) << 16
| (int) (n_colour.z * 255) << 8
| colour & 0xFF;
}
@Override
public boolean load(CCRenderState state) {
if (!state.computeLighting) return false;
state.pipeline.addDependency(CCRenderState.normalAttrib());
state.pipeline.addDependency(CCRenderState.colourAttrib());
return true;
}
@Override
public void operate(CCRenderState state) {
state.setColourInstance(apply(state.colour, state.normal));
}
@Override
public int operationID() {
return operationIndex;
}
public PlanarLightModel reducePlanar() {
int[] colours = new int[6];
for (int i = 0; i < 6; i++) colours[i] = apply(-1, Rotation.axes[i]);
return new PlanarLightModel(colours);
}
}
@@ -1,57 +0,0 @@
package codechicken.lib.lighting;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.BlockCoord;
public class PlanarLightMatrix extends PlanarLightModel {
public static final int operationIndex = CCRenderState.registerOperation();
public static PlanarLightMatrix instance = new PlanarLightMatrix();
public IBlockAccess access;
public BlockCoord pos = new BlockCoord();
private int sampled = 0;
public int[] brightness = new int[6];
public PlanarLightMatrix() {
super(PlanarLightModel.standardLightModel.colours);
}
public PlanarLightMatrix locate(IBlockAccess a, int x, int y, int z) {
access = a;
pos.set(x, y, z);
sampled = 0;
return this;
}
public int brightness(int side) {
if ((sampled & 1 << side) == 0) {
Block b = access.getBlock(pos.x, pos.y, pos.z);
brightness[side] = access
.getLightBrightnessForSkyBlocks(pos.x, pos.y, pos.z, b.getLightValue(access, pos.x, pos.y, pos.z));
sampled |= 1 << side;
}
return brightness[side];
}
@Override
public boolean load(CCRenderState state) {
state.pipeline.addDependency(CCRenderState.sideAttrib());
return true;
}
@Override
public void operate(CCRenderState state) {
super.operate(state);
state.setBrightnessInstance(brightness(state.side));
}
@Override
public int operationID() {
return operationIndex;
}
}
@@ -1,37 +0,0 @@
package codechicken.lib.lighting;
import codechicken.lib.colour.ColourRGBA;
import codechicken.lib.render.CCRenderState;
/**
* Faster precomputed version of LightModel that only works for axis planar sides
*/
public class PlanarLightModel implements CCRenderState.IVertexOperation {
public static PlanarLightModel standardLightModel = LightModel.standardLightModel.reducePlanar();
public int[] colours;
public PlanarLightModel(int[] colours) {
this.colours = colours;
}
@Override
public boolean load(CCRenderState state) {
if (!state.computeLighting) return false;
state.pipeline.addDependency(CCRenderState.sideAttrib());
state.pipeline.addDependency(CCRenderState.colourAttrib());
return true;
}
@Override
public void operate(CCRenderState state) {
state.setColourInstance(ColourRGBA.multiply(state.colour, colours[state.side]));
}
@Override
public int operationID() {
return LightModel.operationIndex;
}
}
@@ -1,56 +0,0 @@
package codechicken.lib.lighting;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.BlockCoord;
/**
* Faster precomputed version of LightModel that only works for axis planar sides
*/
public class SimpleBrightnessModel implements CCRenderState.IVertexOperation {
public static final int operationIndex = CCRenderState.registerOperation();
public static SimpleBrightnessModel instance = new SimpleBrightnessModel();
public IBlockAccess access;
public BlockCoord pos = new BlockCoord();
private int sampled = 0;
private final int[] samples = new int[6];
private final BlockCoord c = new BlockCoord();
public void locate(IBlockAccess a, int x, int y, int z) {
access = a;
pos.set(x, y, z);
sampled = 0;
}
public int sample(int side) {
if ((sampled & 1 << side) == 0) {
c.set(pos).offset(side);
Block block = access.getBlock(c.x, c.y, c.z);
samples[side] = access
.getLightBrightnessForSkyBlocks(c.x, c.y, c.z, block.getLightValue(access, c.x, c.y, c.z));
sampled |= 1 << side;
}
return samples[side];
}
@Override
public boolean load(CCRenderState state) {
state.pipeline.addDependency(CCRenderState.sideAttrib());
return true;
}
@Override
public void operate(CCRenderState state) {
state.setBrightnessInstance(sample(state.side));
}
@Override
public int operationID() {
return operationIndex;
}
}
@@ -1,152 +0,0 @@
package codechicken.lib.math;
public class MathHelper {
public static final double phi = 1.618033988749894;
public static final double pi = Math.PI;
public static final double todeg = 57.29577951308232;
public static final double torad = 0.017453292519943;
public static final double sqrt2 = 1.414213562373095;
public static double[] SIN_TABLE = new double[65536];
static {
for (int i = 0; i < 65536; ++i) SIN_TABLE[i] = Math.sin(i / 65536D * 2 * Math.PI);
SIN_TABLE[0] = 0;
SIN_TABLE[16384] = 1;
SIN_TABLE[32768] = 0;
SIN_TABLE[49152] = 1;
}
public static double sin(double d) {
return SIN_TABLE[(int) ((float) d * 10430.378F) & 65535];
}
public static double cos(double d) {
return SIN_TABLE[(int) ((float) d * 10430.378F + 16384.0F) & 65535];
}
/**
* @param a The value
* @param b The value to approach
* @param max The maximum step
* @return the closed value to b no less than max from a
*/
public static float approachLinear(float a, float b, float max) {
return (a > b) ? (a - b < max ? b : a - max) : (b - a < max ? b : a + max);
}
/**
* @param a The value
* @param b The value to approach
* @param max The maximum step
* @return the closed value to b no less than max from a
*/
public static double approachLinear(double a, double b, double max) {
return (a > b) ? (a - b < max ? b : a - max) : (b - a < max ? b : a + max);
}
/**
* @param a The first value
* @param b The second value
* @param d The interpolation factor, between 0 and 1
* @return a+(b-a)*d
*/
public static float interpolate(float a, float b, float d) {
return a + (b - a) * d;
}
/**
* @param a The first value
* @param b The second value
* @param d The interpolation factor, between 0 and 1
* @return a+(b-a)*d
*/
public static double interpolate(double a, double b, double d) {
return a + (b - a) * d;
}
/**
* @param a The value
* @param b The value to approach
* @param ratio The ratio to reduce the difference by
* @return a+(b-a)*ratio
*/
public static double approachExp(double a, double b, double ratio) {
return a + (b - a) * ratio;
}
/**
* @param a The value
* @param b The value to approach
* @param ratio The ratio to reduce the difference by
* @param cap The maximum amount to advance by
* @return a+(b-a)*ratio
*/
public static double approachExp(double a, double b, double ratio, double cap) {
double d = (b - a) * ratio;
if (Math.abs(d) > cap) d = Math.signum(d) * cap;
return a + d;
}
/**
* @param a The value
* @param b The value to approach
* @param ratio The ratio to reduce the difference by
* @param c The value to retreat from
* @param kick The difference when a == c
* @return
*/
public static double retreatExp(double a, double b, double c, double ratio, double kick) {
double d = (Math.abs(c - a) + kick) * ratio;
if (d > Math.abs(b - a)) return b;
return a + Math.signum(b - a) * d;
}
/**
*
* @param value The value
* @param min The min value
* @param max The max value
* @return The clipped value between min and max
*/
public static double clip(double value, double min, double max) {
if (value > max) value = max;
if (value < min) value = min;
return value;
}
/**
* @return a <= x <= b
*/
public static boolean between(double a, double x, double b) {
return a <= x && x <= b;
}
public static int approachExpI(int a, int b, double ratio) {
int r = (int) Math.round(approachExp(a, b, ratio));
return r == a ? b : r;
}
public static int retreatExpI(int a, int b, int c, double ratio, int kick) {
int r = (int) Math.round(retreatExp(a, b, c, ratio, kick));
return r == a ? b : r;
}
public static int floor_double(double d) {
return net.minecraft.util.MathHelper.floor_double(d);
}
public static int roundAway(double d) {
return (int) (d < 0 ? Math.floor(d) : Math.ceil(d));
}
public static int compare(int a, int b) {
return a == b ? 0 : a < b ? -1 : 1;
}
public static int compare(double a, double b) {
return a == b ? 0 : a < b ? -1 : 1;
}
}
@@ -1,6 +0,0 @@
package codechicken.lib.packet;
public interface ICustomPacketTile {
public void handleDescriptionPacket(PacketCustom packet);
}
@@ -1,553 +0,0 @@
package codechicken.lib.packet;
import java.util.EnumMap;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.Packet;
import net.minecraft.network.play.INetHandlerPlayClient;
import net.minecraft.network.play.INetHandlerPlayServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerManager;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import com.google.common.collect.Maps;
import codechicken.lib.data.MCDataInput;
import codechicken.lib.data.MCDataOutput;
import codechicken.lib.vec.BlockCoord;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.FMLEmbeddedChannel;
import cpw.mods.fml.common.network.FMLOutboundHandler;
import cpw.mods.fml.common.network.NetworkHandshakeEstablished;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
public final class PacketCustom implements MCDataInput, MCDataOutput {
public static interface ICustomPacketHandler {
}
public interface IClientPacketHandler extends ICustomPacketHandler {
public void handlePacket(PacketCustom packetCustom, Minecraft mc, INetHandlerPlayClient handler);
}
public interface IServerPacketHandler extends ICustomPacketHandler {
public void handlePacket(PacketCustom packetCustom, EntityPlayerMP sender, INetHandlerPlayServer handler);
}
public static AttributeKey<CustomInboundHandler> cclHandler = new AttributeKey<CustomInboundHandler>("ccl:handler");
@ChannelHandler.Sharable
public static class CustomInboundHandler extends SimpleChannelInboundHandler<FMLProxyPacket> {
public EnumMap<Side, CustomHandler> handlers = Maps.newEnumMap(Side.class);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
ctx.channel().attr(cclHandler).set(this);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FMLProxyPacket msg) throws Exception {
handlers.get(ctx.channel().attr(NetworkRegistry.CHANNEL_SOURCE).get()).handle(
ctx.channel().attr(NetworkRegistry.NET_HANDLER).get(),
ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get(),
new PacketCustom(msg.payload()));
}
}
private static interface CustomHandler {
public void handle(INetHandler handler, String channel, PacketCustom packet) throws Exception;
}
public static class ClientInboundHandler implements CustomHandler {
private IClientPacketHandler handler;
public ClientInboundHandler(ICustomPacketHandler handler) {
this.handler = (IClientPacketHandler) handler;
}
@Override
public void handle(INetHandler netHandler, String channel, PacketCustom packet) throws Exception {
if (netHandler instanceof INetHandlerPlayClient)
handler.handlePacket(packet, Minecraft.getMinecraft(), (INetHandlerPlayClient) netHandler);
else System.err.println("Invalid INetHandler for PacketCustom on channel: " + channel);
}
}
public static class ServerInboundHandler implements CustomHandler {
private IServerPacketHandler handler;
public ServerInboundHandler(ICustomPacketHandler handler) {
this.handler = (IServerPacketHandler) handler;
}
@Override
public void handle(INetHandler netHandler, String channel, PacketCustom packet) throws Exception {
if (netHandler instanceof NetHandlerPlayServer) handler.handlePacket(
packet,
((NetHandlerPlayServer) netHandler).playerEntity,
(INetHandlerPlayServer) netHandler);
else System.err.println("Invalid INetHandler for PacketCustom on channel: " + channel);
}
}
public static interface IHandshakeHandler {
public void handshakeRecieved(NetHandlerPlayServer netHandler);
}
public static class HandshakeInboundHandler extends ChannelInboundHandlerAdapter {
public IHandshakeHandler handler;
public HandshakeInboundHandler(IHandshakeHandler handler) {
this.handler = handler;
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof NetworkHandshakeEstablished) {
INetHandler netHandler = ((NetworkDispatcher) ctx.channel()
.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).get()).getNetHandler();
if (netHandler instanceof NetHandlerPlayServer)
handler.handshakeRecieved((NetHandlerPlayServer) netHandler);
} else ctx.fireUserEventTriggered(evt);
}
}
public static String channelName(Object channelKey) {
if (channelKey instanceof String) return (String) channelKey;
ModContainer mc = channelKey instanceof ModContainer ? (ModContainer) channelKey
: FMLCommonHandler.instance().findContainerFor(channelKey);
if (mc != null) {
String s = mc.getModId();
if (s.length() > 20) throw new IllegalArgumentException(
"Mod ID (" + s + ") too long for use as channel (20 chars). Use a string identifier");
return s;
}
throw new IllegalArgumentException("Invalid channel: " + channelKey);
}
public static FMLEmbeddedChannel getOrCreateChannel(String channelName, Side side) {
if (!NetworkRegistry.INSTANCE.hasChannel(channelName, side))
NetworkRegistry.INSTANCE.newChannel(channelName, new CustomInboundHandler());
return NetworkRegistry.INSTANCE.getChannel(channelName, side);
}
public static void assignHandler(Object channelKey, ICustomPacketHandler handler) {
String channelName = channelName(channelKey);
Side side = handler instanceof IServerPacketHandler ? Side.SERVER : Side.CLIENT;
FMLEmbeddedChannel channel = getOrCreateChannel(channelName, side);
channel.attr(cclHandler).get().handlers
.put(side, side == Side.SERVER ? new ServerInboundHandler(handler) : new ClientInboundHandler(handler));
}
public static void assignHandshakeHandler(Object channelKey, IHandshakeHandler handler) {
FMLEmbeddedChannel channel = getOrCreateChannel(channelName(channelKey), Side.SERVER);
channel.pipeline().addLast(new HandshakeInboundHandler(handler));
}
private ByteBuf byteBuf;
private String channel;
private int type;
public PacketCustom(ByteBuf payload) {
byteBuf = payload;
type = byteBuf.readUnsignedByte();
if (type > 0x80) decompress();
type &= 0x7F;
}
public PacketCustom(Object channelKey, int type) {
if (type <= 0 || type >= 0x80)
throw new IllegalArgumentException("Packet type: " + type + " is not within required 0 < t < 0x80");
this.channel = channelName(channelKey);
this.type = type;
byteBuf = Unpooled.buffer();
byteBuf.writeByte(type);
}
/**
* Decompresses the remaining ByteBuf (after type has been read) using Snappy
*/
private void decompress() {
Inflater inflater = new Inflater();
try {
int len = byteBuf.readInt();
ByteBuf out = Unpooled.buffer(len);
inflater.setInput(byteBuf.array(), byteBuf.readerIndex(), byteBuf.readableBytes());
inflater.inflate(out.array());
out.writerIndex(len);
byteBuf = out;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
inflater.end();
}
}
/**
* Compresses the payload ByteBuf after the type byte
*/
private void do_compress() {
Deflater deflater = new Deflater();
try {
byteBuf.readerIndex(1);
int len = byteBuf.readableBytes();
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), len);
deflater.finish();
ByteBuf out = Unpooled.buffer(len + 5);
int clen = deflater.deflate(out.array(), 5, len);
if (clen >= len - 5 || !deflater.finished()) // not worth compressing, gets larger
return;
out.setByte(0, type | 0x80);
out.setInt(1, len);
out.writerIndex(clen + 5);
byteBuf = out;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
byteBuf.readerIndex(0);
deflater.end();
}
}
public boolean incoming() {
return channel == null;
}
public int getType() {
return type & 0x7F;
}
public ByteBuf getByteBuf() {
return byteBuf;
}
public PacketCustom compress() {
if (incoming()) throw new IllegalStateException("Tried to compress an incoming packet");
if ((type & 0x80) != 0) throw new IllegalStateException("Packet already compressed");
type |= 0x80;
return this;
}
public PacketCustom writeBoolean(boolean b) {
byteBuf.writeBoolean(b);
return this;
}
public PacketCustom writeByte(int b) {
byteBuf.writeByte(b);
return this;
}
public PacketCustom writeShort(int s) {
byteBuf.writeShort(s);
return this;
}
public PacketCustom writeInt(int i) {
byteBuf.writeInt(i);
return this;
}
public PacketCustom writeFloat(float f) {
byteBuf.writeFloat(f);
return this;
}
public PacketCustom writeDouble(double d) {
byteBuf.writeDouble(d);
return this;
}
public PacketCustom writeLong(long l) {
byteBuf.writeLong(l);
return this;
}
@Override
public PacketCustom writeChar(char c) {
byteBuf.writeChar(c);
return this;
}
public PacketCustom writeVarInt(int i) {
ByteBufUtils.writeVarInt(byteBuf, i, 5);
return this;
}
public PacketCustom writeVarShort(int s) {
ByteBufUtils.writeVarShort(byteBuf, s);
return this;
}
public PacketCustom writeByteArray(byte[] barray) {
byteBuf.writeBytes(barray);
return this;
}
public PacketCustom writeString(String s) {
ByteBufUtils.writeUTF8String(byteBuf, s);
return this;
}
public PacketCustom writeCoord(int x, int y, int z) {
writeInt(x);
writeInt(y);
writeInt(z);
return this;
}
public PacketCustom writeCoord(BlockCoord coord) {
writeInt(coord.x);
writeInt(coord.y);
writeInt(coord.z);
return this;
}
public PacketCustom writeItemStack(ItemStack stack) {
writeItemStack(stack, false);
return this;
}
public PacketCustom writeItemStack(ItemStack stack, boolean large) {
if (stack == null) {
writeInt(-1);
} else {
writeInt(Item.getIdFromItem(stack.getItem()));
if (large) writeInt(stack.stackSize);
else writeByte(stack.stackSize);
writeShort(stack.getItemDamage());
writeNBTTagCompound(stack.stackTagCompound);
}
return this;
}
public PacketCustom writeNBTTagCompound(NBTTagCompound compound) {
ByteBufUtils.writeTag(byteBuf, compound);
return this;
}
public PacketCustom writeFluidStack(FluidStack fluid) {
if (fluid == null) {
writeShort(-1);
} else {
writeShort(fluid.getFluidID());
writeVarInt(fluid.amount);
writeNBTTagCompound(fluid.tag);
}
return this;
}
public boolean readBoolean() {
return byteBuf.readBoolean();
}
public short readUByte() {
return byteBuf.readUnsignedByte();
}
public int readUShort() {
return byteBuf.readUnsignedShort();
}
public byte readByte() {
return byteBuf.readByte();
}
public short readShort() {
return byteBuf.readShort();
}
public int readInt() {
return byteBuf.readInt();
}
public float readFloat() {
return byteBuf.readFloat();
}
public double readDouble() {
return byteBuf.readDouble();
}
public long readLong() {
return byteBuf.readLong();
}
public char readChar() {
return byteBuf.readChar();
}
@Override
public int readVarShort() {
return ByteBufUtils.readVarShort(byteBuf);
}
@Override
public int readVarInt() {
return ByteBufUtils.readVarInt(byteBuf, 5);
}
public BlockCoord readCoord() {
return new BlockCoord(readInt(), readInt(), readInt());
}
public byte[] readByteArray(int length) {
byte[] barray = new byte[length];
byteBuf.readBytes(barray, 0, length);
return barray;
}
public String readString() {
return ByteBufUtils.readUTF8String(byteBuf);
}
public ItemStack readItemStack() {
return readItemStack(false);
}
public ItemStack readItemStack(boolean large) {
ItemStack item = null;
int itemID = readInt();
if (itemID >= 0) {
int stackSize = large ? readInt() : readByte();
short damage = readShort();
item = new ItemStack(Item.getItemById(itemID), stackSize, damage);
item.stackTagCompound = readNBTTagCompound();
}
return item;
}
public NBTTagCompound readNBTTagCompound() {
return ByteBufUtils.readTag(byteBuf);
}
public FluidStack readFluidStack() {
Fluid fluid = FluidRegistry.getFluid(readShort());
if (fluid == null) fluid = FluidRegistry.WATER;
return new FluidStack(fluid, readVarInt(), readNBTTagCompound());
}
public FMLProxyPacket toPacket() {
if (incoming()) throw new IllegalStateException("Tried to write an incoming packet");
if (byteBuf.readableBytes() > 32000 || (type & 0x80) != 0) do_compress();
// FML packet impl returns the whole of the backing array, copy used portion of array to another ByteBuf
return new FMLProxyPacket(byteBuf.copy(), channel);
}
public void sendToPlayer(EntityPlayer player) {
sendToPlayer(toPacket(), player);
}
public static void sendToPlayer(Packet packet, EntityPlayer player) {
if (player == null) sendToClients(packet);
else((EntityPlayerMP) player).playerNetServerHandler.sendPacket(packet);
}
public void sendToClients() {
sendToClients(toPacket());
}
public static void sendToClients(Packet packet) {
MinecraftServer.getServer().getConfigurationManager().sendPacketToAllPlayers(packet);
}
public void sendPacketToAllAround(double x, double y, double z, double range, int dim) {
sendToAllAround(toPacket(), x, y, z, range, dim);
}
public static void sendToAllAround(Packet packet, double x, double y, double z, double range, int dim) {
MinecraftServer.getServer().getConfigurationManager().sendToAllNear(x, y, z, range, dim, packet);
}
public void sendToDimension(int dim) {
sendToDimension(toPacket(), dim);
}
public static void sendToDimension(Packet packet, int dim) {
MinecraftServer.getServer().getConfigurationManager().sendPacketToAllPlayersInDimension(packet, dim);
}
public void sendToChunk(World world, int chunkX, int chunkZ) {
sendToChunk(toPacket(), world, chunkX, chunkZ);
}
public static void sendToChunk(Packet packet, World world, int chunkX, int chunkZ) {
PlayerManager playerManager = ((WorldServer) world).getPlayerManager();
for (EntityPlayerMP player : (List<EntityPlayerMP>) MinecraftServer.getServer()
.getConfigurationManager().playerEntityList)
if (playerManager.isPlayerWatchingChunk(player, chunkX, chunkZ)) sendToPlayer(packet, player);
/*
* Commented until forge accepts access tranformer request PlayerInstance p = ((WorldServer)
* world).getPlayerManager().getOrCreateChunkWatcher(chunkX, chunkZ, false); if (p != null)
* p.sendToAllPlayersWatchingChunk(packet);
*/
}
public void sendToOps() {
sendToOps(toPacket());
}
public static void sendToOps(Packet packet) {
for (EntityPlayerMP player : (List<EntityPlayerMP>) MinecraftServer.getServer()
.getConfigurationManager().playerEntityList)
if (MinecraftServer.getServer().getConfigurationManager().func_152596_g(player.getGameProfile()))
sendToPlayer(packet, player);
}
@SideOnly(Side.CLIENT)
public void sendToServer() {
sendToServer(toPacket());
}
@SideOnly(Side.CLIENT)
public static void sendToServer(Packet packet) {
Minecraft.getMinecraft().getNetHandler().addToSendQueue(packet);
}
}
@@ -1,53 +0,0 @@
package codechicken.lib.raytracer;
import net.minecraft.entity.Entity;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
public class ExtendedMOP extends MovingObjectPosition implements Comparable<ExtendedMOP> {
public Object data;
/**
* The square distance from the start of the raytrace.
*/
public double dist;
public ExtendedMOP(Entity entity, Object data) {
super(entity);
setData(data);
}
public ExtendedMOP(int x, int y, int z, int side, Vec3 hit, Object data) {
super(x, y, z, side, hit);
setData(data);
}
public ExtendedMOP(MovingObjectPosition mop, Object data, double dist) {
super(0, 0, 0, 0, mop.hitVec);
typeOfHit = mop.typeOfHit;
blockX = mop.blockX;
blockY = mop.blockY;
blockZ = mop.blockZ;
sideHit = mop.sideHit;
subHit = mop.subHit;
setData(data);
this.dist = dist;
}
public void setData(Object data) {
if (data instanceof Integer) subHit = ((Integer) data).intValue();
this.data = data;
}
@SuppressWarnings("unchecked")
public static <T> T getData(MovingObjectPosition mop) {
if (mop instanceof ExtendedMOP) return (T) ((ExtendedMOP) mop).data;
return (T) Integer.valueOf(mop.subHit);
}
@Override
public int compareTo(ExtendedMOP o) {
return dist == o.dist ? 0 : dist < o.dist ? -1 : 1;
}
}
@@ -1,13 +0,0 @@
package codechicken.lib.raytracer;
import codechicken.lib.vec.Cuboid6;
public class IndexedCuboid6 extends Cuboid6 {
public Object data;
public IndexedCuboid6(Object data, Cuboid6 cuboid) {
super(cuboid);
this.data = data;
}
}
@@ -1,193 +0,0 @@
package codechicken.lib.raytracer;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class RayTracer {
private Vector3 vec = new Vector3();
private Vector3 vec2 = new Vector3();
private Vector3 s_vec = new Vector3();
private double s_dist;
private int s_side;
private IndexedCuboid6 c_cuboid;
private static ThreadLocal<RayTracer> t_inst = new ThreadLocal<RayTracer>();
public static RayTracer instance() {
RayTracer inst = t_inst.get();
if (inst == null) t_inst.set(inst = new RayTracer());
return inst;
}
private void traceSide(int side, Vector3 start, Vector3 end, Cuboid6 cuboid) {
vec.set(start);
Vector3 hit = null;
switch (side) {
case 0:
hit = vec.XZintercept(end, cuboid.min.y);
break;
case 1:
hit = vec.XZintercept(end, cuboid.max.y);
break;
case 2:
hit = vec.XYintercept(end, cuboid.min.z);
break;
case 3:
hit = vec.XYintercept(end, cuboid.max.z);
break;
case 4:
hit = vec.YZintercept(end, cuboid.min.x);
break;
case 5:
hit = vec.YZintercept(end, cuboid.max.x);
break;
}
if (hit == null) return;
switch (side) {
case 0:
case 1:
if (!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x)
|| !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z))
return;
break;
case 2:
case 3:
if (!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x)
|| !MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y))
return;
break;
case 4:
case 5:
if (!MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y)
|| !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z))
return;
break;
}
double dist = vec2.set(hit).subtract(start).magSquared();
if (dist < s_dist) {
s_side = side;
s_dist = dist;
s_vec.set(vec);
}
}
public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid) {
s_dist = Double.MAX_VALUE;
s_side = -1;
for (int i = 0; i < 6; i++) traceSide(i, start, end, cuboid);
if (s_side < 0) return null;
MovingObjectPosition mop = new MovingObjectPosition(0, 0, 0, s_side, s_vec.toVec3D());
mop.typeOfHit = null;
return mop;
}
public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List<IndexedCuboid6> cuboids) {
double c_dist = Double.MAX_VALUE;
MovingObjectPosition c_hit = null;
for (IndexedCuboid6 cuboid : cuboids) {
MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid);
if (mop != null && s_dist < c_dist) {
mop = new ExtendedMOP(mop, cuboid.data, s_dist);
c_dist = s_dist;
c_hit = mop;
c_cuboid = cuboid;
}
}
return c_hit;
}
public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List<IndexedCuboid6> cuboids,
BlockCoord pos, Block block) {
MovingObjectPosition mop = rayTraceCuboids(start, end, cuboids);
if (mop != null) {
mop.typeOfHit = MovingObjectType.BLOCK;
mop.blockX = pos.x;
mop.blockY = pos.y;
mop.blockZ = pos.z;
if (block != null) c_cuboid.add(new Vector3(-pos.x, -pos.y, -pos.z)).setBlockBounds(block);
}
return mop;
}
public static MovingObjectPosition retraceBlock(World world, EntityPlayer player, int x, int y, int z) {
Block block = world.getBlock(x, y, z);
Vec3 headVec = getCorrectedHeadVec(player);
Vec3 lookVec = player.getLook(1.0F);
double reach = getBlockReachDistance(player);
Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach);
return block.collisionRayTrace(world, x, y, z, headVec, endVec);
}
private static double getBlockReachDistance_server(EntityPlayerMP player) {
return player.theItemInWorldManager.getBlockReachDistance();
}
@SideOnly(Side.CLIENT)
private static double getBlockReachDistance_client() {
return Minecraft.getMinecraft().playerController.getBlockReachDistance();
}
public static MovingObjectPosition reTrace(World world, EntityPlayer player) {
return reTrace(world, player, getBlockReachDistance(player));
}
public static MovingObjectPosition reTrace(World world, EntityPlayer player, double reach) {
Vec3 headVec = getCorrectedHeadVec(player);
Vec3 lookVec = player.getLook(1);
Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach);
return world.func_147447_a(headVec, endVec, true, false, true);
}
public static Vec3 getCorrectedHeadVec(EntityPlayer player) {
Vec3 v = Vec3.createVectorHelper(player.posX, player.posY, player.posZ);
if (player.worldObj.isRemote) {
v.yCoord += player.getEyeHeight() - player.getDefaultEyeHeight(); // compatibility with eye height changing
// mods
} else {
v.yCoord += player.getEyeHeight();
if (player instanceof EntityPlayerMP && player.isSneaking()) v.yCoord -= 0.08;
}
return v;
}
public static Vec3 getStartVec(EntityPlayer player) {
return getCorrectedHeadVec(player);
}
public static double getBlockReachDistance(EntityPlayer player) {
return player.worldObj.isRemote ? getBlockReachDistance_client()
: player instanceof EntityPlayerMP ? getBlockReachDistance_server((EntityPlayerMP) player) : 5D;
}
public static Vec3 getEndVec(EntityPlayer player) {
Vec3 headVec = getCorrectedHeadVec(player);
Vec3 lookVec = player.getLook(1.0F);
double reach = getBlockReachDistance(player);
return headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach);
}
}
@@ -1,204 +0,0 @@
package codechicken.lib.render;
import codechicken.lib.lighting.LC;
import codechicken.lib.render.CCRenderState.VertexAttribute;
import codechicken.lib.vec.Cuboid6;
public class BlockRenderer {
public static class BlockFace implements CCRenderState.IVertexSource {
public Vertex5[] verts = new Vertex5[] { new Vertex5(), new Vertex5(), new Vertex5(), new Vertex5() };
public LC[] lightCoords = new LC[] { new LC(), new LC(), new LC(), new LC() };
public boolean lcComputed = false;
public int side;
@Override
public Vertex5[] getVertices() {
return verts;
}
@Override
public <T> T getAttributes(CCRenderState.VertexAttribute<T> attr) {
return attr == CCRenderState.lightCoordAttrib() && lcComputed ? (T) lightCoords : null;
}
@Override
public boolean hasAttribute(CCRenderState.VertexAttribute<?> attr) {
return attr == CCRenderState.sideAttrib() || attr == CCRenderState.lightCoordAttrib() && lcComputed;
}
@Override
public void prepareVertex(CCRenderState state) {
state.side = side;
}
public BlockFace computeLightCoords() {
if (!lcComputed) {
for (int i = 0; i < 4; i++) lightCoords[i].compute(verts[i].vec, side);
lcComputed = true;
}
return this;
}
public BlockFace loadCuboidFace(Cuboid6 c, int side) {
double x1 = c.min.x;
double x2 = c.max.x;
double y1 = c.min.y;
double y2 = c.max.y;
double z1 = c.min.z;
double z2 = c.max.z;
double u1;
double u2;
double v1;
double v2;
this.side = side;
lcComputed = false;
switch (side) {
case 0:
u1 = x1;
v1 = z1;
u2 = x2;
v2 = z2;
verts[0].set(x1, y1, z2, u1, v2, 0);
verts[1].set(x1, y1, z1, u1, v1, 0);
verts[2].set(x2, y1, z1, u2, v1, 0);
verts[3].set(x2, y1, z2, u2, v2, 0);
break;
case 1:
u1 = x1;
v1 = z1;
u2 = x2;
v2 = z2;
verts[0].set(x2, y2, z2, u2, v2, 1);
verts[1].set(x2, y2, z1, u2, v1, 1);
verts[2].set(x1, y2, z1, u1, v1, 1);
verts[3].set(x1, y2, z2, u1, v2, 1);
break;
case 2:
u1 = 1 - x1;
v1 = 1 - y2;
u2 = 1 - x2;
v2 = 1 - y1;
verts[0].set(x1, y1, z1, u1, v2, 2);
verts[1].set(x1, y2, z1, u1, v1, 2);
verts[2].set(x2, y2, z1, u2, v1, 2);
verts[3].set(x2, y1, z1, u2, v2, 2);
break;
case 3:
u1 = x1;
v1 = 1 - y2;
u2 = x2;
v2 = 1 - y1;
verts[0].set(x2, y1, z2, u2, v2, 3);
verts[1].set(x2, y2, z2, u2, v1, 3);
verts[2].set(x1, y2, z2, u1, v1, 3);
verts[3].set(x1, y1, z2, u1, v2, 3);
break;
case 4:
u1 = z1;
v1 = 1 - y2;
u2 = z2;
v2 = 1 - y1;
verts[0].set(x1, y1, z2, u2, v2, 4);
verts[1].set(x1, y2, z2, u2, v1, 4);
verts[2].set(x1, y2, z1, u1, v1, 4);
verts[3].set(x1, y1, z1, u1, v2, 4);
break;
case 5:
u1 = 1 - z1;
v1 = 1 - y2;
u2 = 1 - z2;
v2 = 1 - y1;
verts[0].set(x2, y1, z1, u1, v2, 5);
verts[1].set(x2, y2, z1, u1, v1, 5);
verts[2].set(x2, y2, z2, u2, v1, 5);
verts[3].set(x2, y1, z2, u2, v2, 5);
}
return this;
}
}
public static class FullBlock implements CCRenderState.IVertexSource {
public Vertex5[] verts = CCModel.quadModel(24).generateBlock(0, Cuboid6.full).verts;
public LC[] lightCoords = new LC[24];
public FullBlock() {
for (int i = 0; i < 24; i++) lightCoords[i] = new LC().compute(verts[i].vec, i / 4);
}
@Override
public Vertex5[] getVertices() {
return verts;
}
@Override
public <T> T getAttributes(VertexAttribute<T> attr) {
return attr == CCRenderState.lightCoordAttrib() ? (T) lightCoords : null;
}
@Override
public boolean hasAttribute(VertexAttribute<?> attr) {
return attr == CCRenderState.sideAttrib() || attr == CCRenderState.lightCoordAttrib();
}
@Override
public void prepareVertex(CCRenderState state) {
state.side = state.vertexIndex >> 2;
}
}
public static FullBlock fullBlock = new FullBlock();
// public static void renderFullBlock(int sideMask) {
public static void renderFullBlock(CCRenderState state, int sideMask) {
state.setModelInstance(fullBlock);
renderFaces(state, sideMask);
}
public static void renderFullBlock(int sideMask) {
renderFullBlock(CCRenderState.instance(), sideMask);
}
/**
* Renders faces of a block-like model based on a sideMask. Eg for side 2, verts 8-11 will be rendered
*
* @param sideMask A mask of faces not to render
*/
// public static void renderFaces(int sideMask) {
public static void renderFaces(CCRenderState state, int sideMask) {
if (sideMask == 0x3F) return;
for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) {
state.setVertexRangeInstance(s * 4, (s + 1) * 4);
state.renderInstance();
}
}
public static void renderFaces(int sideMask) {
renderFaces(CCRenderState.instance(), sideMask);
}
private static final BlockFace face = new BlockFace();
/**
* Renders faces of a cuboid with texture coordinates mapped to match a standard minecraft block
*
* @param bounds The bounding cuboid to render
* @param sideMask A mask of faces not to render
*/
public static void renderCuboid(CCRenderState state, Cuboid6 bounds, int sideMask) {
if (sideMask == 0x3F) return;
state.setModelInstance(face);
for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) {
face.loadCuboidFace(bounds, s);
state.renderInstance();
}
}
public static void renderCuboid(Cuboid6 bounds, int sideMask) {
renderCuboid(CCRenderState.instance(), bounds, sideMask);
}
}
File diff suppressed because it is too large. Load diff
@@ -1,92 +0,0 @@
package codechicken.lib.render;
import static codechicken.lib.math.MathHelper.phi;
import codechicken.lib.vec.Matrix4;
import codechicken.lib.vec.Quat;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Vector3;
public class CCModelLibrary {
public static CCModel icosahedron4;
public static CCModel icosahedron7;
private static int i;
static {
generateIcosahedron();
}
private static void generateIcosahedron() {
Vector3[] verts = new Vector3[12];
verts[0] = new Vector3(-1, phi, 0);
verts[1] = new Vector3(1, phi, 0);
verts[2] = new Vector3(1, -phi, 0);
verts[3] = new Vector3(-1, -phi, 0);
verts[4] = new Vector3(0, -1, phi);
verts[5] = new Vector3(0, 1, phi);
verts[6] = new Vector3(0, 1, -phi);
verts[7] = new Vector3(0, -1, -phi);
verts[8] = new Vector3(phi, 0, -1);
verts[9] = new Vector3(phi, 0, 1);
verts[10] = new Vector3(-phi, 0, 1);
verts[11] = new Vector3(-phi, 0, -1);
Quat quat = Quat.aroundAxis(0, 0, 1, Math.atan(1 / phi));
for (Vector3 vec : verts) quat.rotate(vec);
icosahedron4 = CCModel.newModel(4, 60);
icosahedron7 = CCModel.newModel(7, 80);
i = 0;
// top
addIcosahedronTriangle(verts[1], 0.5, 0, verts[0], 0, 0.25, verts[5], 1, 0.25);
addIcosahedronTriangle(verts[1], 0.5, 0, verts[5], 0, 0.25, verts[9], 1, 0.25);
addIcosahedronTriangle(verts[1], 0.5, 0, verts[9], 0, 0.25, verts[8], 1, 0.25);
addIcosahedronTriangle(verts[1], 0.5, 0, verts[8], 0, 0.25, verts[6], 1, 0.25);
addIcosahedronTriangle(verts[1], 0.5, 0, verts[6], 0, 0.25, verts[0], 1, 0.25);
// centre 1vert top
addIcosahedronTriangle(verts[0], 0.5, 0.25, verts[11], 0, 0.75, verts[10], 1, 0.75);
addIcosahedronTriangle(verts[5], 0.5, 0.25, verts[10], 0, 0.75, verts[4], 1, 0.75);
addIcosahedronTriangle(verts[9], 0.5, 0.25, verts[4], 0, 0.75, verts[2], 1, 0.75);
addIcosahedronTriangle(verts[8], 0.5, 0.25, verts[2], 0, 0.75, verts[7], 1, 0.75);
addIcosahedronTriangle(verts[6], 0.5, 0.25, verts[7], 0, 0.75, verts[11], 1, 0.75);
// centre 1vert bottom
addIcosahedronTriangle(verts[2], 0.5, 0.75, verts[8], 0, 0.25, verts[9], 1, 0.25);
addIcosahedronTriangle(verts[7], 0.5, 0.75, verts[6], 0, 0.25, verts[8], 1, 0.25);
addIcosahedronTriangle(verts[11], 0.5, 0.75, verts[0], 0, 0.25, verts[6], 1, 0.25);
addIcosahedronTriangle(verts[10], 0.5, 0.75, verts[5], 0, 0.25, verts[0], 1, 0.25);
addIcosahedronTriangle(verts[4], 0.5, 0.75, verts[9], 0, 0.25, verts[5], 1, 0.25);
// bottom
addIcosahedronTriangle(verts[3], 0.5, 1, verts[2], 0, 0.75, verts[4], 1, 0.75);
addIcosahedronTriangle(verts[3], 0.5, 1, verts[7], 0, 0.75, verts[2], 1, 0.75);
addIcosahedronTriangle(verts[3], 0.5, 1, verts[11], 0, 0.75, verts[7], 1, 0.75);
addIcosahedronTriangle(verts[3], 0.5, 1, verts[10], 0, 0.75, verts[11], 1, 0.75);
addIcosahedronTriangle(verts[3], 0.5, 1, verts[4], 0, 0.75, verts[10], 1, 0.75);
icosahedron4.computeNormals().smoothNormals();
icosahedron7.computeNormals().smoothNormals();
}
private static void addIcosahedronTriangle(Vector3 vec1, double u1, double v1, Vector3 vec2, double u2, double v2,
Vector3 vec3, double u3, double v3) {
icosahedron4.verts[i * 3] = icosahedron7.verts[i * 4] = new Vertex5(vec1, u1, v1);
icosahedron4.verts[i * 3 + 1] = icosahedron7.verts[i * 4 + 1] = new Vertex5(vec2, u2, v2);
icosahedron4.verts[i * 3
+ 2] = icosahedron7.verts[i * 4 + 2] = icosahedron7.verts[i * 4 + 3] = new Vertex5(vec3, u3, v3);
i++;
}
@Deprecated
public static Matrix4 getRenderMatrix(Vector3 position, Rotation rotation, double scale) {
return new Matrix4().translate(position).scale(scale).apply(rotation);
}
public static Matrix4 getRenderMatrix(double posX, double posY, double posZ, Rotation rotation, double scale) {
return new Matrix4().translate(posX, posY, posZ).scale(scale).apply(rotation);
}
}
@@ -1,142 +0,0 @@
package codechicken.lib.render;
import java.util.ArrayList;
import java.util.Collections;
import codechicken.lib.render.CCRenderState.IVertexOperation;
import codechicken.lib.render.CCRenderState.VertexAttribute;
@SuppressWarnings("ForLoopReplaceableByForEach")
public class CCRenderPipeline {
private final CCRenderState renderState;
private final PipelineBuilder builder;
public CCRenderPipeline(CCRenderState renderState) {
this.renderState = renderState;
builder = new PipelineBuilder(renderState);
}
public CCRenderPipeline() {
this(CCRenderState.instance());
}
public class PipelineBuilder {
private final CCRenderState renderState;
public PipelineBuilder(CCRenderState renderState) {
this.renderState = renderState;
}
public PipelineBuilder() {
this(CCRenderState.instance());
}
public PipelineBuilder add(IVertexOperation op) {
ops.add(op);
return this;
}
public PipelineBuilder add(IVertexOperation... ops) {
Collections.addAll(CCRenderPipeline.this.ops, ops);
return this;
}
public void build() {
rebuild();
}
public void render() {
rebuild();
renderState.renderInstance();
}
}
private class PipelineNode {
public ArrayList<PipelineNode> deps = new ArrayList<>();
public IVertexOperation op;
public void add() {
if (op == null) return;
for (int i = 0; i < deps.size(); i++) deps.get(i).add();
deps.clear();
sorted.add(op);
op = null;
}
}
private final ArrayList<VertexAttribute> attribs = new ArrayList<>();
private final ArrayList<IVertexOperation> ops = new ArrayList<>();
private final ArrayList<PipelineNode> nodes = new ArrayList<>();
private final ArrayList<IVertexOperation> sorted = new ArrayList<>();
private PipelineNode loading;
public void setPipeline(IVertexOperation... ops) {
this.ops.clear();
for (int i = 0; i < ops.length; i++) this.ops.add(ops[i]);
rebuild();
}
public void reset() {
ops.clear();
unbuild();
}
private void unbuild() {
for (int i = 0; i < attribs.size(); i++) attribs.get(i).active = false;
attribs.clear();
sorted.clear();
}
public void rebuild() {
if (ops.isEmpty() || this.renderState.model == null) return;
// ensure enough nodes for all ops
while (nodes.size() < this.renderState.operationCount()) nodes.add(new PipelineNode());
unbuild();
if (this.renderState.useNormals) addAttribute(this.renderState.normalAttrib);
if (this.renderState.useColour) addAttribute(this.renderState.colourAttrib);
if (this.renderState.computeLighting) addAttribute(this.renderState.lightingAttrib);
for (int i = 0; i < ops.size(); i++) {
IVertexOperation op = ops.get(i);
loading = nodes.get(op.operationID());
boolean loaded = op.load(renderState);
if (loaded) loading.op = op;
if (op instanceof VertexAttribute) if (loaded) attribs.add((VertexAttribute) op);
else((VertexAttribute) op).active = false;
}
for (int i = 0; i < nodes.size(); i++) nodes.get(i).add();
}
public void addRequirement(int opRef) {
loading.deps.add(nodes.get(opRef));
}
public void addDependency(VertexAttribute attrib) {
loading.deps.add(nodes.get(attrib.operationID()));
addAttribute(attrib);
}
public void addAttribute(VertexAttribute attrib) {
if (!attrib.active) {
ops.add(attrib);
attrib.active = true;
}
}
public void operate() {
for (int i = 0; i < sorted.size(); i++) sorted.get(i).operate(renderState);
}
public PipelineBuilder builder() {
ops.clear();
return builder;
}
}
@@ -1,593 +0,0 @@
package codechicken.lib.render;
import java.util.ArrayList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import codechicken.lib.colour.ColourRGBA;
import codechicken.lib.lighting.LC;
import codechicken.lib.lighting.LightMatrix;
import codechicken.lib.util.Copyable;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Transformation;
import codechicken.lib.vec.Vector3;
/**
* The core of the CodeChickenLib render system. Where possible assign a local var of CCRenderState to avoid millions of
* calls to instance(); Uses a ThreadLocal system to assign each thread their own CCRenderState so we can use it in
* Multithreaded chunk batching.
* <p>
* Backported from CCL - 1.16.x
*/
public class CCRenderState {
public final CCRenderPipeline pipeline;
private static final ThreadLocal<CCRenderState> instances = ThreadLocal.withInitial(CCRenderState::new);
private CCRenderState() {
pipeline = new CCRenderPipeline(this);
}
public static CCRenderState instance() {
return instances.get();
}
private static int nextOperationIndex;
public static int registerOperation() {
return nextOperationIndex++;
}
public static int operationCount() {
return nextOperationIndex;
}
/**
* Represents an operation to be run for each vertex that operates on and modifies the current state
*/
public interface IVertexOperation {
/**
* Load any required references and add dependencies to the pipeline based on the current model (may be null)
* Return false if this operation is redundant in the pipeline with the given model
*/
default boolean load() {
// Existing code will will override this method so it shouldn't infinite loop
// Default it only for new state aware code
return load(CCRenderState.instance());
}
default boolean load(CCRenderState state) {
// New code will override this and not inplement load() so we shouldn't infinite loop
return load();
}
/**
* Perform the operation on the current render state
*/
default void operate() {
operate(CCRenderState.instance());
}
default void operate(CCRenderState state) {
operate();
}
/**
* Get the unique id representing this type of operation. Duplicate operation IDs within the pipeline may have
* unexpected results. ID shoulld be obtained from CCRenderState.registerOperation() and stored in a static
* variable
*/
int operationID();
}
private static ArrayList<VertexAttribute<?>> vertexAttributes = new ArrayList<>();
private static int registerVertexAttribute(VertexAttribute<?> attr) {
vertexAttributes.add(attr);
return vertexAttributes.size() - 1;
}
public static VertexAttribute<?> getAttribute(int index) {
return vertexAttributes.get(index);
}
/**
* Management class for a vertex attrute such as colour, normal etc This class should handle the loading of the
* attrute from an array provided by IVertexSource.getAttributes or the computation of this attrute from others
*
* @param <T> The array type for this attrute eg. int[], Vector3[]
*/
public abstract static class VertexAttribute<T> implements IVertexOperation {
public final int attributeIndex = registerVertexAttribute(this);
private final int operationIndex = registerOperation();
/**
* Set to true when the attrute is part of the pipeline. Should only be managed by CCRenderState when
* constructing the pipeline
*/
public boolean active = false;
/**
* Construct a new array for storage of vertex attrutes in a model
*/
public abstract T newArray(int length);
@Override
public int operationID() {
return operationIndex;
}
}
public static void arrayCopy(Object src, int srcPos, Object dst, int destPos, int length) {
System.arraycopy(src, srcPos, dst, destPos, length);
if (dst instanceof Copyable[]) {
Object[] oa = (Object[]) dst;
Copyable<Object>[] c = (Copyable[]) dst;
for (int i = destPos; i < destPos + length; i++) if (c[i] != null) oa[i] = c[i].copy();
}
}
public static <T> T copyOf(VertexAttribute<T> attr, T src, int length) {
T dst = attr.newArray(length);
arrayCopy(src, 0, dst, 0, ((Object[]) src).length);
return dst;
}
public interface IVertexSource {
Vertex5[] getVertices();
/**
* Gets an array of vertex attrutes
*
* @param attr The vertex attrute to get
* @param <T> The attrute array type
* @return An array, or null if not computed
*/
<T> T getAttributes(VertexAttribute<T> attr);
/**
* @return True if the specified attrute is provided by this model, either by returning an array from
* getAttributes or by setting the state in prepareVertex
*/
boolean hasAttribute(VertexAttribute<?> attr);
/**
* Callback to set CCRenderState for a vertex before the pipeline runs
*/
void prepareVertex(CCRenderState state);
default void prepareVertex() {
prepareVertex(CCRenderState.instance());
}
}
public static VertexAttribute<Vector3[]> normalAttrib() {
return instances.get().normalAttrib;
}
public static VertexAttribute<int[]> colourAttrib() {
return instances.get().colourAttrib;
}
public static VertexAttribute<LC[]> lightCoordAttrib() {
return instances.get().lightCoordAttrib;
}
public static VertexAttribute<int[]> sideAttrib() {
return instances.get().sideAttrib;
}
public static VertexAttribute<int[]> lightingAttrib() {
return instances.get().lightingAttrib;
}
public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>() {
private Vector3[] normalRef;
@Override
public Vector3[] newArray(int length) {
return new Vector3[length];
}
@Override
public boolean load(CCRenderState state) {
normalRef = state.model.getAttributes(this);
if (state.model.hasAttribute(this)) return normalRef != null;
if (state.model.hasAttribute(sideAttrib)) {
state.pipeline.addDependency(sideAttrib);
return true;
}
throw new IllegalStateException(
"Normals requested but neither normal or side attrutes are provided by the model");
}
@Override
public void operate(CCRenderState state) {
if (normalRef != null) state.setNormalInstance(normalRef[state.vertexIndex]);
else state.setNormalInstance(Rotation.axes[state.side]);
}
};
public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>() {
private int[] colourRef;
@Override
public int[] newArray(int length) {
return new int[length];
}
@Override
public boolean load(CCRenderState state) {
colourRef = state.model.getAttributes(this);
return colourRef != null || !state.model.hasAttribute(this);
}
@Override
public void operate(CCRenderState state) {
if (colourRef != null)
state.setColourInstance(ColourRGBA.multiply(state.baseColour, colourRef[state.vertexIndex]));
else state.setColourInstance(state.baseColour);
}
};
public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>() {
private int[] colourRef;
@Override
public int[] newArray(int length) {
return new int[length];
}
@Override
public boolean load(CCRenderState state) {
if (!state.computeLighting || !state.useColour || !state.model.hasAttribute(this)) return false;
colourRef = state.model.getAttributes(this);
if (colourRef != null) {
state.pipeline.addDependency(colourAttrib);
return true;
}
return false;
}
@Override
public void operate(CCRenderState state) {
state.setColourInstance(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex]));
}
};
public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>() {
private int[] sideRef;
@Override
public int[] newArray(int length) {
return new int[length];
}
@Override
public boolean load(CCRenderState state) {
sideRef = state.model.getAttributes(this);
if (state.model.hasAttribute(this)) return sideRef != null;
state.pipeline.addDependency(normalAttrib);
return true;
}
@Override
public void operate(CCRenderState state) {
if (sideRef != null) state.side = sideRef[state.vertexIndex];
else state.side = CCModel.findSide(state.normal);
}
};
/**
* Uses the position of the lightmatrix to compute LC if not provided
*/
public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>() {
private LC[] lcRef;
private final Vector3 vec = new Vector3(); // for computation
private final Vector3 pos = new Vector3();
@Override
public LC[] newArray(int length) {
return new LC[length];
}
@Override
public boolean load(CCRenderState state) {
lcRef = state.model.getAttributes(this);
if (state.model.hasAttribute(this)) return lcRef != null;
pos.set(state.lightMatrix.pos.x, state.lightMatrix.pos.y, state.lightMatrix.pos.z);
state.pipeline.addDependency(sideAttrib);
state.pipeline.addRequirement(Transformation.operationIndex);
return true;
}
@Override
public void operate(CCRenderState state) {
if (lcRef != null) state.lc.set(lcRef[state.vertexIndex]);
else state.lc.compute(vec.set(state.vert.vec).sub(pos), state.side);
}
};
// pipeline state
public IVertexSource model;
public int firstVertexIndex;
public int lastVertexIndex;
public int vertexIndex;
// context
public int baseColour;
public int alphaOverride;
public boolean useNormals;
public boolean computeLighting;
public boolean useColour;
public LightMatrix lightMatrix = new LightMatrix();
// vertex outputs
public Vertex5 vert = new Vertex5();
public boolean hasNormal;
public Vector3 normal = new Vector3();
public boolean hasColour;
public int colour;
public boolean hasBrightness;
public int brightness;
// attribute storage
public int side;
public LC lc = new LC();
public static void reset() {
instance().resetInstance();
}
public void resetInstance() {
model = null;
pipeline.reset();
useNormals = hasNormal = hasBrightness = hasColour = false;
useColour = computeLighting = true;
baseColour = alphaOverride = -1;
}
public void setPipelineInstance(IVertexOperation... ops) {
pipeline.setPipeline(ops);
}
@Deprecated
public static void setPipeline(IVertexOperation... ops) {
instance().setPipelineInstance(ops);
}
public void setPipelineInstance(IVertexSource model, int start, int end, IVertexOperation... ops) {
pipeline.reset();
setModelInstance(model, start, end);
pipeline.setPipeline(ops);
}
@Deprecated
public static void setPipeline(IVertexSource model, int start, int end, IVertexOperation... ops) {
instance().setPipelineInstance(model, start, end, ops);
}
public void bindModelInstance(IVertexSource model) {
if (this.model != model) {
this.model = model;
pipeline.rebuild();
}
}
@Deprecated
public static void bindModel(IVertexSource model) {
instance().bindModelInstance(model);
}
public void setModelInstance(IVertexSource source) {
setModelInstance(source, 0, source.getVertices().length);
}
@Deprecated
public static void setModel(IVertexSource source) {
instance().setModelInstance(source);
}
public void setModelInstance(IVertexSource source, int start, int end) {
bindModelInstance(source);
setVertexRangeInstance(start, end);
}
@Deprecated
public static void setModel(IVertexSource source, int start, int end) {
instance().setModelInstance(source, start, end);
}
public void setVertexRangeInstance(int start, int end) {
firstVertexIndex = start;
lastVertexIndex = end;
}
@Deprecated
public static void setVertexRange(int start, int end) {
instance().setVertexRangeInstance(start, end);
}
public void renderInstance(IVertexOperation... ops) {
setPipelineInstance(ops);
renderInstance();
}
@Deprecated
public static void render(IVertexOperation... ops) {
instance().renderInstance(ops);
}
public void renderInstance() {
Vertex5[] verts = model.getVertices();
for (vertexIndex = firstVertexIndex; vertexIndex < lastVertexIndex; vertexIndex++) {
model.prepareVertex(this);
vert.set(verts[vertexIndex]);
runPipelineInstance();
writeVertInstance();
}
}
@Deprecated
public static void render() {
instance().renderInstance();
}
public void runPipelineInstance() {
pipeline.operate();
}
@Deprecated
public static void runPipeline() {
instance().runPipelineInstance();
}
public void writeVertInstance() {
if (hasNormal) Tessellator.instance.setNormal((float) normal.x, (float) normal.y, (float) normal.z);
if (hasColour) Tessellator.instance.setColorRGBA(
colour >>> 24,
colour >> 16 & 0xFF,
colour >> 8 & 0xFF,
alphaOverride >= 0 ? alphaOverride : colour & 0xFF);
if (hasBrightness) Tessellator.instance.setBrightness(brightness);
Tessellator.instance.addVertexWithUV(vert.vec.x, vert.vec.y, vert.vec.z, vert.uv.u, vert.uv.v);
}
@Deprecated
public static void writeVert() {
instance().writeVertInstance();
}
public void setNormalInstance(double x, double y, double z) {
hasNormal = true;
normal.set(x, y, z);
}
@Deprecated
public static void setNormal(double x, double y, double z) {
instance().setNormalInstance(x, y, z);
}
public void setNormalInstance(Vector3 n) {
hasNormal = true;
normal.set(n);
}
@Deprecated
public static void setNormal(Vector3 n) {
instance().setNormalInstance(n);
}
public void setColourInstance(int c) {
hasColour = true;
colour = c;
}
@Deprecated
public static void setColour(int c) {
instance().setColourInstance(c);
}
public void setBrightnessInstance(int b) {
hasBrightness = true;
brightness = b;
}
@Deprecated
public static void setBrightness(int b) {
instance().setBrightnessInstance(b);
}
public void setBrightnessInstance(IBlockAccess world, int x, int y, int z) {
setBrightnessInstance(world.getBlock(x, y, z).getMixedBrightnessForBlock(world, x, y, z));
}
@Deprecated
public static void setBrightness(IBlockAccess world, int x, int y, int z) {
instance().setBrightnessInstance(world, x, y, z);
}
public static void pullLightmap() {
instance().pullLightmapInstance();
}
public void pullLightmapInstance() {
setBrightnessInstance((int) OpenGlHelper.lastBrightnessY << 16 | (int) OpenGlHelper.lastBrightnessX);
}
public static void pushLightmap() {
instance().pushLightmapInstance();
}
public void pushLightmapInstance() {
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness & 0xFFFF, brightness >>> 16);
}
/**
* Compact helper for setting dynamic rendering context. Uses normals and doesn't compute lighting
*/
public static void setDynamic() {
instance().setDynamicInstance();
}
public void setDynamicInstance() {
useNormals = true;
computeLighting = false;
}
public static void changeTexture(String texture) {
changeTexture(new ResourceLocation(texture));
}
public static void changeTexture(ResourceLocation texture) {
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
}
public void startDrawingInstance() {
startDrawingInstance(7);
}
@Deprecated
public static void startDrawing() {
instance().startDrawingInstance();
}
public void startDrawingInstance(int mode) {
Tessellator.instance.startDrawing(mode);
if (hasColour) Tessellator.instance.setColorRGBA(
colour >>> 24,
colour >> 16 & 0xFF,
colour >> 8 & 0xFF,
alphaOverride >= 0 ? alphaOverride : colour & 0xFF);
if (hasBrightness) Tessellator.instance.setBrightness(brightness);
}
@Deprecated
public static void startDrawing(int mode) {
instance().startDrawingInstance(mode);
}
public static void draw() {
Tessellator.instance.draw();
}
public void drawInstance() {
Tessellator.instance.draw();
}
}
@@ -1,43 +0,0 @@
package codechicken.lib.render;
import codechicken.lib.colour.ColourRGBA;
public class ColourMultiplier implements CCRenderState.IVertexOperation {
private static final ThreadLocal<ColourMultiplier> instances = ThreadLocal
.withInitial(() -> new ColourMultiplier(-1));
public static ColourMultiplier instance(int colour) {
ColourMultiplier instance = instances.get();
instance.colour = colour;
return instance;
}
public static final int operationIndex = CCRenderState.registerOperation();
public int colour;
public ColourMultiplier(int colour) {
this.colour = colour;
}
@Override
public boolean load(CCRenderState state) {
if (colour == -1) {
state.setColourInstance(-1);
return false;
}
state.pipeline.addDependency(state.colourAttrib);
return true;
}
@Override
public void operate(CCRenderState state) {
state.setColourInstance(ColourRGBA.multiply(state.colour, colour));
}
@Override
public int operationID() {
return operationIndex;
}
}
@@ -1,139 +0,0 @@
package codechicken.lib.render;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
public class EntityDigIconFX extends EntityFX {
public EntityDigIconFX(World world, double x, double y, double z, double dx, double dy, double dz, IIcon icon) {
super(world, x, y, z, dx, dy, dz);
particleIcon = icon;
particleGravity = 1;
particleRed = particleGreen = particleBlue = 0.6F;
particleScale /= 2.0F;
}
@Override
public int getFXLayer() {
return 1;
}
public void setScale(float scale) {
particleScale = scale;
}
public float getScale() {
return particleScale;
}
public void setMaxAge(int age) {
particleMaxAge = age;
}
public int getMaxAge() {
return particleMaxAge;
}
/**
* copy pasted from EntityDiggingFX
*/
@Override
public void renderParticle(Tessellator par1Tessellator, float par2, float par3, float par4, float par5, float par6,
float par7) {
float f6 = (particleTextureIndexX + particleTextureJitterX / 4.0F) / 16.0F;
float f7 = f6 + 0.015609375F;
float f8 = (particleTextureIndexY + particleTextureJitterY / 4.0F) / 16.0F;
float f9 = f8 + 0.015609375F;
float f10 = 0.1F * particleScale;
if (particleIcon != null) {
f6 = particleIcon.getInterpolatedU(particleTextureJitterX / 4.0F * 16.0F);
f7 = particleIcon.getInterpolatedU((particleTextureJitterX + 1.0F) / 4.0F * 16.0F);
f8 = particleIcon.getInterpolatedV(particleTextureJitterY / 4.0F * 16.0F);
f9 = particleIcon.getInterpolatedV((particleTextureJitterY + 1.0F) / 4.0F * 16.0F);
}
float f11 = (float) (prevPosX + (posX - prevPosX) * par2 - interpPosX);
float f12 = (float) (prevPosY + (posY - prevPosY) * par2 - interpPosY);
float f13 = (float) (prevPosZ + (posZ - prevPosZ) * par2 - interpPosZ);
float f14 = 1.0F;
par1Tessellator.setColorOpaque_F(f14 * particleRed, f14 * particleGreen, f14 * particleBlue);
par1Tessellator.addVertexWithUV(
f11 - par3 * f10 - par6 * f10,
f12 - par4 * f10,
f13 - par5 * f10 - par7 * f10,
f6,
f9);
par1Tessellator.addVertexWithUV(
f11 - par3 * f10 + par6 * f10,
f12 + par4 * f10,
f13 - par5 * f10 + par7 * f10,
f6,
f8);
par1Tessellator.addVertexWithUV(
f11 + par3 * f10 + par6 * f10,
f12 + par4 * f10,
f13 + par5 * f10 + par7 * f10,
f7,
f8);
par1Tessellator.addVertexWithUV(
f11 + par3 * f10 - par6 * f10,
f12 - par4 * f10,
f13 + par5 * f10 - par7 * f10,
f7,
f9);
}
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon,
EffectRenderer effectRenderer) {
float border = 0.1F;
Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2 * border);
diff.x *= world.rand.nextDouble();
diff.y *= world.rand.nextDouble();
diff.z *= world.rand.nextDouble();
Vector3 pos = diff.add(bounds.min).add(border);
if (side == 0) diff.y = bounds.min.y - border;
if (side == 1) diff.y = bounds.max.y + border;
if (side == 2) diff.z = bounds.min.z - border;
if (side == 3) diff.z = bounds.max.z + border;
if (side == 4) diff.x = bounds.min.x - border;
if (side == 5) diff.x = bounds.max.x + border;
effectRenderer.addEffect(
new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon).multiplyVelocity(0.2F)
.multipleParticleScaleBy(0.6F));
}
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons,
EffectRenderer effectRenderer) {
Vector3 diff = bounds.max.copy().subtract(bounds.min);
Vector3 center = bounds.min.copy().add(bounds.max).multiply(0.5);
Vector3 density = diff.copy().multiply(4);
density.x = Math.ceil(density.x);
density.y = Math.ceil(density.y);
density.z = Math.ceil(density.z);
for (int i = 0; i < density.x; ++i) for (int j = 0; j < density.y; ++j) for (int k = 0; k < density.z; ++k) {
double x = bounds.min.x + (i + 0.5) * diff.x / density.x;
double y = bounds.min.y + (j + 0.5) * diff.y / density.y;
double z = bounds.min.z + (k + 0.5) * diff.z / density.z;
effectRenderer.addEffect(
new EntityDigIconFX(
world,
x,
y,
z,
x - center.x,
y - center.y,
z - center.z,
icons[world.rand.nextInt(icons.length)]));
}
}
}
@@ -1,63 +0,0 @@
package codechicken.lib.render;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class FontUtils {
public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
public static void drawCenteredString(String s, int xCenter, int y, int colour) {
fontRenderer.drawString(s, xCenter - fontRenderer.getStringWidth(s) / 2, y, colour);
}
public static void drawRightString(String s, int xRight, int y, int colour) {
fontRenderer.drawString(s, xRight - fontRenderer.getStringWidth(s), y, colour);
}
public static final String[] prefixes = new String[] { "K", "M", "G" };
public static void drawItemQuantity(int x, int y, ItemStack item, String quantity, int mode) {
if (item == null || (quantity == null && item.stackSize <= 1)) return;
if (quantity == null) {
switch (mode) {
case 2:
int q = item.stackSize;
String postfix = "";
for (int p = 0; p < 3 && q > 1000; p++) {
q /= 1000;
postfix = prefixes[p];
}
quantity = Integer.toString(q) + postfix;
case 1:
quantity = "";
if (item.stackSize / 64 > 0) quantity += item.stackSize / 64 + "s";
if (item.stackSize % 64 > 0) quantity += item.stackSize % 64;
break;
default:
quantity = Integer.toString(item.stackSize);
break;
}
}
double scale = quantity.length() > 2 ? 0.5 : 1;
double sheight = 8 * scale;
double swidth = fontRenderer.getStringWidth(quantity) * scale;
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glPushMatrix();
GL11.glTranslated(x + 16 - swidth, y + 16 - sheight, 0);
GL11.glScaled(scale, scale, 1);
fontRenderer.drawStringWithShadow(quantity, 0, 0, LocalizedColours.ITEM_QUANTITY_TEXT);
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
}
}
@@ -1,6 +0,0 @@
package codechicken.lib.render;
public interface IFaceRenderer {
public void renderFace(Vertex5[] face, int side);
}
@@ -1,26 +0,0 @@
package codechicken.lib.render;
public class ManagedTextureFX extends TextureFX {
public boolean changed;
public ManagedTextureFX(int size, String name) {
super(size, name);
imageData = new int[size * size];
}
@Override
public void setup() {}
public void setData(int[] data) {
System.arraycopy(data, 0, imageData, 0, imageData.length);
changed = true;
}
@Override
public boolean changed() {
boolean r = changed;
changed = false;
return r;
}
}
@@ -1,22 +0,0 @@
package codechicken.lib.render;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
public class PlaceholderTexture extends TextureAtlasSprite {
protected PlaceholderTexture(String par1) {
super(par1);
}
@Override
public boolean hasCustomLoader(IResourceManager manager, ResourceLocation location) {
return true;
}
@Override
public boolean load(IResourceManager manager, ResourceLocation location) {
return true;
}
}
@@ -1,710 +0,0 @@
package codechicken.lib.render;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import codechicken.lib.render.uv.UV;
import codechicken.lib.render.uv.UVScale;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.CuboidCoord;
import codechicken.lib.vec.Rectangle4i;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Scale;
import codechicken.lib.vec.Transformation;
import codechicken.lib.vec.Translation;
import codechicken.lib.vec.Vector3;
public class QBImporter {
private static class ImagePackNode {
Rectangle4i rect;
ImagePackNode child1;
ImagePackNode child2;
QBImage packed;
public ImagePackNode(int x, int y, int w, int h) {
rect = new Rectangle4i(x, y, w, h);
}
public boolean pack(QBImage img) {
if (child1 != null) return child1.pack(img) || child2.pack(img);
if (packed != null) return false;
int fit = getFit(img.width(), img.height());
if (fit == 0) return false;
if ((fit & 2) != 0) { // exact fit
packed = img;
img.packSlot = rect;
img.packT = new ImageTransform((fit & 1) << 2);
return true;
}
int w = (fit & 1) == 0 ? img.width() : img.height();
int h = (fit & 1) == 0 ? img.height() : img.width();
if (rect.w - w > rect.h - h) { // create split with biggest leftover space
child1 = new ImagePackNode(rect.x, rect.y, w, rect.h);
child2 = new ImagePackNode(rect.x + w, rect.y, rect.w - w, rect.h);
} else {
child1 = new ImagePackNode(rect.x, rect.y, rect.w, h);
child2 = new ImagePackNode(rect.x, rect.y + h, rect.w, rect.h - h);
}
return child1.pack(img);
}
private int getFit(int w, int h) {
if (w == rect.w && h == rect.h) return 2;
if (w == rect.h && h == rect.w) return 3;
if (rect.w >= w && rect.h >= h) return 4;
if (rect.w >= h && rect.h >= w) return 5;
return 0;
}
private static void nextSize(Rectangle4i rect, boolean square) {
if (square) {
rect.w <<= 1;
rect.h <<= 1;
} else {
if (rect.w == rect.h) rect.w *= 2;
else rect.h *= 2;
}
}
public static ImagePackNode pack(List<QBImage> images, boolean square) {
Collections.sort(images);
int area = 0;
for (QBImage img : images) area += img.area();
ImagePackNode node = new ImagePackNode(0, 0, 2, 2);
while (node.rect.area() < area) nextSize(node.rect, square);
while (true) {
boolean packed = true;
for (QBImage img : images) if (!node.pack(img)) {
packed = false;
break;
}
if (packed) return node;
node.child1 = node.child2 = null;
nextSize(node.rect, square);
}
}
public BufferedImage toImage() {
BufferedImage img = new BufferedImage(rect.w, rect.h, BufferedImage.TYPE_INT_ARGB);
write(img);
return img;
}
private void write(BufferedImage img) {
if (child1 != null) {
child1.write(img);
child2.write(img);
} else if (packed != null) {
ImageTransform t = packed.packT;
for (int u = 0; u < rect.w; u++) for (int v = 0; v < rect.h; v++) {
int rgba = t.access(packed, u, v);
img.setRGB(u + rect.x, v + rect.y, rgba >>> 8 | rgba << 24);
}
}
}
}
private static class ImageTransform {
int transform;
public ImageTransform(int i) {
transform = i;
}
public ImageTransform() {
this(0);
}
public boolean transpose() {
return (transform & 4) != 0;
}
public boolean flipU() {
return (transform & 1) != 0;
}
public boolean flipV() {
return (transform & 2) != 0;
}
public int access(QBImage img, int u, int v) {
if (transpose()) {
int tmp = u;
u = v;
v = tmp;
}
if (flipU()) u = img.width() - 1 - u;
if (flipV()) v = img.height() - 1 - v;
return img.data[u][v];
}
public UV transform(UV uv) {
if (transpose()) {
double tmp = uv.u;
uv.u = uv.v;
uv.v = tmp;
}
if (flipU()) uv.u = 1 - uv.u;
if (flipV()) uv.v = 1 - uv.v;
return uv;
}
}
public static class QBImage implements Comparable<QBImage> {
int[][] data;
ImageTransform packT;
Rectangle4i packSlot;
public int width() {
return data.length;
}
public int height() {
return data[0].length;
}
public int area() {
return width() * height();
}
@Override
public int compareTo(QBImage o) {
int a = area();
int b = o.area();
return a > b ? -1 : a == b ? 0 : 1;
}
public ImageTransform transformTo(QBImage img) {
if (width() == img.width() && height() == img.height()) for (int i = 0; i < 4; i++) {
ImageTransform t = new ImageTransform(i);
if (equals(img, t)) return t;
}
if (width() == img.height() && height() == img.width()) for (int i = 4; i < 8; i++) {
ImageTransform t = new ImageTransform(i);
if (equals(img, t)) return t;
}
return null;
}
public boolean equals(QBImage img, ImageTransform t) {
for (int u = 0; u < img.width(); u++)
for (int v = 0; v < img.height(); v++) if (t.access(this, u, v) != img.data[u][v]) return false;
return true;
}
public void transform(UV uv) {
packT.transform(uv);
uv.u *= packSlot.w;
uv.v *= packSlot.h;
uv.u += packSlot.x;
uv.v += packSlot.y;
}
}
private static final int[][] vertOrder = new int[][] { // clockwise because MC is left handed
{ 3, 0 }, { 1, 0 }, { 1, 2 }, { 3, 2 } };
public static class QBQuad {
public Vertex5[] verts = new Vertex5[4];
public QBImage image = new QBImage();
public ImageTransform t = new ImageTransform();
public int side;
public QBQuad(int side) {
this.side = side;
}
public void applyImageT() {
for (Vertex5 vert : verts) {
t.transform(vert.uv);
image.transform(vert.uv);
}
}
public static QBQuad restore(Rectangle4i flat, int side, double d, QBImage img) {
QBQuad quad = new QBQuad(side);
quad.image = img;
Transformation t = new Scale(-1, 1, -1).with(Rotation.sideOrientation(side, 0))
.with(new Translation(new Vector3().setSide(side, d)));
quad.verts[0] = new Vertex5(flat.x, 0, flat.y, 0, 0);
quad.verts[1] = new Vertex5(flat.x + flat.w, 0, flat.y, 1, 0);
quad.verts[2] = new Vertex5(flat.x + flat.w, 0, flat.y + flat.h, 1, 1);
quad.verts[3] = new Vertex5(flat.x, 0, flat.y + flat.h, 0, 1);
for (Vertex5 vert : quad.verts) vert.apply(t);
return quad;
}
public Rectangle4i flatten() {
Transformation t = Rotation.sideOrientation(side, 0).inverse().with(new Scale(-1, 0, -1));
Vector3 vmin = verts[0].vec.copy().apply(t);
Vector3 vmax = verts[2].vec.copy().apply(t);
return new Rectangle4i((int) vmin.x, (int) vmin.z, (int) (vmax.x - vmin.x), (int) (vmax.z - vmin.z));
}
}
public static class QBCuboid {
public QBMatrix mat;
public CuboidCoord c;
public int sides;
public QBCuboid(QBMatrix mat, CuboidCoord c) {
this.mat = mat;
this.c = c;
sides = 0;
}
public static boolean intersects(QBCuboid a, QBCuboid b) {
CuboidCoord c = a.c;
CuboidCoord d = b.c;
return c.min.x <= d.max.x && d.min.x <= c.max.x
&& c.min.y <= d.max.y
&& d.min.y <= c.max.y
&& c.min.z <= d.max.z
&& d.min.z <= c.max.z;
}
public static void clip(QBCuboid a, QBCuboid b) {
if (intersects(a, b)) {
a.clip(b);
b.clip(a);
}
}
public void clip(QBCuboid o) {
CuboidCoord d = o.c;
for (int a = 0; a < 6; a += 2) {
int a1 = (a + 2) % 6;
int a2 = (a + 4) % 6;
if (c.getSide(a1 + 1) <= d.getSide(a1 + 1) && c.getSide(a1) >= d.getSide(a1)
&& c.getSide(a2 + 1) <= d.getSide(a2 + 1)
&& c.getSide(a2) >= d.getSide(a2)) {
if (c.getSide(a) <= d.getSide(a + 1) && c.getSide(a) >= d.getSide(a)) {
c.setSide(a, d.getSide(a + 1) + 1);
sides |= 1 << a;
}
if (c.getSide(a + 1) >= d.getSide(a) && c.getSide(a + 1) <= d.getSide(a + 1)) {
c.setSide(a + 1, d.getSide(a) - 1);
sides |= 2 << a;
}
}
}
}
public void extractQuads(List<QBQuad> quads) {
Cuboid6 box = c.bounds();
for (int s = 0; s < 6; s++) if ((sides & 1 << s) == 0) quads.add(extractQuad(s, box));
}
private QBQuad extractQuad(int side, Cuboid6 box) {
double[] da = new double[3];
da[side >> 1] = box.getSide(side);
QBQuad quad = new QBQuad(side);
for (int i = 0; i < 4; i++) {
int rU = vertOrder[i][0];
int rV = vertOrder[i][1];
int sideU = Rotation.rotateSide(side, rU);
int sideV = Rotation.rotateSide(side, rV);
da[sideU >> 1] = box.getSide(sideU);
da[sideV >> 1] = box.getSide(sideV);
quad.verts[i] = new Vertex5(Vector3.fromAxes(da), (3 - rU) / 2, rV / 2);
}
int sideU = Rotation.rotateSide(side, 1);
int sideV = Rotation.rotateSide(side, 2);
quad.image.data = new int[c.size(sideU)][c.size(sideV)];
QBImage image = quad.image;
int[] ia = new int[3];
ia[side >> 1] = c.getSide(side);
ia[sideU >> 1] = c.getSide(sideU ^ 1);
ia[sideV >> 1] = c.getSide(sideV ^ 1);
BlockCoord b = BlockCoord.fromAxes(ia);
BlockCoord bU = BlockCoord.sideOffsets[sideU];
BlockCoord bV = BlockCoord.sideOffsets[sideV];
for (int u = 0; u < image.width(); u++) for (int v = 0; v < image.height(); v++)
image.data[u][v] = mat.matrix[b.x + bU.x * u + bV.x * v][b.y + bU.y * u + bV.y * v][b.z + bU.z * u
+ bV.z * v];
return quad;
}
}
public static class QBMatrix {
public String name;
public BlockCoord pos;
public BlockCoord size;
public int[][][] matrix;
public void readMatrix(DataInputStream din, boolean compressed) throws IOException {
if (compressed) {
int z = 0;
while (z < size.z) {
int index = 0;
while (true) {
int data = din.readInt();
if (data == NEXTSLICEFLAG) break;
if (data == CODEFLAG) {
int count = readTni(din);
data = din.readInt();
for (int j = 0; j < count; j++, index++) matrix[index % size.x][index / size.x][z] = data;
} else {
matrix[index % size.x][index / size.x][z] = data;
index++;
}
}
z++;
}
} else {
for (int z = 0; z < size.z; z++)
for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) matrix[x][y][z] = din.readInt();
}
}
public void convertBGRAtoRGBA() {
for (int z = 0; z < size.z; z++) for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) {
int i = matrix[x][y][z];
matrix[x][y][z] = Integer.reverseBytes(i >>> 8) | i & 0xFF;
}
}
private boolean voxelFull(boolean[][][] solid, CuboidCoord c) {
for (BlockCoord b : c) if (matrix[b.x][b.y][b.z] == 0) return false;
for (BlockCoord b : c) solid[b.x][b.y][b.z] = false;
return true;
}
private QBCuboid expand(boolean[][][] solid, BlockCoord b) {
CuboidCoord c = new CuboidCoord(b);
solid[b.x][b.y][b.z] = false;
for (int s = 0; s < 6; s++) {
CuboidCoord slice = c.copy();
slice.expand(s ^ 1, -(slice.size(s) - 1));
slice.expand(s, 1);
while (slice.getSide(s) >= 0 && slice.getSide(s) < size.getSide(s)) {
if (!voxelFull(solid, slice)) break;
slice.expand(s ^ 1, -1);
slice.expand(s, 1);
c.expand(s, 1);
}
}
return new QBCuboid(this, c);
}
public List<QBCuboid> rectangulate() {
List<QBCuboid> list = new ArrayList<QBCuboid>();
boolean[][][] solid = new boolean[size.x][size.y][size.z];
for (int z = 0; z < size.z; z++)
for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) solid[x][y][z] = matrix[x][y][z] != 0;
for (int x = 0; x < size.x; x++) for (int z = 0; z < size.z; z++)
for (int y = 0; y < size.y; y++) if (solid[x][y][z]) list.add(expand(solid, new BlockCoord(x, y, z)));
for (int i = 0; i < list.size(); i++)
for (int j = i + 1; j < list.size(); j++) QBCuboid.clip(list.get(i), list.get(j));
return list;
}
public List<QBQuad> extractQuads(boolean texturePlanes) {
List<QBQuad> quads = new LinkedList<QBQuad>();
for (QBCuboid c : rectangulate()) c.extractQuads(quads);
if (texturePlanes) optimisePlanes(quads);
return quads;
}
private void optimisePlanes(List<QBQuad> quads) {
Multimap<Integer, QBQuad> map = HashMultimap.create();
for (QBQuad quad : quads) map.put(quad.side | ((int) quad.verts[0].vec.getSide(quad.side)) << 3, quad);
quads.clear();
for (Integer key : map.keySet()) {
Collection<QBQuad> plane = map.get(key);
if (plane.size() == 1) {
quads.add(plane.iterator().next());
continue;
}
int side = key & 7;
Rectangle4i rect = null;
for (QBQuad q : plane) if (rect == null) rect = q.flatten();
else rect.include(q.flatten());
QBImage img = new QBImage();
img.data = new int[rect.w][rect.h];
for (QBQuad q : plane) {
QBImage from = q.image;
Rectangle4i r = q.flatten();
int du = r.x - rect.x;
int dv = r.y - rect.y;
for (int u = 0; u < from.width(); u++)
for (int v = 0; v < from.height(); v++) img.data[du + u][dv + v] = from.data[u][v];
}
quads.add(QBQuad.restore(rect, side, key >> 3, img));
}
}
public CCModel buildModel(List<QBQuad> quads, BufferedImage img, boolean scaleMC) {
CCModel m = CCModel.quadModel(quads.size() * 4);
int i = 0;
for (QBQuad quad : quads) {
quad.applyImageT();
m.verts[i++] = quad.verts[0];
m.verts[i++] = quad.verts[1];
m.verts[i++] = quad.verts[2];
m.verts[i++] = quad.verts[3];
}
m.apply(new UVScale(1D / img.getWidth(), 1D / img.getHeight()));
m.apply(new Translation(pos.x, pos.y, pos.z));
if (scaleMC) m.apply(new Scale(1 / 16D));
m.computeNormals();
return m;
}
private static void addImages(List<QBQuad> quads, List<QBImage> images) {
for (QBQuad q : quads) {
QBImage img = q.image;
boolean matched = false;
for (QBImage img2 : images) {
ImageTransform t = img.transformTo(img2);
if (t != null) {
q.t = t;
q.image = img2;
matched = true;
break;
}
}
if (!matched) images.add(img);
}
}
}
public static final int TEXTUREPLANES = 1;
public static final int SQUARETEXTURE = 2;
public static final int MERGETEXTURES = 4;
public static final int SCALEMC = 8;
public static class QBModel {
public QBMatrix[] matrices;
public boolean rightHanded;
public RasterisedModel toRasterisedModel(int flags) {
List<QBImage> qbImages = new ArrayList<QBImage>();
List<List<QBQuad>> modelQuads = new ArrayList<List<QBQuad>>();
List<BufferedImage> images = new ArrayList<BufferedImage>();
boolean texturePlanes = (flags & TEXTUREPLANES) != 0;
boolean squareTextures = (flags & SQUARETEXTURE) != 0;
boolean mergeTextures = (flags & MERGETEXTURES) != 0;
boolean scaleMC = (flags & SCALEMC) != 0;
for (QBMatrix mat : matrices) {
List<QBQuad> quads = mat.extractQuads(texturePlanes);
modelQuads.add(quads);
QBMatrix.addImages(quads, qbImages);
if (!mergeTextures) {
images.add(ImagePackNode.pack(qbImages, squareTextures).toImage());
qbImages.clear();
}
}
if (mergeTextures) images.add(ImagePackNode.pack(qbImages, squareTextures).toImage());
RasterisedModel m = new RasterisedModel(images);
for (int i = 0; i < matrices.length; i++) {
QBMatrix mat = matrices[i];
BufferedImage img = images.get(mergeTextures ? 0 : i);
m.add(mat.name, mat.buildModel(modelQuads.get(i), img, scaleMC));
}
return m;
}
}
public static class RasterisedModel {
private class Holder {
CCModel m;
int img;
public Holder(CCModel m, int img) {
this.m = m;
this.img = img;
}
}
private Map<String, Holder> map = new HashMap<String, Holder>();
private List<BufferedImage> images;
private String[] icons;
public RasterisedModel(List<BufferedImage> images) {
this.images = images;
icons = new String[images.size()];
}
public void add(String name, CCModel m) {
map.put(name, new Holder(m, Math.min(map.size(), images.size() - 1)));
}
public CCModel getModel(String key) {
return map.get(key).m;
}
public IIcon getIcon(String key, IIconRegister r, String iconName) {
int img = map.get(key).img;
if (icons[img] != null && !iconName.equals(icons[img])) throw new IllegalArgumentException(
"Attempted to get a previously registered icon by a different name: " + icons[img]
+ ", "
+ iconName);
if (icons[img] != null) return r.registerIcon(iconName);
icons[img] = iconName;
return TextureUtils.getTextureSpecial(r, iconName).addTexture(new TextureDataHolder(images.get(img)));
}
private void exportImg(BufferedImage img, File imgFile) throws IOException {
if (!imgFile.exists()) imgFile.createNewFile();
ImageIO.write(img, "PNG", imgFile);
}
public void export(File objFile, File imgDir) {
try {
if (!objFile.exists()) objFile.createNewFile();
if (!imgDir.exists()) imgDir.mkdirs();
Map<String, CCModel> modelMap = new HashMap<String, CCModel>();
for (Map.Entry<String, Holder> e : map.entrySet()) modelMap.put(e.getKey(), e.getValue().m);
PrintWriter p = new PrintWriter(objFile);
CCModel.exportObj(modelMap, p);
p.close();
if (images.size() < map.size())
exportImg(images.get(0), new File(imgDir, objFile.getName().replaceAll("(.+)\\..+", "$1.png")));
else for (Map.Entry<String, Holder> e : map.entrySet())
exportImg(images.get(e.getValue().img), new File(imgDir, e.getKey() + ".png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static String readAsciiString(DataInputStream din) throws IOException {
byte[] bytes = new byte[din.readByte() & 0xFF];
din.readFully(bytes);
return new String(bytes, "US-ASCII");
}
private static int readTni(DataInputStream din) throws IOException {
return Integer.reverseBytes(din.readInt());
}
private static final int CODEFLAG = Integer.reverseBytes(2);
private static final int NEXTSLICEFLAG = Integer.reverseBytes(6);
public static QBModel loadQB(InputStream input) throws IOException {
DataInputStream din = new DataInputStream(input);
QBModel m = new QBModel();
int version = din.readInt();
int colorFormat = din.readInt();
m.rightHanded = din.readInt() != 0;
boolean compressed = din.readInt() != 0;
boolean visEncoded = din.readInt() != 0;
if (visEncoded) throw new IllegalArgumentException("Encoded Visiblity States not supported");
m.matrices = new QBMatrix[readTni(din)];
for (int i = 0; i < m.matrices.length; i++) {
QBMatrix mat = new QBMatrix();
m.matrices[i] = mat;
mat.name = readAsciiString(din);
mat.size = new BlockCoord(readTni(din), readTni(din), readTni(din));
mat.pos = new BlockCoord(readTni(din), readTni(din), readTni(din));
mat.matrix = new int[mat.size.x][mat.size.y][mat.size.z];
mat.readMatrix(din, compressed);
if (colorFormat == 1) mat.convertBGRAtoRGBA();
}
return m;
}
public static QBModel loadQB(ResourceLocation res) {
try {
return loadQB(Minecraft.getMinecraft().getResourceManager().getResource(res).getInputStream());
} catch (Exception e) {
throw new RuntimeException("failed to load model: " + res, e);
}
}
public static QBModel loadQB(File file) {
try {
FileInputStream fin = new FileInputStream(file);
try {
return loadQB(fin);
} finally {
fin.close();
}
} catch (Exception e) {
throw new RuntimeException("failed to load model: " + file.getPath(), e);
}
}
}
@@ -1,429 +0,0 @@
package codechicken.lib.render;
import static net.minecraftforge.client.IItemRenderer.ItemRenderType.ENTITY;
import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderItem;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraftforge.client.IItemRenderer;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Rectangle4i;
import codechicken.lib.vec.Vector3;
public class RenderUtils {
static RenderItem uniformRenderItem;
static EntityItem entityItem;
static {
uniformRenderItem = new RenderItem() {
public boolean shouldBob() {
return false;
}
};
uniformRenderItem.setRenderManager(RenderManager.instance);
entityItem = new EntityItem(null);
entityItem.hoverStart = 0;
}
@Deprecated
public static void renderFluidQuad(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, IIcon icon,
double res) {
// spotless:off
renderFluidQuad(
point2.x, point2.y, point2.z,
point4.x - point1.x, point4.y - point1.y, point4.z - point1.z,
point1.x - point2.x, point1.y - point2.y, point1.z - point2.z,
icon, res);
}
/**
* Draws a tessellated quadrilateral bottom to top, left to right
*
* @param base The bottom left corner of the quad
* @param wide The bottom of the quad
* @param high The left side of the quad
* @param res Units per icon
*/
@Deprecated
public static void renderFluidQuad(Vector3 base, Vector3 wide, Vector3 high, IIcon icon, double res) {
renderFluidQuad(
base.x, base.y, base.z,
wide.x, wide.y, wide.z,
wide.x, wide.y, wide.z,
icon, res);
}
/**
* Draws a tessellated quadrilateral bottom to top, left to right
* <p>
* base : The bottom left corner of the quad
* <p>
* wide : The bottom of the quad
* <p>
* high : The left side of the quad
* <p>
* res : Units per icon
*/
public static void renderFluidQuad(
double baseX, double baseY, double baseZ,
double wideX, double wideY, double wideZ,
double highX, double highY, double highZ,
IIcon icon, double res) {
Tessellator tessellator = Tessellator.instance;
double u = icon.getMinU();
double du = icon.getMaxU() - icon.getMinU();
double v = icon.getMinV();
double dv = icon.getMaxV() - icon.getMinV();
double wideLen = Math.sqrt(wideX * wideX + wideY * wideY + wideZ * wideZ);
double highLen = Math.sqrt(highX * highX + highY * highY + highZ * highZ);
double x = 0;
while (x < wideLen) {
double rx = wideLen - x;
if (rx > res) rx = res;
double y = 0;
while (y < highLen) {
double ry = highLen - y;
if (ry > res) ry = res;
final double mult1 = x / wideLen;
double dx1X = wideX * mult1;
double dx1Y = wideY * mult1;
double dx1Z = wideZ * mult1;
final double mult2 = (x + rx) / wideLen;
double dx2X = wideX * mult2;
double dx2Y = wideY * mult2;
double dx2Z = wideZ * mult2;
final double mult3 = y / highLen;
double dy1X = highX * mult3;
double dy1Y = highY * mult3;
double dy1Z = highZ * mult3;
final double mult4 = (y + ry) / highLen;
double dy2X = highX * mult4;
double dy2Y = highY * mult4;
double dy2Z = highZ * mult4;
tessellator.addVertexWithUV(
baseX + dx1X + dy2X,
baseY + dx1Y + dy2Y,
baseZ + dx1Z + dy2Z,
u,
v + ry / res * dv);
tessellator.addVertexWithUV(
baseX + dx1X + dy1X,
baseY + dx1Y + dy1Y,
baseZ + dx1Z + dy1Z,
u,
v);
tessellator.addVertexWithUV(
baseX + dx2X + dy1X,
baseY + dx2Y + dy1Y,
baseZ + dx2Z + dy1Z,
u + rx / res * du,
v);
tessellator.addVertexWithUV(
baseX + dx2X + dy2X,
baseY + dx2Y + dy2Y,
baseZ + dx2Z + dy2Z,
u + rx / res * du,
v + ry / res * dv);
y += ry;
}
x += rx;
}
}
public static void translateToWorldCoords(Entity entity, float partialTicks) {
double interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks;
double interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks;
double interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks;
GL11.glTranslated(-interpPosX, -interpPosY, -interpPosZ);
}
public static void drawCuboidOutline(Cuboid6 c) {
Tessellator tess = Tessellator.instance;
tess.startDrawing(3);
tess.addVertex(c.min.x, c.min.y, c.min.z);
tess.addVertex(c.max.x, c.min.y, c.min.z);
tess.addVertex(c.max.x, c.min.y, c.max.z);
tess.addVertex(c.min.x, c.min.y, c.max.z);
tess.addVertex(c.min.x, c.min.y, c.min.z);
tess.draw();
tess.startDrawing(3);
tess.addVertex(c.min.x, c.max.y, c.min.z);
tess.addVertex(c.max.x, c.max.y, c.min.z);
tess.addVertex(c.max.x, c.max.y, c.max.z);
tess.addVertex(c.min.x, c.max.y, c.max.z);
tess.addVertex(c.min.x, c.max.y, c.min.z);
tess.draw();
tess.startDrawing(1);
tess.addVertex(c.min.x, c.min.y, c.min.z);
tess.addVertex(c.min.x, c.max.y, c.min.z);
tess.addVertex(c.max.x, c.min.y, c.min.z);
tess.addVertex(c.max.x, c.max.y, c.min.z);
tess.addVertex(c.max.x, c.min.y, c.max.z);
tess.addVertex(c.max.x, c.max.y, c.max.z);
tess.addVertex(c.min.x, c.min.y, c.max.z);
tess.addVertex(c.min.x, c.max.y, c.max.z);
tess.draw();
}
public static void renderFluidCuboid(CCRenderState state, Cuboid6 bound, IIcon tex, double res) {
renderFluidCuboid(bound, tex, res);
}
public static void renderFluidCuboid(Cuboid6 bound, IIcon tex, double res) {
final double minX = bound.min.x;
final double minY = bound.min.y;
final double minZ = bound.min.z;
final double maxX = bound.max.x;
final double maxY = bound.max.y;
final double maxZ = bound.max.z;
renderFluidQuad( // bottom
minX, minY, minZ,
maxX - minX, 0, 0,
0, 0, maxZ - minZ,
tex, res);
renderFluidQuad( // top
minX, maxY, minZ,
0, 0, maxZ - minZ,
maxX - minX, 0, 0,
tex, res);
renderFluidQuad( // -x
minX, maxY, minZ,
0, minY - maxY, 0,
0, 0, maxZ - minZ,
tex, res);
renderFluidQuad( // +x
maxX, maxY, maxZ,
0, minY - maxY, 0,
0, 0, minZ - maxZ,
tex, res);
renderFluidQuad( // -z
maxX, maxY, minZ,
0, minY - maxY, 0,
minX - maxX, 0, 0,
tex, res);
renderFluidQuad( // +z
minX, maxY, maxZ,
0, minY - maxY, 0,
maxX - minX, 0, 0,
tex, res);
}
// spotless:on
public static void renderBlockOverlaySide(int x, int y, int z, int side, double tx1, double tx2, double ty1,
double ty2) {
Tessellator tessellator = Tessellator.instance;
final double minX = x - 0.009;
final double maxX = x + 1.009;
final double minY = y - 0.009;
final double maxY = y + 1.009;
final double minZ = z - 0.009;
final double maxZ = z + 1.009;
switch (side) {
case 0:
tessellator.addVertexWithUV(minX, minY, minZ, tx1, ty1);
tessellator.addVertexWithUV(maxX, minY, minZ, tx2, ty1);
tessellator.addVertexWithUV(maxX, minY, maxZ, tx2, ty2);
tessellator.addVertexWithUV(minX, minY, maxZ, tx1, ty2);
break;
case 1:
tessellator.addVertexWithUV(maxX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(minX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(minX, maxY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(maxX, maxY, maxZ, tx2, ty2);
break;
case 2:
tessellator.addVertexWithUV(minX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(maxX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(maxX, minY, minZ, tx1, ty2);
tessellator.addVertexWithUV(minX, minY, minZ, tx2, ty2);
break;
case 3:
tessellator.addVertexWithUV(maxX, maxY, maxZ, tx2, ty1);
tessellator.addVertexWithUV(minX, maxY, maxZ, tx1, ty1);
tessellator.addVertexWithUV(minX, minY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(maxX, minY, maxZ, tx2, ty2);
break;
case 4:
tessellator.addVertexWithUV(minX, maxY, maxZ, tx2, ty1);
tessellator.addVertexWithUV(minX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(minX, minY, minZ, tx1, ty2);
tessellator.addVertexWithUV(minX, minY, maxZ, tx2, ty2);
break;
case 5:
tessellator.addVertexWithUV(maxX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(maxX, maxY, maxZ, tx1, ty1);
tessellator.addVertexWithUV(maxX, minY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(maxX, minY, minZ, tx2, ty2);
break;
}
}
public static boolean shouldRenderFluid(FluidStack stack) {
return stack.amount > 0 && stack.getFluid() != null;
}
/**
* @param stack The fluid stack to render
* @return The icon of the fluid
*/
public static IIcon prepareFluidRender(CCRenderState state, FluidStack stack, int alpha) {
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
Fluid fluid = stack.getFluid();
state.setColourInstance(fluid.getColor(stack) << 8 | alpha);
TextureUtils.bindAtlas(fluid.getSpriteNumber());
return TextureUtils.safeIcon(fluid.getIcon(stack));
}
public static IIcon prepareFluidRender(FluidStack stack, int alpha) {
return prepareFluidRender(CCRenderState.instance(), stack, alpha);
}
/**
* Re-enables lighting and disables blending.
*/
public static void postFluidRender() {
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
}
public static double fluidDensityToAlpha(double d) {
return Math.pow(d, 0.4);
}
/**
* Renders a fluid within a bounding box. If the fluid is a liquid it will render as a normal tank with height equal
* to fillRate/bound.height. If the fluid is a gas, it will render the full box with an alpha equal to fillRate.
* Warning, bound will be mutated if the fluid is a liquid
*
* @param stack The fluid to render.
* @param bound The bounding box within which the fluid is contained.
* @param fillRate The volume of fluid / the capacity of the tank. This is a double between 0 and 1.
* @param res The resolution to render at.
*/
public static void renderFluidCuboid(CCRenderState state, FluidStack stack, Cuboid6 bound, double fillRate,
double res) {
if (!shouldRenderFluid(stack)) return;
fillRate = MathHelper.clamp_double(fillRate, 0d, 1d);
int alpha = 255;
if (stack.getFluid().isGaseous()) {
alpha = (int) (fluidDensityToAlpha(fillRate) * 255);
} else {
bound.max.y = bound.min.y + (bound.max.y - bound.min.y) * fillRate;
}
IIcon tex = prepareFluidRender(state, stack, alpha);
state.startDrawingInstance();
renderFluidCuboid(bound, tex, res);
state.drawInstance();
postFluidRender();
}
/**
* Renders a fluid within a bounding box. If the fluid is a liquid it will render as a normal tank with height equal
* to fillRate/bound.height. If the fluid is a gas, it will render the full box with an alpha equal to fillRate.
* Warning, bound will be mutated if the fluid is a liquid
*
* @param stack The fluid to render.
* @param bound The bounding box within which the fluid is contained.
* @param fillRate The volume of fluid / the capacity of the tank. This is a double between 0 and 1.
* @param res The resolution to render at.
*/
public static void renderFluidCuboid(FluidStack stack, Cuboid6 bound, double fillRate, double res) {
renderFluidCuboid(CCRenderState.instance(), stack, bound, fillRate, res);
}
public static void renderFluidGauge(CCRenderState state, FluidStack stack, Rectangle4i rect, double fillRate,
double res) {
if (!shouldRenderFluid(stack)) return;
fillRate = MathHelper.clamp_double(fillRate, 0d, 1d);
int alpha = 255;
if (stack.getFluid().isGaseous()) {
alpha = (int) (fluidDensityToAlpha(fillRate) * 255);
} else {
int height = (int) (rect.h * fillRate);
rect.y += rect.h - height;
rect.h = height;
}
IIcon tex = prepareFluidRender(state, stack, alpha);
state.startDrawingInstance();
renderFluidQuad(rect.x, rect.y + rect.h, 0, rect.w, 0, 0, 0, -rect.h, 0, tex, res);
state.drawInstance();
postFluidRender();
}
public static void renderFluidGauge(FluidStack stack, Rectangle4i rect, double fillRate, double res) {
renderFluidGauge(CCRenderState.instance(), stack, rect, fillRate, res);
}
/**
* Renders items and blocks in the world at 0,0,0 with transformations that size them appropriately
*/
public static void renderItemUniform(ItemStack item) {
renderItemUniform(item, 0);
}
/**
* Renders items and blocks in the world at 0,0,0 with transformations that size them appropriately
*
* @param spin The spin angle of the item around the y axis in degrees
*/
public static void renderItemUniform(ItemStack item, double spin) {
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, ENTITY);
boolean is3D = customRenderer != null && customRenderer.shouldUseRenderHelper(ENTITY, item, BLOCK_3D);
boolean larger = false;
if (item.getItem() instanceof ItemBlock
&& RenderBlocks.renderItemIn3d(Block.getBlockFromItem(item.getItem()).getRenderType())) {
int renderType = Block.getBlockFromItem(item.getItem()).getRenderType();
larger = !(renderType == 1 || renderType == 19 || renderType == 12 || renderType == 2);
} else if (is3D) {
larger = true;
}
double d = 2;
double d1 = 1 / d;
if (larger) GL11.glScaled(d, d, d);
GL11.glColor4f(1, 1, 1, 1);
entityItem.setEntityItemStack(item);
uniformRenderItem.doRender(entityItem, 0, larger ? 0.09 : 0.06, 0, 0, (float) (spin * 9 / Math.PI));
if (larger) GL11.glScaled(d1, d1, d1);
}
}
@@ -1,125 +0,0 @@
package codechicken.lib.render;
import static org.lwjgl.opengl.ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB;
import static org.lwjgl.opengl.ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB;
import static org.lwjgl.opengl.ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB;
import static org.lwjgl.opengl.ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB;
import static org.lwjgl.opengl.ARBShaderObjects.glAttachObjectARB;
import static org.lwjgl.opengl.ARBShaderObjects.glCompileShaderARB;
import static org.lwjgl.opengl.ARBShaderObjects.glCreateProgramObjectARB;
import static org.lwjgl.opengl.ARBShaderObjects.glCreateShaderObjectARB;
import static org.lwjgl.opengl.ARBShaderObjects.glDeleteObjectARB;
import static org.lwjgl.opengl.ARBShaderObjects.glGetInfoLogARB;
import static org.lwjgl.opengl.ARBShaderObjects.glGetObjectParameteriARB;
import static org.lwjgl.opengl.ARBShaderObjects.glLinkProgramARB;
import static org.lwjgl.opengl.ARBShaderObjects.glShaderSourceARB;
import static org.lwjgl.opengl.ARBShaderObjects.glUseProgramObjectARB;
import static org.lwjgl.opengl.ARBShaderObjects.glValidateProgramARB;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;
public class ShaderProgram {
int programID;
public ShaderProgram() {
programID = glCreateProgramObjectARB();
if (programID == 0) throw new RuntimeException("Unable to allocate shader program object.");
}
public void attach(int shaderType, String resource) {
InputStream stream = ShaderProgram.class.getResourceAsStream(resource);
if (stream == null) throw new RuntimeException("Unable to locate resource: " + resource);
attach(shaderType, stream);
}
public void use() {
glUseProgramObjectARB(programID);
}
public static void restore() {
glUseProgramObjectARB(0);
}
public void link() {
glLinkProgramARB(programID);
if (glGetObjectParameteriARB(programID, GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Error linking program: " + getInfoLog(programID));
glValidateProgramARB(programID);
if (glGetObjectParameteriARB(programID, GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Error validating program: " + getInfoLog(programID));
use();
onLink();
restore();
}
public void attach(int shaderType, InputStream stream) {
if (stream == null) throw new RuntimeException("Invalid shader inputstream");
int shaderID = 0;
try {
shaderID = glCreateShaderObjectARB(shaderType);
if (shaderID == 0) throw new RuntimeException("Unable to allocate shader object.");
try {
glShaderSourceARB(shaderID, asString(stream));
} catch (IOException e) {
throw new RuntimeException("Error reading inputstream.", e);
}
glCompileShaderARB(shaderID);
if (glGetObjectParameteriARB(shaderID, GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Error compiling shader: " + getInfoLog(shaderID));
glAttachObjectARB(programID, shaderID);
} catch (RuntimeException e) {
glDeleteObjectARB(shaderID);
throw e;
}
}
public static String asString(InputStream stream) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader bin = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = bin.readLine()) != null) sb.append(line).append('\n');
stream.close();
return sb.toString();
}
private static String getInfoLog(int shaderID) {
return glGetInfoLogARB(shaderID, glGetObjectParameteriARB(shaderID, GL_OBJECT_INFO_LOG_LENGTH_ARB));
}
public int getUniformLoc(String name) {
return ARBShaderObjects.glGetUniformLocationARB(programID, name);
}
public int getAttribLoc(String name) {
return ARBVertexShader.glGetAttribLocationARB(programID, name);
}
public void uniformTexture(String name, int textureIndex) {
ARBShaderObjects.glUniform1iARB(getUniformLoc(name), textureIndex);
}
public void onLink() {}
public void glVertexAttributeMat4(int loc, Matrix4f matrix) {
ARBVertexShader.glVertexAttrib4fARB(loc, matrix.m00, matrix.m01, matrix.m02, matrix.m03);
ARBVertexShader.glVertexAttrib4fARB(loc + 1, matrix.m10, matrix.m11, matrix.m12, matrix.m13);
ARBVertexShader.glVertexAttrib4fARB(loc + 2, matrix.m20, matrix.m21, matrix.m22, matrix.m23);
ARBVertexShader.glVertexAttrib4fARB(loc + 3, matrix.m30, matrix.m31, matrix.m32, matrix.m33);
}
}
@@ -1,129 +0,0 @@
package codechicken.lib.render;
import java.util.ArrayList;
import java.util.HashMap;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import codechicken.lib.render.TextureUtils.IIconSelfRegister;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class SpriteSheetManager {
@SideOnly(Side.CLIENT)
public static class SpriteSheet implements IIconSelfRegister {
private int tilesX;
private int tilesY;
private ArrayList<Integer> newSprites = new ArrayList<Integer>();
private TextureSpecial[] sprites;
private ResourceLocation resource;
private TextureDataHolder texture;
private int spriteWidth;
private int spriteHeight;
public int atlasIndex;
private SpriteSheet(int tilesX, int tilesY, ResourceLocation textureFile) {
this.tilesX = tilesX;
this.tilesY = tilesY;
this.resource = textureFile;
sprites = new TextureSpecial[tilesX * tilesY];
}
public void requestIndicies(int... indicies) {
for (int i : indicies) setupSprite(i);
}
public void registerIcons(IIconRegister register) {
TextureMap textureMap = (TextureMap) register;
if (TextureUtils.refreshTexture(textureMap, resource.getResourcePath())) {
reloadTexture();
for (TextureSpecial sprite : sprites)
if (sprite != null) textureMap.setTextureEntry(sprite.getIconName(), sprite);
} else {
for (int i : newSprites) textureMap.setTextureEntry(sprites[i].getIconName(), sprites[i]);
}
newSprites.clear();
}
public TextureSpecial setupSprite(int i) {
if (sprites[i] == null) {
String name = resource + "_" + i;
sprites[i] = new TextureSpecial(name).baseFromSheet(this, i);
newSprites.add(i);
}
return sprites[i];
}
private void reloadTexture() {
texture = TextureUtils.loadTexture(resource);
spriteWidth = texture.width / tilesX;
spriteHeight = texture.height / tilesY;
}
public IIcon getSprite(int index) {
IIcon i = sprites[index];
if (i == null) throw new IllegalArgumentException(
"Sprite at index: " + index + " from texture file " + resource + " was not preloaded.");
return i;
}
public TextureDataHolder createSprite(int spriteIndex) {
int sx = spriteIndex % tilesX;
int sy = spriteIndex / tilesX;
TextureDataHolder sprite = new TextureDataHolder(spriteWidth, spriteHeight);
TextureUtils.copySubImg(
texture.data,
texture.width,
sx * spriteWidth,
sy * spriteHeight,
spriteWidth,
spriteHeight,
sprite.data,
spriteWidth,
0,
0);
return sprite;
}
public int spriteWidth() {
return spriteWidth;
}
public int spriteHeight() {
return spriteHeight;
}
public TextureSpecial bindTextureFX(int i, TextureFX textureFX) {
return setupSprite(i).addTextureFX(textureFX);
}
public SpriteSheet selfRegister(int atlas) {
TextureUtils.addIconRegistrar(this);
return this;
}
@Override
public int atlasIndex() {
return atlasIndex;
}
}
private static HashMap<String, SpriteSheet> spriteSheets = new HashMap<String, SpriteSheet>();
public static SpriteSheet getSheet(ResourceLocation resource) {
return getSheet(16, 16, resource);
}
public static SpriteSheet getSheet(int tilesX, int tilesY, ResourceLocation resource) {
SpriteSheet sheet = spriteSheets.get(resource.toString());
if (sheet == null) spriteSheets.put(resource.toString(), sheet = new SpriteSheet(tilesX, tilesY, resource));
return sheet;
}
}
@@ -1,34 +0,0 @@
package codechicken.lib.render;
import java.awt.image.BufferedImage;
public class TextureDataHolder {
public int width;
public int height;
public int[] data;
public TextureDataHolder(int width, int height) {
this.width = width;
this.height = height;
data = new int[width * height];
}
public TextureDataHolder(int[] data, int width) {
this.data = data;
this.width = width;
height = data.length / width;
}
public TextureDataHolder(BufferedImage img) {
this(img.getWidth(), img.getHeight());
img.getRGB(0, 0, width, height, data, 0, width);
}
public TextureDataHolder copyData() {
int[] copy = new int[data.length];
System.arraycopy(data, 0, copy, 0, data.length);
data = copy;
return this;
}
}
Loaded 100 of 148 files, more files were not shown because too many files have changed in this diff. Show more