Compare commits

...
19 Commits
Author SHA1 Message Date
Jarnexis a9643dba1c Add initial Ukrainian (uk_UA) localization for CodeChickenCore (#55) 2026-09-19 20:15:54 +02:00
Martin Robertz 31d26db5b4 update+gradle+bs+deps+bs+gitignore
fix curse auto uploader
2026-09-17 22:32:16 +02:00
Algent 2077a8da86 Fix missing colour tint on block particles (#57) 2026-09-14 10:51:15 +02:00
Ryan Nasers ba757e4237 Fixing silly read write issue (#56) 2026-09-02 16:59:45 +02:00
DrParadox7 b738e8e073 NPE check on onChunkUnload (#51) 2026-08-21 17:39:04 +02:00
Jason Mitchell a385d29ab3 Additional thread saftey for CCL (#50) 2026-07-22 15:28:54 -07:00
Jason Mitchell 60d44f3c7b Skip MR class files (#49) 2026-06-16 10:35:36 -07:00
Ranzu 6209831d08 Revert #47 (#48) 2026-06-07 19:36:40 +00:00
Ranzu 7ae8b15810 Convert colors to ColorUtils (#47) 2026-06-07 19:17:10 +02:00
Alexdoru 08f3e4d42a intern superclass and interfaces of ClassHeirachyManager data (#46) 2026-05-24 15:19:20 +02:00
Martin Robertz 9bd4c63b00 update gradle+deps+bs 2026-03-10 19:37:26 +01:00
Ranzu 6e90633d31 Added color override helper (#43) 2026-03-10 13:59:10 +01:00
danyadevandAlexdoru f02dbc956c optimize ClassHeirachyManager (#42)
Co-authored-by: Alexdoru <57050655+Alexdoru@users.noreply.github.com>
2026-03-05 17:41:52 +00:00
boubou19 63f36ca383 Reapply "Fix ItemStack packet id limited by short" (#23) (#37) (#41) 2025-12-07 16:17:57 +00:00
Alexdoru bb008a1265 [Memory-opti:static allocations] Reduce allocations and speedup ClassHeirarchyManager (#39) 2025-12-05 22:21:23 +01:00
TacoMagickandRecursivePineapple bb98bdc6d4 Revert "Fix ItemStack packet id limited by short" (#23) (#37)
Co-authored-by: RecursivePineapple <recursive_pineapple@proton.me>
2025-11-11 17:21:20 +00:00
Alexdoru 4e5bcf829c ASM Housekeeping (#36) 2025-11-11 12:13:45 -05:00
Alexdoru 678970e48b Delete DepLoader entirely (#35) 2025-08-11 16:52:51 +02:00
AlexdoruandMartin Robertz 5ad7ebe1ac further empty DepLoader (#34)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-08-09 23:51:19 +02:00
39 changed files with 891 additions and 583 deletions

No files matched your search

+3
View File
@@ -27,3 +27,6 @@ whitelist.json
*.iws
src/main/resources/mixins.*.json
*.bat
.DS_Store
*.sh
+7
View File
@@ -6,3 +6,10 @@ minecraft {
}
}
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
+5
View File
@@ -3,3 +3,8 @@
plugins {
id 'com.gtnewhorizons.gtnhconvention'
}
minecraft {
//extraRunJvmArguments.add("-Dccc.dev.runtimePublic=true")
//extraRunJvmArguments.add("-Dccc.dev.deobfuscate=true")
}
+3
View File
@@ -1,4 +1,7 @@
// Add your dependencies here
dependencies {
testImplementation(platform('org.junit:junit-bom:5.14.1'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
}
+44 -4
View File
@@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables 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.
@@ -90,7 +114,9 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
mixinsPackage =
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -154,6 +180,11 @@ modrinthRelations =
# Leave this empty if you don't want to publish on CurseForge.
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.
# Syntax: type1:name1;type2:name2;...
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
@@ -166,6 +197,12 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -195,3 +232,6 @@ curseForgeRelations =
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
+12
View File
@@ -0,0 +1,12 @@
#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.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+5 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+12 -24
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
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 location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,30 +65,18 @@ echo. 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
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@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
:end
@rem End local scope for the variables with windows NT shell
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
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.30'
}
@@ -116,6 +116,7 @@ public class ClassDiscoverer {
while (zipEntries.hasMoreElements()) {
ZipEntry zipentry = zipEntries.nextElement();
String fullname = zipentry.getName().replace('\\', '/');
if (fullname.startsWith("META-INF/versions/")) continue;
int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) {
@@ -2,12 +2,16 @@ package codechicken.core;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.World;
import codechicken.core.internal.CCCEventHandler;
import codechicken.lib.colour.LocalizedColours;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.Side;
@@ -65,4 +69,16 @@ public class ClientUtils extends CommonUtils {
.replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:");
GuiModListScroll.register(mod);
}
@SideOnly(Side.CLIENT)
public static void registerLocalizedColourReloadListener() {
((IReloadableResourceManager) mc().getResourceManager())
.registerReloadListener(new IResourceManagerReloadListener() {
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
LocalizedColours.reloadLocalizedColours();
}
});
}
}
@@ -36,6 +36,7 @@ import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import cpw.mods.fml.client.GuiModList;
@@ -148,10 +149,11 @@ public class GuiModListScroll {
double dy = scroll % (height + 20);
GL11.glPushMatrix();
GL11.glTranslated(0, -dy, 0);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, 0xDDDDDD);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
if (needsScroll) {
GL11.glTranslated(0, height + 20, 0);
GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, 0xDDDDDD);
GuiDraw.fontRenderer
.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
}
GL11.glPopMatrix();
@@ -5,32 +5,19 @@ import java.lang.reflect.Field;
import com.google.common.collect.ImmutableBiMap;
import codechicken.lib.asm.ObfMapping;
import cpw.mods.fml.common.asm.transformers.AccessTransformer;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
// This AccessTransformer will make all minecraft classes public at runtime in MCP just as they are in modloader.
// You should ONLY use this when you are testing with a mod that relies on runtime publicity and doesn't include
// access transformers. Such mods are doing the wrong thing and should be fixed.
public class CodeChickenAccessTransformer extends AccessTransformer {
private static boolean makeAllPublic;
private static Field f_classNameBiMap;
private static final Field f_classNameBiMap;
@SuppressWarnings("FieldMayBeFinal")
private static Object emptyMap = ImmutableBiMap.of();
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;
static {
try {
f_classNameBiMap = FMLDeobfuscatingRemapper.class.getDeclaredField("classNameBiMap");
f_classNameBiMap.setAccessible(true);
@@ -39,16 +26,18 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
}
}
public CodeChickenAccessTransformer() throws IOException {}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
boolean setPublic = makeAllPublic && name.startsWith("net.minecraft.");
boolean setPublic = name.startsWith("net.minecraft.");
if (setPublic) setClassMap(name);
bytes = super.transform(name, transformedName, bytes);
if (setPublic) restoreClassMap();
return bytes;
}
private void restoreClassMap() {
private static void restoreClassMap() {
try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap);
} catch (Exception e) {
@@ -56,7 +45,7 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
}
}
private void setClassMap(String name) {
private static void setClassMap(String name) {
try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, ImmutableBiMap.of(name.replace('.', '/'), ""));
} catch (Exception e) {
@@ -1,6 +1,5 @@
package codechicken.core.asm;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
@@ -13,7 +12,6 @@ import codechicken.core.ClientUtils;
import codechicken.core.featurehack.LiquidTextures;
import codechicken.core.internal.CCCEventHandler;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.config.ConfigFile;
import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.LoadController;
@@ -26,14 +24,6 @@ import cpw.mods.fml.common.versioning.VersionRange;
public class CodeChickenCoreModContainer extends DummyModContainer {
public static ConfigFile config;
public static void loadConfig() {
if (config == null)
config = new ConfigFile(new File(CodeChickenCorePlugin.minecraftDir, "config/CodeChickenCore.cfg"))
.setComment("CodeChickenCore configuration file.");
}
public CodeChickenCoreModContainer() {
super(getModMetadata());
}
@@ -75,6 +65,7 @@ public class CodeChickenCoreModContainer extends DummyModContainer {
public void init(FMLInitializationEvent event) {
if (event.getSide().isClient()) {
ClientUtils.enhanceSupportersList("CodeChickenCore");
ClientUtils.registerLocalizedColourReloadListener();
FMLCommonHandler.instance().bus().register(new CCCEventHandler());
MinecraftForge.EVENT_BUS.register(new CCCEventHandler());
@@ -7,35 +7,50 @@ import java.util.HashSet;
import java.util.LinkedList;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ClassHeirachyManager;
import codechicken.lib.asm.ObfMapping;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DefaultImplementationTransformer implements IClassTransformer {
private static LaunchClassLoader cl = (LaunchClassLoader) ClassHeirachyManager.class.getClassLoader();
private static final HashMap<String, InterfaceImpl> impls = new HashMap<>();
private static ClassNode getClassNode(String name) {
try {
return ASMHelper.createClassNode(cl.getClassBytes(name.replace('/', '.')));
} catch (IOException e) {
throw new RuntimeException(e);
public static void registerDefaultImpl(String iname, String cname) {
if (impls.isEmpty()) {
String name = DefaultImplementationTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
}
impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname));
}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (transformedName.startsWith("net.minecraft") || impls.isEmpty()) return bytes;
ClassNode cnode = ASMHelper.createClassNode(bytes);
boolean changed = false;
for (String iname : cnode.interfaces) {
InterfaceImpl impl = impls.get(iname);
if (impl != null) changed |= impl.patch(cnode);
}
return changed ? ASMHelper.createBytes(cnode, 0) : bytes;
}
static class InterfaceImpl {
public final String iname;
public ArrayList<MethodNode> impls = new ArrayList<MethodNode>();
public ArrayList<MethodNode> impls = new ArrayList<>();
public InterfaceImpl(String iname, String cname) {
this.iname = iname;
HashSet<String> names = new HashSet<String>();
HashSet<String> names = new HashSet<>();
ClassNode inode = getClassNode(iname);
for (MethodNode method : inode.methods) names.add(method.name + method.desc);
@@ -47,7 +62,7 @@ public class DefaultImplementationTransformer implements IClassTransformer {
}
public boolean patch(ClassNode cnode) {
LinkedList<String> names = new LinkedList<String>();
LinkedList<String> names = new LinkedList<>();
for (MethodNode method : cnode.methods) {
ObfMapping m = new ObfMapping(cnode.name, method.name, method.desc).toRuntime();
names.add(m.s_name + m.s_desc);
@@ -69,25 +84,13 @@ public class DefaultImplementationTransformer implements IClassTransformer {
}
return changed;
}
}
private static HashMap<String, InterfaceImpl> impls = new HashMap<String, InterfaceImpl>();
public static void registerDefaultImpl(String iname, String cname) {
impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname));
}
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (transformedName.startsWith("net.minecraft") || impls.isEmpty()) return bytes;
ClassNode cnode = ASMHelper.createClassNode(bytes);
boolean changed = false;
for (String iname : cnode.interfaces) {
InterfaceImpl impl = impls.get(iname);
if (impl != null) changed |= impl.patch(cnode);
private static ClassNode getClassNode(String name) {
try {
return ASMHelper.createClassNode(Launch.classLoader.getClassBytes(name.replace('/', '.')));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return changed ? ASMHelper.createBytes(cnode, 0) : bytes;
}
}
@@ -1,7 +1,5 @@
package codechicken.core.asm;
import static codechicken.core.launch.CodeChickenCorePlugin.logger;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -11,7 +9,9 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.Stack;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import net.minecraft.launchwrapper.IClassTransformer;
@@ -21,14 +21,18 @@ import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import codechicken.core.launch.CodeChickenCorePlugin;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class DelegatedTransformer implements IClassTransformer {
private static ArrayList<IClassTransformer> delegatedTransformers;
private static Method m_defineClass;
private static Field f_cachedClasses;
private static final ArrayList<IClassTransformer> delegatedTransformers;
private static final Method m_defineClass;
private static final Field f_cachedClasses;
private static IClassTransformer[] transformers;
public DelegatedTransformer() {
delegatedTransformers = new ArrayList<IClassTransformer>();
static {
delegatedTransformers = new ArrayList<>();
try {
m_defineClass = ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
@@ -41,14 +45,75 @@ public class DelegatedTransformer implements IClassTransformer {
}
@Override
public byte[] transform(String name, String tname, byte[] bytes) {
if (bytes == null) return null;
for (IClassTransformer trans : delegatedTransformers) bytes = trans.transform(name, tname, bytes);
return bytes;
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
for (IClassTransformer t : transformers) {
basicClass = t.transform(name, transformedName, basicClass);
}
return basicClass;
}
public static void load() {
scanModsForDelegatedTransformers();
final int size = delegatedTransformers.size();
if (size == 0) {
CodeChickenCorePlugin.logger
.debug("No delegated transformer found, skipping registration of main DelegatedTransformer.");
return;
}
CodeChickenCorePlugin.logger
.debug("Found " + size + " delegated transformers, registering main DelegatedTransformer.");
transformers = delegatedTransformers.toArray(new IClassTransformer[0]);
String name = DelegatedTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
}
private static void scanModsForDelegatedTransformers() {
File modsDir = new File(CodeChickenCorePlugin.minecraftDir, "mods");
if (modsDir.exists()) {
final File[] files = modsDir.listFiles();
if (files != null) {
for (File file : files) {
scanMod(file);
}
}
}
File versionModsDir = new File(
CodeChickenCorePlugin.minecraftDir,
"mods/" + CodeChickenCorePlugin.currentMcVersion);
if (versionModsDir.exists()) {
final File[] files = versionModsDir.listFiles();
if (files != null) {
for (File file : files) {
scanMod(file);
}
}
}
}
private static void scanMod(File file) {
if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip")) return;
try {
try (JarFile jar = new JarFile(file)) {
Manifest manifest = jar.getManifest();
if (manifest == null) return;
Attributes attr = manifest.getMainAttributes();
if (attr == null) return;
String transformer = attr.getValue("CCTransformer");
if (transformer != null) {
addTransformer(transformer, jar, file);
}
}
} catch (Exception e) {
CodeChickenCorePlugin.logger.error("CodeChickenCore: Failed to read jar file: " + file.getName(), e);
}
}
public static void addTransformer(String transformer, JarFile jar, File jarFile) {
logger.debug("Adding CCTransformer: " + transformer);
CodeChickenCorePlugin.logger.debug("Adding CCTransformer: " + transformer);
try {
byte[] bytes;
bytes = Launch.classLoader.getClassBytes(transformer);
@@ -107,7 +172,7 @@ public class DelegatedTransformer implements IClassTransformer {
byte[] depbytes = readFully(jar.getInputStream(entry));
defineDependancies(depbytes, jar, jarFile, depStack);
logger.debug("Defining dependancy: " + dependancy);
CodeChickenCorePlugin.logger.debug("Defining dependancy: " + dependancy);
defineClass(dependancy.replace('/', '.'), depbytes);
}
@@ -128,7 +193,6 @@ public class DelegatedTransformer implements IClassTransformer {
while ((r = stream.read()) != -1) {
bos.write(r);
}
return bos.toByteArray();
}
}
@@ -24,7 +24,6 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import codechicken.lib.asm.ASMHelper;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.CC_ClassWriter;
import codechicken.lib.asm.ObfMapping;
import codechicken.obfuscator.IHeirachyEvaluator;
@@ -35,15 +34,11 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes, IHeirachyEvaluator {
static {
ASMInit.init();
}
public static class LoadPlugin implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
return new String[0];
return null;
}
@Override
@@ -118,26 +113,20 @@ public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes,
}
public static void load() {
CodeChickenCoreModContainer.loadConfig();
run = new ObfuscationRun(
false,
ObfMapping.MCPRemapper.getConfFiles(),
ObfuscationRun.fillDefaults(new HashMap<String, String>()));
run.obf.setHeirachyEvaluator(instance);
run.setQuiet().parseMappings();
Collections.addAll(excludedPackages, run.config.get("excludedPackages").split(";"));
if (CodeChickenCoreModContainer.config.getTag("dev.deobfuscate")
.setComment("set to true to completely deobfuscate mcp names")
.getBooleanValue(!ObfMapping.obfuscated)) {
run = new ObfuscationRun(
false,
ObfMapping.MCPRemapper.getConfFiles(),
ObfuscationRun.fillDefaults(new HashMap<String, String>()));
run.obf.setHeirachyEvaluator(instance);
run.setQuiet().parseMappings();
Collections.addAll(excludedPackages, run.config.get("excludedPackages").split(";"));
if (ObfMapping.obfuscated) {
ObfMapping.loadMCPRemapper();
run.setSeargeConstants();
getTransformers().add(instance);
} else {
getTransformers().add(0, instance); // insert transformer as first.
}
if (ObfMapping.obfuscated) {
ObfMapping.loadMCPRemapper();
run.setSeargeConstants();
getTransformers().add(instance);
} else {
getTransformers().add(0, instance); // insert transformer as first.
}
}
@@ -5,14 +5,15 @@ import static codechicken.lib.asm.InsnComparator.findOnce;
import java.util.Map;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.asm.ASMBlock;
import codechicken.lib.asm.ASMInit;
import codechicken.lib.asm.ASMReader;
import codechicken.lib.asm.ModularASMTransformer;
import codechicken.lib.asm.ModularASMTransformer.MethodReplacer;
@@ -20,20 +21,29 @@ import codechicken.lib.asm.ModularASMTransformer.MethodTransformer;
import codechicken.lib.asm.ModularASMTransformer.MethodWriter;
import codechicken.lib.asm.ObfMapping;
import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public class TweakTransformer implements IClassTransformer, Opcodes {
static {
ASMInit.init();
}
private static ModularASMTransformer transformer = new ModularASMTransformer();
private static Map<String, ASMBlock> blocks = ASMReader.loadResource("/assets/codechickencore/asm/tweaks.asm");
private static final ModularASMTransformer transformer = new ModularASMTransformer();
private static final Map<String, ASMBlock> blocks = ASMReader
.loadResource("/assets/codechickencore/asm/tweaks.asm");
public static ConfigTag tweaks;
public static void load() {
CodeChickenCoreModContainer.loadConfig();
tweaks = CodeChickenCoreModContainer.config.getTag("tweaks")
loadTransformersFromConfig();
if (transformer.isEmpty()) {
CodeChickenCorePlugin.logger
.debug("No tweaks parsed from config, skipping registration of TweakTransformer.");
return;
}
String name = TweakTransformer.class.getName();
FMLRelaunchLog.finer("Registering transformer %s", name);
Launch.classLoader.registerTransformer(name);
}
private static void loadTransformersFromConfig() {
tweaks = CodeChickenCorePlugin.config.getTag("tweaks")
.setComment("Various tweaks that can be applied to game mechanics.").useBraces();
tweaks.removeTag("persistantLava");
@@ -103,7 +113,7 @@ public class TweakTransformer implements IClassTransformer, Opcodes {
}
@Override
public byte[] transform(String name, String tname, byte[] bytes) {
public final byte[] transform(String name, String tname, byte[] bytes) {
return transformer.transform(name, bytes);
}
}
@@ -6,6 +6,8 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCButton extends GuiWidget {
public String text;
@@ -73,7 +75,8 @@ public class GuiCCButton extends GuiWidget {
}
public int getTextColour(int mousex, int mousey) {
return !isEnabled ? 0xFFA0A0A0 : pointInside(mousex, mousey) ? 0xFFFFFFA0 : 0xFFE0E0E0;
return !isEnabled ? LocalizedColours.BUTTON_TEXT_DISABLED
: pointInside(mousex, mousey) ? LocalizedColours.BUTTON_TEXT_HOVER : LocalizedColours.BUTTON_TEXT;
}
public GuiCCButton setActionCommand(String string) {
@@ -5,6 +5,8 @@ import net.minecraft.util.ChatAllowedCharacters;
import org.lwjgl.input.Keyboard;
import codechicken.lib.colour.LocalizedColours;
public class GuiCCTextField extends GuiWidget {
private String text;
@@ -120,8 +122,8 @@ public class GuiCCTextField extends GuiWidget {
}
public void drawBackground() {
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0);
drawRect(x, y, x + width, y + height, 0xff000000);
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, LocalizedColours.TEXT_FIELD_BORDER);
drawRect(x, y, x + width, y + height, LocalizedColours.TEXT_FIELD_BACKGROUND);
}
public String getDrawText() {
@@ -135,7 +137,7 @@ public class GuiCCTextField extends GuiWidget {
}
public int getTextColour() {
return isEnabled ? 0xe0e0e0 : 0x707070;
return isEnabled ? LocalizedColours.TEXT_FIELD_TEXT : LocalizedColours.TEXT_FIELD_TEXT_DISABLED;
}
public GuiCCTextField setMaxStringLength(int i) {
@@ -3,6 +3,7 @@ package codechicken.core.gui;
import java.awt.Dimension;
import java.awt.Rectangle;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.Rectangle4i;
@@ -184,28 +185,29 @@ public abstract class GuiScrollPane extends GuiWidget {
}
public void drawBackground(float frame) {
drawRect(x, y, x + width, y + height, 0xff000000);
drawRect(x, y, x + width, y + height, LocalizedColours.SCROLL_PANE_BACKGROUND);
}
public abstract void drawContent(int mx, int my, float frame);
public void drawOverlay(float frame) {
// outlines
drawRect(x, y - 1, x + width, y, 0xffa0a0a0); // top
drawRect(x, y + height, x + width, y + height + 1, 0xffa0a0a0); // bottom
drawRect(x - 1, y - 1, x, y + height + 1, 0xffa0a0a0); // left
drawRect(x + width, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); // right
drawRect(x, y - 1, x + width, y, LocalizedColours.SCROLL_PANE_OVERLAY_TOP);
drawRect(x, y + height, x + width, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_BOTTOM);
drawRect(x - 1, y - 1, x, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_LEFT);
drawRect(x + width, y - 1, x + width + 1, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_RIGHT);
}
public void drawScrollbar(float frame) {
Rectangle r = scrollbarBounds();
drawRect(r.x, r.y, r.x + r.width, r.y + r.height, 0xFF8B8B8B); // corners
drawRect(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1, 0xFFF0F0F0); // topleft up
drawRect(r.x + 1, r.y + 1, r.x + r.width, r.y + r.height, 0xFF555555); // bottom right down
drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, 0xFFC6C6C6); // scrollbar
drawRect(r.x, r.y, r.x + r.width, r.y + r.height, LocalizedColours.SCROLLBAR_CORNERS);
drawRect(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_TOP_LEFT);
drawRect(r.x + 1, r.y + 1, r.x + r.width, r.y + r.height, LocalizedColours.SCROLLBAR_BOTTOM_RIGHT);
drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_FILL);
int algn = scrollbarGuideAlignment();
if (algn != 0) drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, 0xFF808080); // lineguide
if (algn != 0)
drawRect(algn > 0 ? r.x + r.width : r.x - 1, y, r.x, y + height, LocalizedColours.SCROLLBAR_GUIDE);
}
}
@@ -9,9 +9,6 @@ import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
@@ -21,48 +18,87 @@ import javax.swing.event.HyperlinkListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import codechicken.core.asm.CodeChickenCoreModContainer;
import codechicken.core.asm.DelegatedTransformer;
import codechicken.core.asm.MCPDeobfuscationTransformer;
import codechicken.core.asm.Tags;
import codechicken.core.asm.TweakTransformer;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.ConfigTag;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser;
import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions;
@TransformerExclusions(value = { "codechicken.core.asm", "codechicken.obfuscator" })
@MCVersion("1.7.10")
public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
@IFMLLoadingPlugin.MCVersion("1.7.10")
@IFMLLoadingPlugin.TransformerExclusions({ "codechicken.core.asm", "codechicken.obfuscator", "codechicken.lib.asm",
"codechicken.lib.config" })
public class CodeChickenCorePlugin implements IFMLLoadingPlugin {
public static final String mcVersion = "[1.7.10]";
@Deprecated
public static final String version = Tags.VERSION;
public static ConfigFile config;
public static File minecraftDir;
public static String currentMcVersion;
public static Logger logger = LogManager.getLogger("CodeChickenCore");
public CodeChickenCorePlugin() {
if (minecraftDir != null) return; // get called twice, once for IFMLCallHook
if (minecraftDir != null) return;
minecraftDir = (File) FMLInjectionData.data()[6];
currentMcVersion = (String) FMLInjectionData.data()[4];
config = new ConfigFile(new File(minecraftDir, "config/CodeChickenCore.cfg"))
.setComment("CodeChickenCore configuration file.");
if (Boolean.getBoolean("ccc.dev.deobfuscate")) {
injectDeobfPlugin();
}
}
injectDeobfPlugin();
@Override
public String[] getASMTransformerClass() {
versionCheck(mcVersion, "CodeChickenCore");
return new String[] { "codechicken.lib.asm.ClassHeirachyManager", "codechicken.lib.asm.RedirectorTransformer" };
}
@Override
public String getAccessTransformerClass() {
if (Boolean.getBoolean("ccc.dev.runtimePublic")) {
return "codechicken.core.asm.CodeChickenAccessTransformer";
} else {
return null;
}
}
@Override
public String getModContainerClass() {
return "codechicken.core.asm.CodeChickenCoreModContainer";
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
ConfigTag checkRAM = CodeChickenCorePlugin.config.getTag("checks")
.setComment("Configuration options for checking various requirements for a modpack.").useBraces();
if (checkRAM.getTag("checkRAM")
.setComment("If set to true, check RAM available for Minecraft before continuing to load")
.getBooleanValue(false)) {
systemCheck(checkRAM);
}
TweakTransformer.load();
DelegatedTransformer.load();
}
private void injectDeobfPlugin() {
try {
Class<?> wrapperClass = Class.forName("cpw.mods.fml.relauncher.CoreModManager$FMLPluginWrapper");
Constructor wrapperConstructor = wrapperClass
Constructor<?> wrapperConstructor = wrapperClass
.getConstructor(String.class, IFMLLoadingPlugin.class, File.class, Integer.TYPE, String[].class);
Field f_loadPlugins = CoreModManager.class.getDeclaredField("loadPlugins");
wrapperConstructor.setAccessible(true);
@@ -76,7 +112,7 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
0,
new String[0]));
} catch (Exception e) {
logger.error("Failed to inject MCPDeobfuscation Transformer", e);
logger.error("Failed to inject FMLPluginWrapper for MCPDeobfuscation Transformer", e);
}
}
@@ -186,74 +222,4 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
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);
}
}
}
@@ -1,186 +0,0 @@
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,14 +1,13 @@
package codechicken.lib.asm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
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.ClassReader;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
@@ -18,47 +17,60 @@ import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
*/
public class ClassHeirachyManager implements IClassTransformer {
private static final Map<String, ClassInfo> superclasses = new ConcurrentHashMap<>(1000);
static {
ASMInit.init();
superclasses.put("java.lang.Object", ClassInfo.OBJECT);
}
public static class SuperCache {
String superclass;
public HashSet<String> parents = new HashSet<String>();
private boolean flattened;
public void add(String parent) {
parents.add(parent);
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) return null;
if (!superclasses.containsKey(transformedName)) {
declareASM(transformedName, bytes);
}
return bytes;
}
public void flatten() {
if (flattened) return;
private static ClassInfo declareASM(String name, byte[] bytes) {
final ClassReader classReader = new ClassReader(bytes);
final ClassInfo classInfo = new ClassInfo(classReader);
superclasses.put(name, classInfo);
return classInfo;
}
for (String s : new ArrayList<String>(parents)) {
SuperCache c = declareClass(s);
if (c != null) {
c.flatten();
parents.addAll(c.parents);
}
private static ClassInfo declareReflection(String name) throws ClassNotFoundException {
final Class<?> aclass = Class.forName(name);
final ClassInfo classInfo = new ClassInfo(aclass);
superclasses.put(name, classInfo);
return classInfo;
}
private static ClassInfo declareClass(String name) {
try {
byte[] bytes = Launch.classLoader.getClassBytes(unKey(name));
if (bytes != null) {
return declareASM(name, bytes);
}
flattened = true;
} catch (Exception ignored) {}
try {
return declareReflection(name);
} catch (ClassNotFoundException ignored) {}
return null;
}
private static String toKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
public static HashMap<String, SuperCache> superclasses = new HashMap<String, SuperCache>();
private static LaunchClassLoader cl = Launch.classLoader;
public static String toKey(String name) {
if (ObfMapping.obfuscated)
name = FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
return name;
}
public static String unKey(String name) {
if (ObfMapping.obfuscated)
name = FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
return name;
private static String unKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
/**
@@ -72,83 +84,99 @@ public class ClassHeirachyManager implements IClassTransformer {
if (name.equals(superclass)) return true;
SuperCache cache = declareClass(name);
if (cache == null) // just can't handle this
return false;
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null) return false;
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;
return classInfo.hasSuper(superclass);
}
public static String getSuperClass(String name, boolean runtime) {
name = toKey(name);
SuperCache cache = declareClass(name);
if (cache == null) return "java.lang.Object";
cache.flatten();
String s = cache.superclass;
if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null || classInfo.superclass == null) return "java.lang.Object";
final String s = classInfo.superclass;
if (!runtime) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
}
return s;
}
private static class ClassInfo {
public static final ClassInfo OBJECT = new ClassInfo();
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,17 +275,22 @@ public class ModularASMTransformer {
}
}
public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<String, ClassNodeTransformerList>();
public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<>();
public void add(ClassNodeTransformer t) {
ClassNodeTransformerList list = transformers.get(t.className());
if (list == null) transformers.put(t.className(), list = new ClassNodeTransformerList());
if (list == null) {
transformers.put(t.className(), list = new ClassNodeTransformerList());
}
list.add(t);
}
public boolean isEmpty() {
return transformers.isEmpty();
}
public byte[] transform(String name, byte[] bytes) {
if (bytes == null) return null;
ClassNodeTransformerList list = transformers.get(name);
return list == null ? bytes : list.transform(bytes);
}
@@ -0,0 +1,86 @@
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;
}
}
}
+20 -3
View File
@@ -18,6 +18,7 @@ import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.colour.LocalizedColours;
import codechicken.lib.math.MathHelper;
import codechicken.lib.render.CCRenderState;
@@ -162,7 +163,15 @@ public class GuiDraw {
}
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list) {
drawMultilineTip(font, x, y, list, 0xf0100010, 0xf0100010, 0x505000ff, 0x5028007F);
drawMultilineTip(
font,
x,
y,
list,
LocalizedColours.TOOLTIP_BG_START,
LocalizedColours.TOOLTIP_BG_END,
LocalizedColours.TOOLTIP_BORDER_START,
LocalizedColours.TOOLTIP_BORDER_END);
}
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list, int bgStart, int bgEnd,
@@ -201,7 +210,7 @@ public class GuiDraw {
line.draw(x, y);
y += line.getSize().height;
} else {
font.drawStringWithShadow(s, x, y, -1);
font.drawStringWithShadow(s, x, y, LocalizedColours.TOOLTIP_TEXT);
y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10;
}
}
@@ -215,7 +224,15 @@ public class GuiDraw {
}
public static void drawTooltipBox(int x, int y, int w, int h) {
drawTooltipBox(x, y, w, h, 0xf0100010, 0xf0100010, 0x505000ff, 0x5028007F);
drawTooltipBox(
x,
y,
w,
h,
LocalizedColours.TOOLTIP_BG_START,
LocalizedColours.TOOLTIP_BG_END,
LocalizedColours.TOOLTIP_BORDER_START,
LocalizedColours.TOOLTIP_BORDER_END);
}
public static void drawTooltipBox(int x, int y, int w, int h, int bgStart, int bgEnd, int borderStart,
@@ -12,7 +12,12 @@ import codechicken.lib.vec.BlockCoord;
public class SimpleBrightnessModel implements CCRenderState.IVertexOperation {
public static final int operationIndex = CCRenderState.registerOperation();
public static SimpleBrightnessModel instance = new SimpleBrightnessModel();
private static final ThreadLocal<SimpleBrightnessModel> instances = ThreadLocal
.withInitial(SimpleBrightnessModel::new);
public static SimpleBrightnessModel instance() {
return instances.get();
}
public IBlockAccess access;
public BlockCoord pos = new BlockCoord();
@@ -466,7 +466,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
public FluidStack readFluidStack() {
Fluid fluid = FluidRegistry.getFluid(readShort());
if (fluid == null) fluid = FluidRegistry.WATER;
if (fluid == null) return null;
return new FluidStack(fluid, readVarInt(), readNBTTagCompound());
}
@@ -1,6 +1,7 @@
package codechicken.lib.render;
import java.util.ArrayList;
import java.util.HashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
@@ -38,9 +39,9 @@ public class CCRenderState {
return instances.get();
}
private static int nextOperationIndex;
private static volatile int nextOperationIndex;
public static int registerOperation() {
public static synchronized int registerOperation() {
return nextOperationIndex++;
}
@@ -87,15 +88,52 @@ public class CCRenderState {
int operationID();
}
private static ArrayList<VertexAttribute<?>> vertexAttributes = new ArrayList<>();
public static final class AttributeKey<T> {
private static int registerVertexAttribute(VertexAttribute<?> attr) {
vertexAttributes.add(attr);
return vertexAttributes.size() - 1;
private static final HashMap<String, AttributeKey<?>> nameMap = new HashMap<>();
private static final ArrayList<AttributeKey<?>> attributeKeys = new ArrayList<>();
private static int nextLegacyId;
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) {
return vertexAttributes.get(index);
return AttributeKey.get(index).canonical;
}
/**
@@ -106,14 +144,26 @@ public class CCRenderState {
*/
public abstract static class VertexAttribute<T> implements IVertexOperation {
public final int attributeIndex = registerVertexAttribute(this);
private final int operationIndex = registerOperation();
public final int attributeIndex;
private final int operationIndex;
/**
* Set to true when the attrute is part of the pipeline. Should only be managed by CCRenderState when
* constructing the pipeline
*/
public boolean active = false;
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
*/
@@ -189,7 +239,7 @@ public class CCRenderState {
return instances.get().lightingAttrib;
}
public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>() {
public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>(NORMAL_KEY) {
private Vector3[] normalRef;
@@ -217,7 +267,7 @@ public class CCRenderState {
else state.setNormalInstance(Rotation.axes[state.side]);
}
};
public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>() {
public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>(COLOUR_KEY) {
private int[] colourRef;
@@ -239,7 +289,7 @@ public class CCRenderState {
else state.setColourInstance(state.baseColour);
}
};
public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>() {
public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>(LIGHTING_KEY) {
private int[] colourRef;
@@ -265,7 +315,7 @@ public class CCRenderState {
state.setColourInstance(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex]));
}
};
public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>() {
public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>(SIDE_KEY) {
private int[] sideRef;
@@ -292,7 +342,7 @@ public class CCRenderState {
/**
* Uses the position of the lightmatrix to compute LC if not provided
*/
public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>() {
public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>(LC_KEY) {
private LC[] lcRef;
private final Vector3 vec = new Vector3(); // for computation
@@ -1,5 +1,7 @@
package codechicken.lib.render;
import java.util.Arrays;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
@@ -11,6 +13,9 @@ import codechicken.lib.vec.Vector3;
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) {
super(world, x, y, z, dx, dy, dz);
particleIcon = icon;
@@ -19,6 +24,18 @@ public class EntityDigIconFX extends EntityFX {
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
public int getFXLayer() {
return 1;
@@ -90,8 +107,24 @@ public class EntityDigIconFX extends EntityFX {
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,
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;
Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2 * border);
diff.x *= world.rand.nextDouble();
@@ -106,13 +139,34 @@ public class EntityDigIconFX extends EntityFX {
if (side == 4) diff.x = bounds.min.x - border;
if (side == 5) diff.x = bounds.max.x + border;
effectRenderer.addEffect(
new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon).multiplyVelocity(0.2F)
.multipleParticleScaleBy(0.6F));
EntityDigIconFX fx = new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon);
effectRenderer.addEffect(fx.applyColourMultiplier(colour).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,
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 center = bounds.min.copy().add(bounds.max).multiply(0.5);
Vector3 density = diff.copy().multiply(4);
@@ -121,19 +175,20 @@ public class EntityDigIconFX extends EntityFX {
density.z = Math.ceil(density.z);
for (int i = 0; i < density.x; ++i) for (int j = 0; j < density.y; ++j) for (int k = 0; k < density.z; ++k) {
double x = bounds.min.x + (i + 0.5) * diff.x / density.x;
double y = bounds.min.y + (j + 0.5) * diff.y / density.y;
double z = bounds.min.z + (k + 0.5) * diff.z / density.z;
effectRenderer.addEffect(
new EntityDigIconFX(
world,
x,
y,
z,
x - center.x,
y - center.y,
z - center.z,
icons[world.rand.nextInt(icons.length)]));
double px = bounds.min.x + (i + 0.5) * diff.x / density.x;
double py = bounds.min.y + (j + 0.5) * diff.y / density.y;
double pz = bounds.min.z + (k + 0.5) * diff.z / density.z;
int iconSide = world.rand.nextInt(icons.length);
EntityDigIconFX fx = new EntityDigIconFX(
world,
px,
py,
pz,
px - center.x,
py - center.y,
pz - center.z,
icons[iconSide]);
effectRenderer.addEffect(fx.applyColourMultiplier(colours[iconSide]));
}
}
}
@@ -6,6 +6,8 @@ import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.colour.LocalizedColours;
public class FontUtils {
public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
@@ -53,7 +55,7 @@ public class FontUtils {
GL11.glPushMatrix();
GL11.glTranslated(x + 16 - swidth, y + 16 - sheight, 0);
GL11.glScaled(scale, scale, 1);
fontRenderer.drawStringWithShadow(quantity, 0, 0, 0xFFFFFF);
fontRenderer.drawStringWithShadow(quantity, 0, 0, LocalizedColours.ITEM_QUANTITY_TEXT);
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
@@ -55,7 +55,10 @@ public class WorldExtensionManager {
public void onChunkUnLoad(ChunkEvent.Unload event) {
if (event.getChunk() instanceof EmptyChunk) return;
for (WorldExtension extension : worldMap.get(event.world)) extension.unloadChunk(event.getChunk());
WorldExtension[] extensions = worldMap.get(event.world);
if (extensions == null) return;
for (WorldExtension extension : extensions) extension.unloadChunk(event.getChunk());
if (event.world.isRemote) removeChunk(event.world, event.getChunk());
}
@@ -1 +1,26 @@
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
@@ -0,0 +1 @@
codechickencore.update=Доступна версія %s для %s
@@ -0,0 +1,102 @@
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));
}
}
@@ -0,0 +1,20 @@
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());
}
}