Compare commits

..
2 Commits
Author SHA1 Message Date
Dream Master 0ffe3048cb update 2025-07-29 14:31:37 +07:00
Alexdoru 69d1335aea Speedup ClassDiscoverer by using ClassReader.SKIP_CODE 2025-07-27 21:37:55 +02:00
42 changed files with 612 additions and 1189 deletions

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@ name: Build and test
on: on:
pull_request: pull_request:
branches: [ master, main, release/** ] branches: [ master, main ]
push: push:
branches: [ master, main, release/** ] branches: [ master, main ]
jobs: jobs:
build-and-test: build-and-test:
-3
View File
@@ -27,6 +27,3 @@ whitelist.json
*.iws *.iws
src/main/resources/mixins.*.json src/main/resources/mixins.*.json
*.bat *.bat
.DS_Store
*.sh
-7
View File
@@ -6,10 +6,3 @@ minecraft {
} }
} }
} }
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
-5
View File
@@ -3,8 +3,3 @@
plugins { plugins {
id 'com.gtnewhorizons.gtnhconvention' id 'com.gtnewhorizons.gtnhconvention'
} }
minecraft {
//extraRunJvmArguments.add("-Dccc.dev.runtimePublic=true")
//extraRunJvmArguments.add("-Dccc.dev.deobfuscate=true")
}
-3
View File
@@ -1,7 +1,4 @@
// Add your dependencies here // Add your dependencies here
dependencies { dependencies {
testImplementation(platform('org.junit:junit-bom:5.14.1'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
} }
+4 -44
View File
@@ -39,33 +39,9 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE. # `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer developmentEnvironmentUserName = Developer
# Enables modern Java syntax support. Valid values: # Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# - false: No modern syntax, Java 8 only # See https://github.com/bsideup/jabel for details on how this works.
# - jabel: Jabel syntax-only support, compiles to J8 bytecode enableModernJavaSyntax = true
# - 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 injecting missing generics into the decompiled source code for a better coding experience. # 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. # Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -114,9 +90,7 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise. # Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin = mixinPlugin =
# Specify the package that contains all of your Mixins. The package must exist or # Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
# 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.
mixinsPackage = mixinsPackage =
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -180,11 +154,6 @@ modrinthRelations =
# Leave this empty if you don't want to publish on CurseForge. # Leave this empty if you don't want to publish on CurseForge.
curseForgeProjectId = 746279 curseForgeProjectId = 746279
# Comma-separated supported CurseForge environments: client, server (case-insensitive).
# Use client or server for mods that only support one environment. Defaults to client,server.
# These tags describe where the mod can run, not whether installation is required on both sides.
curseForgeEnvironments = client,server
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge. # The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
# Syntax: type1:name1;type2:name2;... # Syntax: type1:name1;type2:name2;...
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible], # Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
@@ -197,12 +166,6 @@ curseForgeRelations =
# projects. New projects should not use this parameter. # projects. New projects should not use this parameter.
# customArchiveBaseName = # 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. # 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'. # 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. # The check is ONLY performed if the version is a git tag.
@@ -232,6 +195,3 @@ curseForgeRelations =
# This is meant to be set in $HOME/.gradle/gradle.properties. # This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true # 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/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9064f22734b42ac03e85df04b632e167/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/4d0277c49991306ac78048580fe34bcf/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a9fa3b809e484a1e1ac4308a5dda661a/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/aa99913fa6d767d899ffc96082da01ca/redirect
toolchainVersion=25
Binary file not shown.
+1 -3
View File
@@ -1,9 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000 networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+8 -5
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015 the original authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
############################################################################## ##############################################################################
# #
# gradlew start up script for POSIX generated by Gradle. # Gradle start up script for POSIX generated by Gradle.
# #
# Important for running: # Important for running:
# #
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole # bash, then to run this script, type that shell name before the whole
# command line, like: # command line, like:
# #
# ksh gradlew # ksh Gradle
# #
# Busybox and similar reduced shells will NOT work, because this script # Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features: # requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,6 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -171,6 +172,7 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -210,7 +212,8 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-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. # Stop when "xargs" is not available.
Vendored
+24 -12
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem gradlew startup script for Windows @rem Gradle startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled @rem Set local scope for the variables with windows NT shell
setlocal EnableExtensions if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2 echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1 goto fail
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,18 +65,30 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2 echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1 goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute gradlew @rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:exitWithErrorLevel :end
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts @rem End local scope for the variables with windows NT shell
"%COMSPEC%" /c exit %ERRORLEVEL% if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
} }
plugins { plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.30' id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
} }
@@ -116,7 +116,6 @@ public class ClassDiscoverer {
while (zipEntries.hasMoreElements()) { while (zipEntries.hasMoreElements()) {
ZipEntry zipentry = zipEntries.nextElement(); ZipEntry zipentry = zipEntries.nextElement();
String fullname = zipentry.getName().replace('\\', '/'); String fullname = zipentry.getName().replace('\\', '/');
if (fullname.startsWith("META-INF/versions/")) continue;
int pos = fullname.lastIndexOf('/'); int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1); String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) { if (!zipentry.isDirectory() && matcher.matches(name)) {
@@ -2,16 +2,12 @@ package codechicken.core;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen; 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.network.NetworkManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.World; import net.minecraft.world.World;
import codechicken.core.internal.CCCEventHandler; import codechicken.core.internal.CCCEventHandler;
import codechicken.lib.colour.LocalizedColours;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.Side;
@@ -69,16 +65,4 @@ public class ClientUtils extends CommonUtils {
.replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:"); .replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:");
GuiModListScroll.register(mod); 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 org.lwjgl.opengl.GL11;
import codechicken.core.launch.CodeChickenCorePlugin; import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.gui.GuiDraw; import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i; import codechicken.lib.vec.Rectangle4i;
import cpw.mods.fml.client.GuiModList; import cpw.mods.fml.client.GuiModList;
@@ -149,11 +148,10 @@ public class GuiModListScroll {
double dy = scroll % (height + 20); double dy = scroll % (height + 20);
GL11.glPushMatrix(); GL11.glPushMatrix();
GL11.glTranslated(0, -dy, 0); 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) { if (needsScroll) {
GL11.glTranslated(0, height + 20, 0); GL11.glTranslated(0, height + 20, 0);
GuiDraw.fontRenderer GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, 0xDDDDDD);
.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
} }
GL11.glPopMatrix(); GL11.glPopMatrix();
@@ -89,22 +89,6 @@ public class ReflectionManager {
} }
} }
/**
* Set a field of the object {@code instance} of type {@code class1} to {@code value}.
*
* @deprecated According to {@link Class#getDeclaredFields()}, the array of fields has no defined order, therefore
* selecting a field based on its index will result in JVM-specific behaviour. Use
* {@link #setField(Class, Object, String, Object)} instead.
*
* @param class1 The class which defines the field in question.
* @param instance The object being altered - must be an instance of {@code class1}
* @param fieldindex The index of the field being set.
* @param value The value to set the field to.
* @throws IllegalArgumentException If {@code instance} is not an instance of {@code class1}, or if {@code value}
* doesn't match the type of the field.
* @throws IllegalAccessException If unable to write to the field.
*/
@Deprecated
public static void setField(Class<?> class1, Object instance, int fieldindex, Object value) public static void setField(Class<?> class1, Object instance, int fieldindex, Object value)
throws IllegalArgumentException, IllegalAccessException { throws IllegalArgumentException, IllegalAccessException {
Field field = class1.getDeclaredFields()[fieldindex]; Field field = class1.getDeclaredFields()[fieldindex];
@@ -195,21 +179,6 @@ public class ReflectionManager {
return null; return null;
} }
/**
* Get a field of the object {@code instance} of type {@code class1}.
*
* @deprecated According to {@link Class#getDeclaredFields()}, the array of fields has no defined order, therefore
* selecting a field based on its index will result in JVM-specific behaviour. Use
* {@link #getField(Class, Class, Object, String)} instead.
*
* @param class1 The class which defines the field in question.
* @param fieldType The type of the field being read.
* @param instance The object being read from - must be an instance of {@code class1}
* @param fieldIndex The index of the field being read.
* @throws IllegalArgumentException If {@code instance} is not an instance of {@code class1}.
* @throws IllegalAccessException If unable to read from the field.
*/
@Deprecated
public static <T> T getField(Class<?> class1, Class<T> fieldType, Object instance, int fieldIndex) public static <T> T getField(Class<?> class1, Class<T> fieldType, Object instance, int fieldIndex)
throws IllegalArgumentException, IllegalAccessException { throws IllegalArgumentException, IllegalAccessException {
Field field = class1.getDeclaredFields()[fieldIndex]; Field field = class1.getDeclaredFields()[fieldIndex];
@@ -5,19 +5,32 @@ import java.lang.reflect.Field;
import com.google.common.collect.ImmutableBiMap; 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.AccessTransformer;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; 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 { public class CodeChickenAccessTransformer extends AccessTransformer {
private static final Field f_classNameBiMap; private static boolean makeAllPublic;
@SuppressWarnings("FieldMayBeFinal") private static Field f_classNameBiMap;
private static Object emptyMap = ImmutableBiMap.of(); 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 { try {
f_classNameBiMap = FMLDeobfuscatingRemapper.class.getDeclaredField("classNameBiMap"); f_classNameBiMap = FMLDeobfuscatingRemapper.class.getDeclaredField("classNameBiMap");
f_classNameBiMap.setAccessible(true); f_classNameBiMap.setAccessible(true);
@@ -26,18 +39,16 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
} }
} }
public CodeChickenAccessTransformer() throws IOException {}
@Override @Override
public byte[] transform(String name, String transformedName, byte[] bytes) { 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); if (setPublic) setClassMap(name);
bytes = super.transform(name, transformedName, bytes); bytes = super.transform(name, transformedName, bytes);
if (setPublic) restoreClassMap(); if (setPublic) restoreClassMap();
return bytes; return bytes;
} }
private static void restoreClassMap() { private void restoreClassMap() {
try { try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap); f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap);
} catch (Exception e) { } catch (Exception e) {
@@ -45,7 +56,7 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
} }
} }
private static void setClassMap(String name) { private void setClassMap(String name) {
try { try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, ImmutableBiMap.of(name.replace('.', '/'), "")); f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, ImmutableBiMap.of(name.replace('.', '/'), ""));
} catch (Exception e) { } catch (Exception e) {
@@ -1,5 +1,6 @@
package codechicken.core.asm; package codechicken.core.asm;
import java.io.File;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -12,6 +13,7 @@ import codechicken.core.ClientUtils;
import codechicken.core.featurehack.LiquidTextures; import codechicken.core.featurehack.LiquidTextures;
import codechicken.core.internal.CCCEventHandler; import codechicken.core.internal.CCCEventHandler;
import codechicken.core.launch.CodeChickenCorePlugin; import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.config.ConfigFile;
import cpw.mods.fml.common.DummyModContainer; import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.LoadController; import cpw.mods.fml.common.LoadController;
@@ -24,6 +26,14 @@ import cpw.mods.fml.common.versioning.VersionRange;
public class CodeChickenCoreModContainer extends DummyModContainer { 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() { public CodeChickenCoreModContainer() {
super(getModMetadata()); super(getModMetadata());
} }
@@ -65,7 +75,6 @@ public class CodeChickenCoreModContainer extends DummyModContainer {
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
if (event.getSide().isClient()) { if (event.getSide().isClient()) {
ClientUtils.enhanceSupportersList("CodeChickenCore"); ClientUtils.enhanceSupportersList("CodeChickenCore");
ClientUtils.registerLocalizedColourReloadListener();
FMLCommonHandler.instance().bus().register(new CCCEventHandler()); FMLCommonHandler.instance().bus().register(new CCCEventHandler());
MinecraftForge.EVENT_BUS.register(new CCCEventHandler()); MinecraftForge.EVENT_BUS.register(new CCCEventHandler());
@@ -7,50 +7,35 @@ import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import net.minecraft.launchwrapper.IClassTransformer; 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.ClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import codechicken.lib.asm.ASMHelper; import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ClassHeirachyManager;
import codechicken.lib.asm.ObfMapping; import codechicken.lib.asm.ObfMapping;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DefaultImplementationTransformer implements IClassTransformer { 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) { private static ClassNode getClassNode(String name) {
if (impls.isEmpty()) { try {
String name = DefaultImplementationTransformer.class.getName(); return ASMHelper.createClassNode(cl.getClassBytes(name.replace('/', '.')));
FMLRelaunchLog.finer("Registering transformer %s", name); } catch (IOException e) {
Launch.classLoader.registerTransformer(name); 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 { static class InterfaceImpl {
public final String iname; public final String iname;
public ArrayList<MethodNode> impls = new ArrayList<>(); public ArrayList<MethodNode> impls = new ArrayList<MethodNode>();
public InterfaceImpl(String iname, String cname) { public InterfaceImpl(String iname, String cname) {
this.iname = iname; this.iname = iname;
HashSet<String> names = new HashSet<>(); HashSet<String> names = new HashSet<String>();
ClassNode inode = getClassNode(iname); ClassNode inode = getClassNode(iname);
for (MethodNode method : inode.methods) names.add(method.name + method.desc); 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) { public boolean patch(ClassNode cnode) {
LinkedList<String> names = new LinkedList<>(); LinkedList<String> names = new LinkedList<String>();
for (MethodNode method : cnode.methods) { for (MethodNode method : cnode.methods) {
ObfMapping m = new ObfMapping(cnode.name, method.name, method.desc).toRuntime(); ObfMapping m = new ObfMapping(cnode.name, method.name, method.desc).toRuntime();
names.add(m.s_name + m.s_desc); names.add(m.s_name + m.s_desc);
@@ -84,13 +69,25 @@ public class DefaultImplementationTransformer implements IClassTransformer {
} }
return changed; return changed;
} }
}
private static ClassNode getClassNode(String name) { private static HashMap<String, InterfaceImpl> impls = new HashMap<String, InterfaceImpl>();
try {
return ASMHelper.createClassNode(Launch.classLoader.getClassBytes(name.replace('/', '.'))); public static void registerDefaultImpl(String iname, String cname) {
} catch (IOException e) { impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname));
throw new RuntimeException(e); }
}
@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; package codechicken.core.asm;
import static codechicken.core.launch.CodeChickenCorePlugin.logger;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -9,9 +11,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.jar.Attributes;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.IClassTransformer;
@@ -21,18 +21,14 @@ import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import codechicken.core.launch.CodeChickenCorePlugin;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DelegatedTransformer implements IClassTransformer { public class DelegatedTransformer implements IClassTransformer {
private static final ArrayList<IClassTransformer> delegatedTransformers; private static ArrayList<IClassTransformer> delegatedTransformers;
private static final Method m_defineClass; private static Method m_defineClass;
private static final Field f_cachedClasses; private static Field f_cachedClasses;
private static IClassTransformer[] transformers;
static { public DelegatedTransformer() {
delegatedTransformers = new ArrayList<>(); delegatedTransformers = new ArrayList<IClassTransformer>();
try { try {
m_defineClass = ClassLoader.class m_defineClass = ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE); .getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
@@ -45,75 +41,14 @@ public class DelegatedTransformer implements IClassTransformer {
} }
@Override @Override
public byte[] transform(String name, String transformedName, byte[] basicClass) { public byte[] transform(String name, String tname, byte[] bytes) {
if (basicClass == null) return null; if (bytes == null) return null;
for (IClassTransformer t : transformers) { for (IClassTransformer trans : delegatedTransformers) bytes = trans.transform(name, tname, bytes);
basicClass = t.transform(name, transformedName, basicClass); return bytes;
}
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 static void addTransformer(String transformer, JarFile jar, File jarFile) { public static void addTransformer(String transformer, JarFile jar, File jarFile) {
CodeChickenCorePlugin.logger.debug("Adding CCTransformer: " + transformer); logger.debug("Adding CCTransformer: " + transformer);
try { try {
byte[] bytes; byte[] bytes;
bytes = Launch.classLoader.getClassBytes(transformer); bytes = Launch.classLoader.getClassBytes(transformer);
@@ -172,7 +107,7 @@ public class DelegatedTransformer implements IClassTransformer {
byte[] depbytes = readFully(jar.getInputStream(entry)); byte[] depbytes = readFully(jar.getInputStream(entry));
defineDependancies(depbytes, jar, jarFile, depStack); defineDependancies(depbytes, jar, jarFile, depStack);
CodeChickenCorePlugin.logger.debug("Defining dependancy: " + dependancy); logger.debug("Defining dependancy: " + dependancy);
defineClass(dependancy.replace('/', '.'), depbytes); defineClass(dependancy.replace('/', '.'), depbytes);
} }
@@ -193,6 +128,7 @@ public class DelegatedTransformer implements IClassTransformer {
while ((r = stream.read()) != -1) { while ((r = stream.read()) != -1) {
bos.write(r); bos.write(r);
} }
return bos.toByteArray(); return bos.toByteArray();
} }
} }
@@ -24,6 +24,7 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import codechicken.lib.asm.ASMHelper; import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.CC_ClassWriter; import codechicken.lib.asm.CC_ClassWriter;
import codechicken.lib.asm.ObfMapping; import codechicken.lib.asm.ObfMapping;
import codechicken.obfuscator.IHeirachyEvaluator; import codechicken.obfuscator.IHeirachyEvaluator;
@@ -34,11 +35,15 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes, IHeirachyEvaluator { public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes, IHeirachyEvaluator {
static {
ASMInit.init();
}
public static class LoadPlugin implements IFMLLoadingPlugin { public static class LoadPlugin implements IFMLLoadingPlugin {
@Override @Override
public String[] getASMTransformerClass() { public String[] getASMTransformerClass() {
return null; return new String[0];
} }
@Override @Override
@@ -113,20 +118,26 @@ public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes,
} }
public static void load() { public static void load() {
run = new ObfuscationRun( CodeChickenCoreModContainer.loadConfig();
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) { if (CodeChickenCoreModContainer.config.getTag("dev.deobfuscate")
ObfMapping.loadMCPRemapper(); .setComment("set to true to completely deobfuscate mcp names")
run.setSeargeConstants(); .getBooleanValue(!ObfMapping.obfuscated)) {
getTransformers().add(instance); run = new ObfuscationRun(
} else { false,
getTransformers().add(0, instance); // insert transformer as first. 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 java.util.Map;
import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.asm.ASMBlock; import codechicken.lib.asm.ASMBlock;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.ASMReader; import codechicken.lib.asm.ASMReader;
import codechicken.lib.asm.ModularASMTransformer; import codechicken.lib.asm.ModularASMTransformer;
import codechicken.lib.asm.ModularASMTransformer.MethodReplacer; 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.ModularASMTransformer.MethodWriter;
import codechicken.lib.asm.ObfMapping; import codechicken.lib.asm.ObfMapping;
import codechicken.lib.config.ConfigTag; import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class TweakTransformer implements IClassTransformer, Opcodes { public class TweakTransformer implements IClassTransformer, Opcodes {
private static final ModularASMTransformer transformer = new ModularASMTransformer(); static {
private static final Map<String, ASMBlock> blocks = ASMReader ASMInit.init();
.loadResource("/assets/codechickencore/asm/tweaks.asm"); }
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 ConfigTag tweaks;
public static void load() { public static void load() {
loadTransformersFromConfig(); CodeChickenCoreModContainer.loadConfig();
if (transformer.isEmpty()) { tweaks = CodeChickenCoreModContainer.config.getTag("tweaks")
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")
.setComment("Various tweaks that can be applied to game mechanics.").useBraces(); .setComment("Various tweaks that can be applied to game mechanics.").useBraces();
tweaks.removeTag("persistantLava"); tweaks.removeTag("persistantLava");
@@ -113,7 +103,7 @@ public class TweakTransformer implements IClassTransformer, Opcodes {
} }
@Override @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); return transformer.transform(name, bytes);
} }
} }
@@ -6,8 +6,6 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCButton extends GuiWidget { public class GuiCCButton extends GuiWidget {
public String text; public String text;
@@ -75,8 +73,7 @@ public class GuiCCButton extends GuiWidget {
} }
public int getTextColour(int mousex, int mousey) { public int getTextColour(int mousex, int mousey) {
return !isEnabled ? LocalizedColours.BUTTON_TEXT_DISABLED return !isEnabled ? 0xFFA0A0A0 : pointInside(mousex, mousey) ? 0xFFFFFFA0 : 0xFFE0E0E0;
: pointInside(mousex, mousey) ? LocalizedColours.BUTTON_TEXT_HOVER : LocalizedColours.BUTTON_TEXT;
} }
public GuiCCButton setActionCommand(String string) { public GuiCCButton setActionCommand(String string) {
@@ -5,8 +5,6 @@ import net.minecraft.util.ChatAllowedCharacters;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCTextField extends GuiWidget { public class GuiCCTextField extends GuiWidget {
private String text; private String text;
@@ -122,8 +120,8 @@ public class GuiCCTextField extends GuiWidget {
} }
public void drawBackground() { public void drawBackground() {
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, LocalizedColours.TEXT_FIELD_BORDER); drawRect(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0);
drawRect(x, y, x + width, y + height, LocalizedColours.TEXT_FIELD_BACKGROUND); drawRect(x, y, x + width, y + height, 0xff000000);
} }
public String getDrawText() { public String getDrawText() {
@@ -137,7 +135,7 @@ public class GuiCCTextField extends GuiWidget {
} }
public int getTextColour() { public int getTextColour() {
return isEnabled ? LocalizedColours.TEXT_FIELD_TEXT : LocalizedColours.TEXT_FIELD_TEXT_DISABLED; return isEnabled ? 0xe0e0e0 : 0x707070;
} }
public GuiCCTextField setMaxStringLength(int i) { public GuiCCTextField setMaxStringLength(int i) {
@@ -3,7 +3,6 @@ package codechicken.core.gui;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Rectangle; import java.awt.Rectangle;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper; import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.Rectangle4i; import codechicken.lib.vec.Rectangle4i;
@@ -185,29 +184,28 @@ public abstract class GuiScrollPane extends GuiWidget {
} }
public void drawBackground(float frame) { 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 abstract void drawContent(int mx, int my, float frame);
public void drawOverlay(float frame) { public void drawOverlay(float frame) {
// outlines // outlines
drawRect(x, y - 1, x + width, y, LocalizedColours.SCROLL_PANE_OVERLAY_TOP); drawRect(x, y - 1, x + width, y, 0xffa0a0a0); // top
drawRect(x, y + height, x + width, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_BOTTOM); drawRect(x, y + height, x + width, y + height + 1, 0xffa0a0a0); // bottom
drawRect(x - 1, y - 1, x, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_LEFT); drawRect(x - 1, y - 1, x, y + height + 1, 0xffa0a0a0); // left
drawRect(x + width, y - 1, x + width + 1, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_RIGHT); drawRect(x + width, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); // right
} }
public void drawScrollbar(float frame) { public void drawScrollbar(float frame) {
Rectangle r = scrollbarBounds(); 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, r.y + r.height, 0xFF8B8B8B); // corners
drawRect(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_TOP_LEFT); 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, LocalizedColours.SCROLLBAR_BOTTOM_RIGHT); 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, LocalizedColours.SCROLLBAR_FILL); drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, 0xFFC6C6C6); // scrollbar
int algn = scrollbarGuideAlignment(); int algn = scrollbarGuideAlignment();
if (algn != 0) if (algn != 0) drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, 0xFF808080); // lineguide
drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, LocalizedColours.SCROLLBAR_GUIDE);
} }
} }
@@ -9,6 +9,9 @@ import java.text.CharacterIterator;
import java.text.StringCharacterIterator; import java.text.StringCharacterIterator;
import java.util.List; import java.util.List;
import java.util.Map; 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.JEditorPane;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@@ -18,87 +21,48 @@ import javax.swing.event.HyperlinkListener;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import codechicken.core.asm.CodeChickenCoreModContainer;
import codechicken.core.asm.DelegatedTransformer; import codechicken.core.asm.DelegatedTransformer;
import codechicken.core.asm.MCPDeobfuscationTransformer; import codechicken.core.asm.MCPDeobfuscationTransformer;
import codechicken.core.asm.Tags; import codechicken.core.asm.Tags;
import codechicken.core.asm.TweakTransformer; import codechicken.core.asm.TweakTransformer;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.ConfigTag; import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion; import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser; import cpw.mods.fml.common.versioning.VersionParser;
import cpw.mods.fml.relauncher.CoreModManager; import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.FMLInjectionData; import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin; 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") @TransformerExclusions(value = { "codechicken.core.asm", "codechicken.obfuscator" })
@IFMLLoadingPlugin.TransformerExclusions({ "codechicken.core.asm", "codechicken.obfuscator", "codechicken.lib.asm", @MCVersion("1.7.10")
"codechicken.lib.config" }) public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
public static final String mcVersion = "[1.7.10]"; public static final String mcVersion = "[1.7.10]";
@Deprecated @Deprecated
public static final String version = Tags.VERSION; public static final String version = Tags.VERSION;
public static ConfigFile config;
public static File minecraftDir; public static File minecraftDir;
public static String currentMcVersion; public static String currentMcVersion;
public static Logger logger = LogManager.getLogger("CodeChickenCore"); public static Logger logger = LogManager.getLogger("CodeChickenCore");
public CodeChickenCorePlugin() { public CodeChickenCorePlugin() {
if (minecraftDir != null) return; if (minecraftDir != null) return; // get called twice, once for IFMLCallHook
minecraftDir = (File) FMLInjectionData.data()[6]; minecraftDir = (File) FMLInjectionData.data()[6];
currentMcVersion = (String) FMLInjectionData.data()[4]; 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 injectDeobfPlugin();
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();
} }
private void injectDeobfPlugin() { private void injectDeobfPlugin() {
try { try {
Class<?> wrapperClass = Class.forName("cpw.mods.fml.relauncher.CoreModManager$FMLPluginWrapper"); 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); .getConstructor(String.class, IFMLLoadingPlugin.class, File.class, Integer.TYPE, String[].class);
Field f_loadPlugins = CoreModManager.class.getDeclaredField("loadPlugins"); Field f_loadPlugins = CoreModManager.class.getDeclaredField("loadPlugins");
wrapperConstructor.setAccessible(true); wrapperConstructor.setAccessible(true);
@@ -112,7 +76,7 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
0, 0,
new String[0])); new String[0]));
} catch (Exception e) { } 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); 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,13 +1,14 @@
package codechicken.lib.asm; package codechicken.lib.asm;
import java.util.Map; import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap;
import java.util.function.Function; import java.util.HashSet;
import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
@@ -17,60 +18,47 @@ import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
*/ */
public class ClassHeirachyManager implements IClassTransformer { public class ClassHeirachyManager implements IClassTransformer {
private static final Map<String, ClassInfo> superclasses = new ConcurrentHashMap<>(1000);
static { static {
superclasses.put("java.lang.Object", ClassInfo.OBJECT); ASMInit.init();
} }
@Override public static class SuperCache {
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) return null; String superclass;
if (!superclasses.containsKey(transformedName)) { public HashSet<String> parents = new HashSet<String>();
declareASM(transformedName, bytes); private boolean flattened;
public void add(String parent) {
parents.add(parent);
} }
return bytes;
}
private static ClassInfo declareASM(String name, byte[] bytes) { public void flatten() {
final ClassReader classReader = new ClassReader(bytes); if (flattened) return;
final ClassInfo classInfo = new ClassInfo(classReader);
superclasses.put(name, classInfo);
return classInfo;
}
private static ClassInfo declareReflection(String name) throws ClassNotFoundException { for (String s : new ArrayList<String>(parents)) {
final Class<?> aclass = Class.forName(name); SuperCache c = declareClass(s);
final ClassInfo classInfo = new ClassInfo(aclass); if (c != null) {
superclasses.put(name, classInfo); c.flatten();
return classInfo; parents.addAll(c.parents);
} }
private static ClassInfo declareClass(String name) {
try {
byte[] bytes = Launch.classLoader.getClassBytes(unKey(name));
if (bytes != null) {
return declareASM(name, bytes);
} }
} catch (Exception ignored) {} flattened = true;
try { }
return declareReflection(name);
} catch (ClassNotFoundException ignored) {}
return null;
} }
private static String toKey(String name) { public static HashMap<String, SuperCache> superclasses = new HashMap<String, SuperCache>();
if (ObfMapping.obfuscated) { private static LaunchClassLoader cl = Launch.classLoader;
return FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
} public static String toKey(String name) {
return name.replace('/', '.'); if (ObfMapping.obfuscated)
name = FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
return name;
} }
private static String unKey(String name) { public static String unKey(String name) {
if (ObfMapping.obfuscated) { if (ObfMapping.obfuscated)
return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.'); name = FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
} return name;
return name.replace('/', '.');
} }
/** /**
@@ -84,99 +72,83 @@ public class ClassHeirachyManager implements IClassTransformer {
if (name.equals(superclass)) return true; if (name.equals(superclass)) return true;
ClassInfo classInfo = superclasses.get(name); SuperCache cache = declareClass(name);
if (classInfo == null) classInfo = declareClass(name); if (cache == null) // just can't handle this
if (classInfo == null) return false; return false;
return classInfo.hasSuper(superclass); cache.flatten();
return cache.parents.contains(superclass);
}
private static SuperCache declareClass(String name) {
name = toKey(name);
SuperCache cache = superclasses.get(name);
if (cache != null) return cache;
try {
byte[] bytes = cl.getClassBytes(unKey(name));
if (bytes != null) cache = declareASM(bytes);
} catch (Exception e) {}
if (cache != null) return cache;
try {
cache = declareReflection(name);
} catch (ClassNotFoundException e) {}
return cache;
}
private static SuperCache declareReflection(String name) throws ClassNotFoundException {
Class<?> aclass = Class.forName(name);
SuperCache cache = getOrCreateCache(name);
if (aclass.isInterface()) cache.superclass = "java.lang.Object";
else if (name.equals("java.lang.Object")) return cache;
else cache.superclass = toKey(aclass.getSuperclass().getName());
cache.add(cache.superclass);
for (Class<?> iclass : aclass.getInterfaces()) cache.add(toKey(iclass.getName()));
return cache;
}
private static SuperCache declareASM(byte[] bytes) {
ClassNode node = ASMHelper.createClassNode(bytes);
String name = toKey(node.name);
SuperCache cache = getOrCreateCache(name);
cache.superclass = toKey(node.superName.replace('/', '.'));
cache.add(cache.superclass);
for (String iclass : node.interfaces) cache.add(toKey(iclass.replace('/', '.')));
return cache;
}
@Override
public byte[] transform(String name, String tname, byte[] bytes) {
if (bytes == null) return null;
if (!superclasses.containsKey(tname)) declareASM(bytes);
return bytes;
}
public static SuperCache getOrCreateCache(String name) {
SuperCache cache = superclasses.get(name);
if (cache == null) superclasses.put(name, cache = new SuperCache());
return cache;
} }
public static String getSuperClass(String name, boolean runtime) { public static String getSuperClass(String name, boolean runtime) {
name = toKey(name); name = toKey(name);
SuperCache cache = declareClass(name);
if (cache == null) return "java.lang.Object";
ClassInfo classInfo = superclasses.get(name); cache.flatten();
if (classInfo == null) classInfo = declareClass(name); String s = cache.superclass;
if (classInfo == null || classInfo.superclass == null) return "java.lang.Object"; if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
final String s = classInfo.superclass;
if (!runtime) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
}
return s; return s;
} }
private static class ClassInfo {
public static final ClassInfo OBJECT = new ClassInfo();
private static final Map<String, String> STRING_POOL = new ConcurrentHashMap<>(256);
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 = pool(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] = pool(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 = pool(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] = pool(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);
}
private static String pool(String s) {
return STRING_POOL.computeIfAbsent(s, Function.identity());
}
}
} }
@@ -275,22 +275,17 @@ public class ModularASMTransformer {
} }
} }
public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<>(); public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<String, ClassNodeTransformerList>();
public void add(ClassNodeTransformer t) { public void add(ClassNodeTransformer t) {
ClassNodeTransformerList list = transformers.get(t.className()); ClassNodeTransformerList list = transformers.get(t.className());
if (list == null) { if (list == null) transformers.put(t.className(), list = new ClassNodeTransformerList());
transformers.put(t.className(), list = new ClassNodeTransformerList());
}
list.add(t); list.add(t);
} }
public boolean isEmpty() {
return transformers.isEmpty();
}
public byte[] transform(String name, byte[] bytes) { public byte[] transform(String name, byte[] bytes) {
if (bytes == null) return null; if (bytes == null) return null;
ClassNodeTransformerList list = transformers.get(name); ClassNodeTransformerList list = transformers.get(name);
return list == null ? bytes : list.transform(bytes); return list == null ? bytes : list.transform(bytes);
} }
@@ -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;
}
}
}
+3 -20
View File
@@ -18,7 +18,6 @@ import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL12;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper; import codechicken.lib.math.MathHelper;
import codechicken.lib.render.CCRenderState; import codechicken.lib.render.CCRenderState;
@@ -163,15 +162,7 @@ public class GuiDraw {
} }
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list) { public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list) {
drawMultilineTip( drawMultilineTip(font, x, y, list, 0xf0100010, 0xf0100010, 0x505000ff, 0x5028007F);
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, public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list, int bgStart, int bgEnd,
@@ -210,7 +201,7 @@ public class GuiDraw {
line.draw(x, y); line.draw(x, y);
y += line.getSize().height; y += line.getSize().height;
} else { } else {
font.drawStringWithShadow(s, x, y, LocalizedColours.TOOLTIP_TEXT); font.drawStringWithShadow(s, x, y, -1);
y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10; y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10;
} }
} }
@@ -224,15 +215,7 @@ public class GuiDraw {
} }
public static void drawTooltipBox(int x, int y, int w, int h) { public static void drawTooltipBox(int x, int y, int w, int h) {
drawTooltipBox( drawTooltipBox(x, y, w, h, 0xf0100010, 0xf0100010, 0x505000ff, 0x5028007F);
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, public static void drawTooltipBox(int x, int y, int w, int h, int bgStart, int bgEnd, int borderStart,
@@ -12,12 +12,7 @@ import codechicken.lib.vec.BlockCoord;
public class SimpleBrightnessModel implements CCRenderState.IVertexOperation { public class SimpleBrightnessModel implements CCRenderState.IVertexOperation {
public static final int operationIndex = CCRenderState.registerOperation(); public static final int operationIndex = CCRenderState.registerOperation();
private static final ThreadLocal<SimpleBrightnessModel> instances = ThreadLocal public static SimpleBrightnessModel instance = new SimpleBrightnessModel();
.withInitial(SimpleBrightnessModel::new);
public static SimpleBrightnessModel instance() {
return instances.get();
}
public IBlockAccess access; public IBlockAccess access;
public BlockCoord pos = new BlockCoord(); public BlockCoord pos = new BlockCoord();
@@ -2,7 +2,6 @@ package codechicken.lib.packet;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@@ -51,8 +50,6 @@ import io.netty.util.AttributeKey;
public final class PacketCustom implements MCDataInput, MCDataOutput { public final class PacketCustom implements MCDataInput, MCDataOutput {
private static final int MAX_DECOMPRESSED_SIZE = 2 * 1024 * 1024;
public static interface ICustomPacketHandler { public static interface ICustomPacketHandler {
} }
@@ -193,7 +190,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf = payload; byteBuf = payload;
type = byteBuf.readUnsignedByte(); type = byteBuf.readUnsignedByte();
if (type > 0x80) decompressPayload(); if (type > 0x80) decompress();
type &= 0x7F; type &= 0x7F;
} }
@@ -207,61 +204,44 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf.writeByte(type); byteBuf.writeByte(type);
} }
private void decompressPayload() { /**
if (byteBuf.readableBytes() < 4) throw new IllegalArgumentException("Missing compressed packet length"); * Decompresses the remaining ByteBuf (after type has been read) using Snappy
int declaredLength = byteBuf.readInt(); */
if (declaredLength < 0 || declaredLength > MAX_DECOMPRESSED_SIZE) private void decompress() {
throw new IllegalArgumentException("Invalid decompressed packet length: " + declaredLength);
Inflater inflater = new Inflater(); Inflater inflater = new Inflater();
ByteBuf decodedPayload = null;
try { try {
byte[] compressedInput = new byte[byteBuf.readableBytes()]; int len = byteBuf.readInt();
byteBuf.readBytes(compressedInput); ByteBuf out = Unpooled.buffer(len);
inflater.setInput(compressedInput); inflater.setInput(byteBuf.array(), byteBuf.readerIndex(), byteBuf.readableBytes());
decodedPayload = Unpooled.buffer(Math.min(declaredLength, 8192), declaredLength); inflater.inflate(out.array());
byte[] inflatedChunk = new byte[Math.min(declaredLength + 1, 8192)]; out.writerIndex(len);
while (!inflater.finished()) { byteBuf = out;
int inflatedBytes = inflater.inflate(inflatedChunk); } catch (Exception e) {
if (inflatedBytes > declaredLength - decodedPayload.writerIndex()) throw new RuntimeException(e);
throw new IllegalArgumentException("Decompressed packet exceeds declared length");
if (inflatedBytes == 0 && !inflater.finished())
throw new IllegalArgumentException("Incomplete or dictionary-dependent compressed packet");
decodedPayload.writeBytes(inflatedChunk, 0, inflatedBytes);
}
if (decodedPayload.writerIndex() != declaredLength || inflater.getRemaining() != 0)
throw new IllegalArgumentException(
"Compressed packet does not match declared length or has trailing data");
byteBuf = decodedPayload;
decodedPayload = null;
} catch (DataFormatException e) {
throw new IllegalArgumentException("Invalid compressed packet", e);
} finally { } finally {
if (decodedPayload != null) decodedPayload.release();
inflater.end(); inflater.end();
} }
} }
private void compressPayload() { /**
if ((byteBuf.getUnsignedByte(0) & 0x80) != 0) return; * Compresses the payload ByteBuf after the type byte
*/
int payloadLength = byteBuf.writerIndex() - 1; private void do_compress() {
if (payloadLength > MAX_DECOMPRESSED_SIZE)
throw new IllegalArgumentException("Packet payload exceeds 2 MiB decompressed limit: " + payloadLength);
Deflater deflater = new Deflater(); Deflater deflater = new Deflater();
try { try {
byteBuf.readerIndex(1); byteBuf.readerIndex(1);
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), payloadLength); int len = byteBuf.readableBytes();
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), len);
deflater.finish(); deflater.finish();
ByteBuf compressedPayload = Unpooled.buffer(payloadLength + 5); ByteBuf out = Unpooled.buffer(len + 5);
int compressedLength = deflater.deflate(compressedPayload.array(), 5, payloadLength); int clen = deflater.deflate(out.array(), 5, len);
if (compressedLength >= payloadLength - 5 || !deflater.finished()) return; if (clen >= len - 5 || !deflater.finished()) // not worth compressing, gets larger
return;
compressedPayload.setByte(0, type | 0x80); out.setByte(0, type | 0x80);
compressedPayload.setInt(1, payloadLength); out.setInt(1, len);
compressedPayload.writerIndex(compressedLength + 5); out.writerIndex(clen + 5);
byteBuf = compressedPayload; byteBuf = out;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
@@ -486,7 +466,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
public FluidStack readFluidStack() { public FluidStack readFluidStack() {
Fluid fluid = FluidRegistry.getFluid(readShort()); Fluid fluid = FluidRegistry.getFluid(readShort());
if (fluid == null) return null; if (fluid == null) fluid = FluidRegistry.WATER;
return new FluidStack(fluid, readVarInt(), readNBTTagCompound()); return new FluidStack(fluid, readVarInt(), readNBTTagCompound());
} }
@@ -494,7 +474,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
public FMLProxyPacket toPacket() { public FMLProxyPacket toPacket() {
if (incoming()) throw new IllegalStateException("Tried to write an incoming packet"); if (incoming()) throw new IllegalStateException("Tried to write an incoming packet");
if (byteBuf.readableBytes() > 32000 || (type & 0x80) != 0) compressPayload(); 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 // FML packet impl returns the whole of the backing array, copy used portion of array to another ByteBuf
return new FMLProxyPacket(byteBuf.copy(), channel); return new FMLProxyPacket(byteBuf.copy(), channel);
@@ -1,7 +1,6 @@
package codechicken.lib.render; package codechicken.lib.render;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.OpenGlHelper;
@@ -39,9 +38,9 @@ public class CCRenderState {
return instances.get(); return instances.get();
} }
private static volatile int nextOperationIndex; private static int nextOperationIndex;
public static synchronized int registerOperation() { public static int registerOperation() {
return nextOperationIndex++; return nextOperationIndex++;
} }
@@ -88,52 +87,15 @@ public class CCRenderState {
int operationID(); int operationID();
} }
public static final class AttributeKey<T> { private static ArrayList<VertexAttribute<?>> vertexAttributes = new ArrayList<>();
private static final HashMap<String, AttributeKey<?>> nameMap = new HashMap<>(); private static int registerVertexAttribute(VertexAttribute<?> attr) {
private static final ArrayList<AttributeKey<?>> attributeKeys = new ArrayList<>(); vertexAttributes.add(attr);
private static int nextLegacyId; return vertexAttributes.size() - 1;
public final String name;
public final int attributeKeyIndex;
public final int operationIndex;
private VertexAttribute<T> canonical;
private AttributeKey(String name) {
this.name = name;
this.attributeKeyIndex = attributeKeys.size();
this.operationIndex = registerOperation();
attributeKeys.add(this);
nameMap.put(name, this);
}
public static synchronized <T> AttributeKey<T> create(String name) {
if (nameMap.containsKey(name))
throw new IllegalArgumentException("Duplicate registration of attribute with name: " + name);
return new AttributeKey<>(name);
}
static synchronized <T> AttributeKey<T> createLegacy() {
return new AttributeKey<>("legacy#" + nextLegacyId++);
}
synchronized void setCanonical(VertexAttribute<T> attr) {
if (canonical == null) canonical = attr;
}
static synchronized AttributeKey<?> get(int index) {
return attributeKeys.get(index);
}
} }
private static final AttributeKey<Vector3[]> NORMAL_KEY = AttributeKey.create("normal");
private static final AttributeKey<int[]> COLOUR_KEY = AttributeKey.create("colour");
private static final AttributeKey<int[]> LIGHTING_KEY = AttributeKey.create("lighting");
private static final AttributeKey<int[]> SIDE_KEY = AttributeKey.create("side");
private static final AttributeKey<LC[]> LC_KEY = AttributeKey.create("lc");
public static VertexAttribute<?> getAttribute(int index) { public static VertexAttribute<?> getAttribute(int index) {
return AttributeKey.get(index).canonical; return vertexAttributes.get(index);
} }
/** /**
@@ -144,26 +106,14 @@ public class CCRenderState {
*/ */
public abstract static class VertexAttribute<T> implements IVertexOperation { public abstract static class VertexAttribute<T> implements IVertexOperation {
public final int attributeIndex; public final int attributeIndex = registerVertexAttribute(this);
private final int operationIndex; private final int operationIndex = registerOperation();
/** /**
* Set to true when the attrute is part of the pipeline. Should only be managed by CCRenderState when * Set to true when the attrute is part of the pipeline. Should only be managed by CCRenderState when
* constructing the pipeline * constructing the pipeline
*/ */
public boolean active = false; public boolean active = false;
protected VertexAttribute(AttributeKey<T> key) {
attributeIndex = key.attributeKeyIndex;
operationIndex = key.operationIndex;
key.setCanonical(this);
}
/** @deprecated keyless attributes get a per-construction index that is threadsafe */
@Deprecated
protected VertexAttribute() {
this(AttributeKey.createLegacy());
}
/** /**
* Construct a new array for storage of vertex attrutes in a model * Construct a new array for storage of vertex attrutes in a model
*/ */
@@ -239,7 +189,7 @@ public class CCRenderState {
return instances.get().lightingAttrib; return instances.get().lightingAttrib;
} }
public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>(NORMAL_KEY) { public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>() {
private Vector3[] normalRef; private Vector3[] normalRef;
@@ -267,7 +217,7 @@ public class CCRenderState {
else state.setNormalInstance(Rotation.axes[state.side]); else state.setNormalInstance(Rotation.axes[state.side]);
} }
}; };
public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>(COLOUR_KEY) { public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>() {
private int[] colourRef; private int[] colourRef;
@@ -289,7 +239,7 @@ public class CCRenderState {
else state.setColourInstance(state.baseColour); else state.setColourInstance(state.baseColour);
} }
}; };
public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>(LIGHTING_KEY) { public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>() {
private int[] colourRef; private int[] colourRef;
@@ -315,7 +265,7 @@ public class CCRenderState {
state.setColourInstance(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex])); state.setColourInstance(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex]));
} }
}; };
public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>(SIDE_KEY) { public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>() {
private int[] sideRef; private int[] sideRef;
@@ -342,7 +292,7 @@ public class CCRenderState {
/** /**
* Uses the position of the lightmatrix to compute LC if not provided * Uses the position of the lightmatrix to compute LC if not provided
*/ */
public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>(LC_KEY) { public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>() {
private LC[] lcRef; private LC[] lcRef;
private final Vector3 vec = new Vector3(); // for computation private final Vector3 vec = new Vector3(); // for computation
@@ -1,7 +1,5 @@
package codechicken.lib.render; package codechicken.lib.render;
import java.util.Arrays;
import net.minecraft.client.particle.EffectRenderer; import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX; import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
@@ -13,9 +11,6 @@ import codechicken.lib.vec.Vector3;
public class EntityDigIconFX extends EntityFX { public class EntityDigIconFX extends EntityFX {
/** Tint that leaves the particle colour untouched, for materials that don't colour their texture. */
public static final int NO_TINT = 0xFFFFFF;
public EntityDigIconFX(World world, double x, double y, double z, double dx, double dy, double dz, IIcon icon) { 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); super(world, x, y, z, dx, dy, dz);
particleIcon = icon; particleIcon = icon;
@@ -24,18 +19,6 @@ public class EntityDigIconFX extends EntityFX {
particleScale /= 2.0F; particleScale /= 2.0F;
} }
/**
* Multiplies the particle colour by an 0xRRGGBB tint, the equivalent of vanilla
* EntityDiggingFX.applyColourMultiplier. Materials that draw a greyscale texture and colour it at render time
* (leaves, grass tops) need this, or their particles come out flat grey.
*/
public EntityDigIconFX applyColourMultiplier(int colour) {
particleRed *= (colour >> 16 & 255) / 255F;
particleGreen *= (colour >> 8 & 255) / 255F;
particleBlue *= (colour & 255) / 255F;
return this;
}
@Override @Override
public int getFXLayer() { public int getFXLayer() {
return 1; return 1;
@@ -107,24 +90,8 @@ public class EntityDigIconFX extends EntityFX {
f9); f9);
} }
/**
* @deprecated the particle is never tinted, so a material that colours a greyscale texture at render time (leaves,
* grass tops) breaks into grey particles. Use
* {@link #addBlockHitEffects(World, Cuboid6, int, IIcon, int, EffectRenderer)}, passing
* {@link #NO_TINT} for materials whose texture is already coloured.
*/
@Deprecated
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon, public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon,
EffectRenderer effectRenderer) { EffectRenderer effectRenderer) {
addBlockHitEffects(world, bounds, side, icon, NO_TINT, effectRenderer);
}
/**
* Spawns a hit particle on side of bounds, tinted by colour (0xRRGGBB, {@link #NO_TINT} for none). The caller
* resolves the tint for the side it hit, so per-face materials like grass can tint only the faces that need it.
*/
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon, int colour,
EffectRenderer effectRenderer) {
float border = 0.1F; float border = 0.1F;
Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2 * border); Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2 * border);
diff.x *= world.rand.nextDouble(); diff.x *= world.rand.nextDouble();
@@ -139,34 +106,13 @@ public class EntityDigIconFX extends EntityFX {
if (side == 4) diff.x = bounds.min.x - border; if (side == 4) diff.x = bounds.min.x - border;
if (side == 5) diff.x = bounds.max.x + border; if (side == 5) diff.x = bounds.max.x + border;
EntityDigIconFX fx = new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon); effectRenderer.addEffect(
effectRenderer.addEffect(fx.applyColourMultiplier(colour).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F)); new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon).multiplyVelocity(0.2F)
.multipleParticleScaleBy(0.6F));
} }
/**
* @deprecated the particles are never tinted, so a material that colours a greyscale texture at render time
* (leaves, grass tops) breaks into grey particles. Use
* {@link #addBlockDestroyEffects(World, Cuboid6, IIcon[], int[], EffectRenderer)}, passing
* {@link #NO_TINT} for the faces whose texture is already coloured.
*/
@Deprecated
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons, public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons,
EffectRenderer effectRenderer) { EffectRenderer effectRenderer) {
int[] colours = new int[icons.length];
Arrays.fill(colours, NO_TINT);
addBlockDestroyEffects(world, bounds, icons, colours, effectRenderer);
}
/**
* Spawns break particles filling bounds, each drawn from a random face. colours holds the 0xRRGGBB tint for the
* matching entry of icons ({@link #NO_TINT} for none) and must be the same length, so a particle is tinted by the
* face it was taken from.
*/
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons, int[] colours,
EffectRenderer effectRenderer) {
if (icons.length != colours.length)
throw new IllegalArgumentException("icons and colours must have the same length");
Vector3 diff = bounds.max.copy().subtract(bounds.min); Vector3 diff = bounds.max.copy().subtract(bounds.min);
Vector3 center = bounds.min.copy().add(bounds.max).multiply(0.5); Vector3 center = bounds.min.copy().add(bounds.max).multiply(0.5);
Vector3 density = diff.copy().multiply(4); Vector3 density = diff.copy().multiply(4);
@@ -175,20 +121,19 @@ public class EntityDigIconFX extends EntityFX {
density.z = Math.ceil(density.z); 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) { 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 px = bounds.min.x + (i + 0.5) * diff.x / density.x; double x = bounds.min.x + (i + 0.5) * diff.x / density.x;
double py = bounds.min.y + (j + 0.5) * diff.y / density.y; double y = bounds.min.y + (j + 0.5) * diff.y / density.y;
double pz = bounds.min.z + (k + 0.5) * diff.z / density.z; double z = bounds.min.z + (k + 0.5) * diff.z / density.z;
int iconSide = world.rand.nextInt(icons.length); effectRenderer.addEffect(
EntityDigIconFX fx = new EntityDigIconFX( new EntityDigIconFX(
world, world,
px, x,
py, y,
pz, z,
px - center.x, x - center.x,
py - center.y, y - center.y,
pz - center.z, z - center.z,
icons[iconSide]); icons[world.rand.nextInt(icons.length)]));
effectRenderer.addEffect(fx.applyColourMultiplier(colours[iconSide]));
} }
} }
} }
@@ -6,8 +6,6 @@ import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class FontUtils { public class FontUtils {
public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
@@ -55,7 +53,7 @@ public class FontUtils {
GL11.glPushMatrix(); GL11.glPushMatrix();
GL11.glTranslated(x + 16 - swidth, y + 16 - sheight, 0); GL11.glTranslated(x + 16 - swidth, y + 16 - sheight, 0);
GL11.glScaled(scale, scale, 1); GL11.glScaled(scale, scale, 1);
fontRenderer.drawStringWithShadow(quantity, 0, 0, LocalizedColours.ITEM_QUANTITY_TEXT); fontRenderer.drawStringWithShadow(quantity, 0, 0, 0xFFFFFF);
GL11.glPopMatrix(); GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL11.GL_DEPTH_TEST);
@@ -55,10 +55,7 @@ public class WorldExtensionManager {
public void onChunkUnLoad(ChunkEvent.Unload event) { public void onChunkUnLoad(ChunkEvent.Unload event) {
if (event.getChunk() instanceof EmptyChunk) return; if (event.getChunk() instanceof EmptyChunk) return;
WorldExtension[] extensions = worldMap.get(event.world); for (WorldExtension extension : worldMap.get(event.world)) extension.unloadChunk(event.getChunk());
if (extensions == null) return;
for (WorldExtension extension : extensions) extension.unloadChunk(event.getChunk());
if (event.world.isRemote) removeChunk(event.world, event.getChunk()); if (event.world.isRemote) removeChunk(event.world, event.getChunk());
} }
@@ -1,26 +1 @@
codechickencore.update=Version %s of %s is available codechickencore.update=Version %s of %s is available
codechickencore.color.buttonTextDisabled=FFA0A0A0
codechickencore.color.buttonTextHover=FFFFFFA0
codechickencore.color.buttonText=FFE0E0E0
codechickencore.color.textFieldBorder=FFA0A0A0
codechickencore.color.textFieldBackground=FF000000
codechickencore.color.textFieldText=E0E0E0
codechickencore.color.textFieldTextDisabled=707070
codechickencore.color.background=FF000000
codechickencore.color.overlayTop=FFA0A0A0
codechickencore.color.overlayBottom=FFA0A0A0
codechickencore.color.overlayLeft=FFA0A0A0
codechickencore.color.overlayRight=FFA0A0A0
codechickencore.color.scrollbarCorners=FF8B8B8B
codechickencore.color.scrollbarTopLeft=FFF0F0F0
codechickencore.color.scrollbarBottomRight=FF555555
codechickencore.color.scrollbarFill=FFC6C6C6
codechickencore.color.scrollbarGuide=FF808080
codechickencore.color.tooltipBgStart=F0100010
codechickencore.color.tooltipBgEnd=F0100010
codechickencore.color.tooltipBorderStart=505000FF
codechickencore.color.tooltipBorderEnd=5028007F
codechickencore.color.tooltipText=FFFFFFFF
codechickencore.color.itemQuantityText=FFFFFF
codechickencore.color.modDescriptionText=DDDDDD
@@ -1 +0,0 @@
codechickencore.update=Доступна версія %s для %s
@@ -1,218 +0,0 @@
package codechicken.lib.packet;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.zip.Deflater;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class PacketCustomTest {
private static final int MAX_DECOMPRESSED_SIZE = 2 * 1024 * 1024;
@Test
public void rejectsLargeDeclarationWithoutCompressedData() {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(8 * 1024 * 1024));
}
@ParameterizedTest
@ValueSource(ints = { -1, Integer.MIN_VALUE, MAX_DECOMPRESSED_SIZE + 1, Integer.MAX_VALUE })
public void rejectsInvalidDeclaredLengths(int length) {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(length));
}
@Test
public void rejectsMissingCompressedDataWithinLimit() {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(MAX_DECOMPRESSED_SIZE));
}
@Test
public void rejectsCompleteStreamAboveDecodeLimit() {
assertRejected(compressed(new byte[MAX_DECOMPRESSED_SIZE + 1]));
}
@Test
public void rejectsTruncatedLengthHeader() {
assertRejected(Unpooled.buffer(4).writeByte(0x81).writeMedium(10));
}
@Test
public void rejectsMismatchedInflatedLengths() {
byte[] data = new byte[16384];
assertRejected(compressed(data).setInt(1, data.length - 1));
assertRejected(compressed(data).setInt(1, data.length + 1));
assertRejected(compressed(data).setInt(1, 0));
}
@Test
public void rejectsTruncatedStreamEvenWhenAllOutputWasProduced() {
ByteBuf payload = compressed(new byte[16384]);
payload.writerIndex(payload.writerIndex() - 1);
assertRejected(payload);
}
@Test
public void rejectsCorruptStream() {
ByteBuf payload = compressed(new byte[16384]);
payload.setByte(payload.writerIndex() - 1, payload.getByte(payload.writerIndex() - 1) ^ 1);
assertRejected(payload);
}
@Test
public void rejectsDictionaryDependentStream() {
assertRejected(compressed(new byte[16384], new byte[] { 1, 2, 3 }));
}
@Test
public void rejectsTrailingCompressedData() {
assertRejected(compressed(new byte[16384]).writeByte(0));
}
@ParameterizedTest
@ValueSource(ints = { 0, 1, 8192, 8193, MAX_DECOMPRESSED_SIZE })
public void decodesCompleteStreams(int length) {
byte[] data = new byte[length];
Arrays.fill(data, (byte) 42);
assertDecoded(compressed(data), data);
}
@Test
public void decodesSlicedAndDirectBuffers() {
byte[] data = new byte[16384];
Arrays.fill(data, (byte) 42);
ByteBuf payload = compressed(data);
ByteBuf prefixed = Unpooled.buffer().writeLong(-1).writeBytes(payload, 0, payload.readableBytes());
ByteBuf direct = Unpooled.directBuffer().writeBytes(payload);
payload.release();
try {
assertDecoded(prefixed.slice(8, prefixed.readableBytes() - 8).retain(), data);
assertDecoded(direct, data);
} finally {
prefixed.release();
}
}
@Test
public void preservesUncompressedPackets() {
ByteBuf payload = Unpooled.buffer().writeByte(7).writeInt(42);
try {
PacketCustom packet = new PacketCustom(payload);
assertEquals(7, packet.getType());
assertEquals(42, packet.readInt());
assertEquals(0, packet.getByteBuf().readableBytes());
} finally {
payload.release();
}
}
@Test
public void roundTripsOutgoingCompression() {
for (int length : new int[] { 16, 64, 40000, MAX_DECOMPRESSED_SIZE }) {
byte[] data = new byte[length];
Arrays.fill(data, (byte) 42);
PacketCustom outgoing = new PacketCustom("PacketTest", 7);
FMLProxyPacket wire = null;
try {
outgoing.writeByteArray(data);
if (length <= 64) outgoing.compress();
wire = outgoing.toPacket();
assertEquals(length == 16 ? 7 : 0x87, wire.payload().getUnsignedByte(0));
assertDecoded(wire.payload().retain(), data);
} finally {
if (wire != null) wire.payload().release();
outgoing.getByteBuf().release();
}
}
}
@ParameterizedTest
@ValueSource(booleans = { false, true })
public void repeatedSerializationPreservesCompressedPayload(boolean forceCompression) {
byte[] data = new byte[40000];
Arrays.fill(data, (byte) 42);
PacketCustom outgoing = new PacketCustom("PacketTest", 7).writeByteArray(data);
if (forceCompression) outgoing.compress();
try {
for (int serialization = 0; serialization < 3; serialization++) {
FMLProxyPacket wire = outgoing.toPacket();
try {
assertEquals(0x87, wire.payload().getUnsignedByte(0));
assertDecoded(wire.payload().retain(), data);
} finally {
wire.payload().release();
}
}
} finally {
outgoing.getByteBuf().release();
}
}
@Test
public void rejectsOutgoingPayloadAboveDecodeLimit() {
PacketCustom outgoing = new PacketCustom("PacketTest", 7);
try {
outgoing.getByteBuf().writeZero(MAX_DECOMPRESSED_SIZE + 1);
assertThrows(IllegalArgumentException.class, outgoing::toPacket);
} finally {
outgoing.getByteBuf().release();
}
}
private static ByteBuf compressed(byte[] data) {
return compressed(data, null);
}
private static ByteBuf compressed(byte[] data, byte[] dictionary) {
Deflater deflater = new Deflater();
try {
if (dictionary != null) deflater.setDictionary(dictionary);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
byte[] scratch = new byte[8192];
while (!deflater.finished()) {
int count = deflater.deflate(scratch);
compressed.write(scratch, 0, count);
}
return Unpooled.buffer().writeByte(0x87).writeInt(data.length).writeBytes(compressed.toByteArray());
} finally {
deflater.end();
}
}
private static void assertDecoded(ByteBuf payload, byte[] expected) {
PacketCustom packet = null;
try {
packet = new PacketCustom(payload);
assertEquals(7, packet.getType());
assertEquals(expected.length, packet.getByteBuf().readableBytes());
byte[] actual = new byte[expected.length];
packet.getByteBuf().readBytes(actual);
assertArrayEquals(expected, actual);
} finally {
if (packet != null && packet.getByteBuf() != payload) packet.getByteBuf().release();
payload.release();
}
}
private static void assertRejected(ByteBuf payload) {
try {
assertThrows(IllegalArgumentException.class, () -> {
PacketCustom packet = new PacketCustom(payload);
if (packet.getByteBuf() != payload) packet.getByteBuf().release();
});
} finally {
payload.release();
}
}
}
@@ -1,102 +0,0 @@
package codechicken.lib.render;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import codechicken.lib.vec.Vector3;
public class CCRenderStateThreadingTest {
private static int[] attribIndices() {
return new int[] { CCRenderState.normalAttrib().attributeIndex, CCRenderState.colourAttrib().attributeIndex,
CCRenderState.lightingAttrib().attributeIndex, CCRenderState.sideAttrib().attributeIndex,
CCRenderState.lightCoordAttrib().attributeIndex };
}
@Test
public void attributeIndicesStableAcrossThreads() throws Exception {
int[] main = attribIndices();
assertEquals(5, Arrays.stream(main).distinct().count());
ExecutorService pool = Executors.newFixedThreadPool(8);
try {
List<Future<int[]>> futures = new ArrayList<>();
for (int i = 0; i < 8; i++) futures.add(pool.submit(CCRenderStateThreadingTest::attribIndices));
for (Future<int[]> f : futures) assertArrayEquals(main, f.get(10, TimeUnit.SECONDS));
} finally {
pool.shutdownNow();
}
}
@Test
public void modelBakedOnOneThreadIsReadableOnAnother() throws Exception {
ExecutorService bakeThread = Executors.newSingleThreadExecutor();
ExecutorService renderThread = Executors.newSingleThreadExecutor();
try {
CCModel model = bakeThread.submit(() -> {
CCModel m = CCModel.quadModel(4);
m.verts[0] = new Vertex5(0, 1, 0, 0, 0);
m.verts[1] = new Vertex5(0, 1, 1, 0, 1);
m.verts[2] = new Vertex5(1, 1, 1, 1, 1);
m.verts[3] = new Vertex5(1, 1, 0, 1, 0);
return m.computeNormals();
}).get(10, TimeUnit.SECONDS);
int side = renderThread.submit(() -> {
assertTrue(
model.hasAttribute(CCRenderState.normalAttrib()),
"normals baked on another thread not visible under this thread's attributeIndex");
Vector3[] normals = model.getAttributes(CCRenderState.normalAttrib());
assertNotNull(normals);
return CCModel.findSide(normals[0]);
}).get(10, TimeUnit.SECONDS);
assertEquals(1, side);
assertEquals(1, CCModel.findSide(model.getAttributes(CCRenderState.normalAttrib())[0]));
} finally {
bakeThread.shutdownNow();
renderThread.shutdownNow();
}
}
@Test
public void concurrentFirstUseKeepsIndicesConsistent() throws Exception {
int threads = 16;
CyclicBarrier barrier = new CyclicBarrier(threads);
ExecutorService pool = Executors.newFixedThreadPool(threads);
try {
List<Future<int[]>> futures = new ArrayList<>();
for (int i = 0; i < threads; i++) futures.add(pool.submit(() -> {
barrier.await(10, TimeUnit.SECONDS);
return attribIndices();
}));
int[] expected = attribIndices();
for (Future<int[]> f : futures) assertArrayEquals(expected, f.get(10, TimeUnit.SECONDS));
} finally {
pool.shutdownNow();
}
}
@Test
public void getAttributeReturnsCanonicalForIndex() {
int normalIndex = CCRenderState.normalAttrib().attributeIndex;
CCRenderState.VertexAttribute<?> canonical = CCRenderState.getAttribute(normalIndex);
assertNotNull(canonical);
assertEquals(normalIndex, canonical.attributeIndex);
assertSame(canonical, CCRenderState.getAttribute(normalIndex));
}
}
@@ -1,20 +0,0 @@
package codechicken.lib.render;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import net.minecraft.util.IIcon;
import org.junit.jupiter.api.Test;
public class EntityDigIconFXTest {
@Test
public void rejectsMismatchedIconAndColourCountsBeforeSpawning() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> EntityDigIconFX.addBlockDestroyEffects(null, null, new IIcon[1], new int[0], null));
assertEquals("icons and colours must have the same length", exception.getMessage());
}
}