Compare commits

...
41 Commits
Author SHA1 Message Date
Worive fd7d5c269f Bound compressed packet decoding (#58) 2026-11-13 18:20:48 +01:00
UltraProdigy 89dac36ebc ci: run build and test on release branches 2026-11-09 02:19:02 -05:00
Nikolay S. 18bac36c10 Deprecate index-based field selection (#52) 2026-11-01 14:15:34 +01:00
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
2025-08-09 23:51:19 +02:00
Dream Master 033009fed5 update 2025-08-08 18:58:12 +07:00
Alexdoru acd863e748 Speedup ClassDiscoverer by using ClassReader.SKIP_CODE (#33) 2025-08-07 22:10:04 +02:00
Martin Robertz 850e2b9368 update 2025-05-29 22:09:14 +02:00
FalsePattern 4713f7aebd modern java compat (#31) 2025-05-29 19:26:51 +00:00
Jason Mitchell b999279656 More thread safe, again. (#30) 2025-05-04 08:08:02 +09:00
mts2200 1879ba2071 Improve NPE checks (#26) 2025-01-08 18:51:06 +00:00
Martin Robertz 9d600169ac update 2024-12-22 01:38:26 +01:00
charagarlnad 4e7eb37f3f use ZipFile instead of ZipInputStream in ClassDiscoverer (#25) 2024-12-20 15:18:04 +01:00
glowredman 22dfc1360e Allow colorful Tooltips (#24) 2024-11-13 06:47:10 +00:00
Maya 7bf5935f69 add issue templates 2024-11-11 04:15:39 -06:00
Alexdoru 162de447d5 Change ASM dump folder (#21)
* minor changes to RedirectorTransformer and class dumping

* don't print the stacktrace when failing to write class
2024-10-06 11:49:00 +00:00
Martin Robertz 39ed6607a6 update 2024-10-06 13:35:38 +02:00
Alexdoru 99b815c12e Reduce RAM allocations during rendering (#22)
* edit doc for fluid rendering method + clamp fillRate of the tank between 0 and 1

* init all static fields in the static block

* remove object instantiation that is never used in the following method and assign local variables

* reduce object allocation spam in CCModelLibrary.getRenderMatrix

* fix error in local V value

* remove all object instantiation during rendering to minimize RAM allocation in RenderUtils
2024-10-06 13:33:56 +02:00
Martin Robertz 0f37937ada update 2024-09-01 22:46:57 +02:00
Alexdoru c9d29a5c8e fix logic of the ClassConstantPoolParser (#20) 2024-09-01 22:19:33 +02:00
tth05 ea4f68d163 Add map to provide fast access to all config tags in sub-tree (#18) 2024-07-18 21:37:30 +07:00
Dream Master 549f769747 update 2024-07-17 16:47:04 +07:00
Caedis cba2e0f642 Fix casing 2024-07-04 00:30:22 -07:00
Jason Mitchell 3740253590 Maybe more backwards compatible (#16)
* Maybe more backwards compatible
2024-07-03 11:21:27 -07:00
63 changed files with 1947 additions and 1060 deletions

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
name: Report Bug
description: "Report a bug."
body:
- type: markdown
attributes:
value: "A bug/crash report with sufficient information and logs to reproduce and track down."
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Fake#1234"
- type: input
id: version
attributes:
label: Mod Version
description: "What version of the Mod are you using?"
placeholder: "Example: 1.0.0"
validations:
required: true
- type: textarea
id: report
attributes:
label: Bug Report
description: "Relevant information, as well as relevant logs attached such as `logs/fml-client-latest.log."
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: dropdown
id: java
attributes:
label: Java Version
description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version.
options:
- Java 8
- Java 9
- Java 11
- Java 17
- Java 19
- Java 20
- Java 21
- Other (Please Specify)
validations:
required: true
- type: textarea
id: modlist
attributes:
label: Mod List or GTNH Pack Version
description: "List of mods, ideally a minimal reproducible set (can be retrieved from latest.log). If using GTNH please indicate pack version and any changed mods, not the entire modlist."
placeholder: "List of mods or GTNH Pack version goes here"
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
required: true
@@ -0,0 +1,28 @@
name: Feature Request
description: "Request a feature."
body:
- type: markdown
attributes:
value: "Please use this form to request features. We ask you check if it was already requested though"
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Wumpus#1234"
- type: textarea
id: request
attributes:
label: Feature Request
description: "Relevant information, as well as relevant images"
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
required: true
+2 -2
View File
@@ -3,9 +3,9 @@ name: Build and test
on: on:
pull_request: pull_request:
branches: [ master, main ] branches: [ master, main, release/** ]
push: push:
branches: [ master, main ] branches: [ master, main, release/** ]
jobs: jobs:
build-and-test: build-and-test:
+3
View File
@@ -27,3 +27,6 @@ whitelist.json
*.iws *.iws
src/main/resources/mixins.*.json src/main/resources/mixins.*.json
*.bat *.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 { 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,4 +1,7 @@
// 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')
} }
+61 -7
View File
@@ -14,7 +14,7 @@ modId = CodeChickenCore
modGroup = codechicken modGroup = codechicken
# Whether to use modGroup as the maven publishing group. # Whether to use modGroup as the maven publishing group.
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods. # When false, com.github.GTNewHorizons is used.
useModGroupForPublishing = false useModGroupForPublishing = false
# Updates your build.gradle and settings.gradle automatically whenever an update is available. # Updates your build.gradle and settings.gradle automatically whenever an update is available.
@@ -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. # `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer developmentEnvironmentUserName = Developer
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8. # Enables modern Java syntax support. Valid values:
# See https://github.com/bsideup/jabel for details on how this works. # - false: No modern syntax, Java 8 only
enableModernJavaSyntax = true # - 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. # 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.
@@ -55,6 +79,9 @@ generateGradleTokenClass = codechicken.core.asm.Tags
# Name of the token containing the project's current version to generate/replace. # Name of the token containing the project's current version to generate/replace.
gradleTokenVersion = VERSION gradleTokenVersion = VERSION
# [DEPRECATED] Mod Group replacement token.
gradleTokenGroupName =
# [DEPRECATED] # [DEPRECATED]
# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java # Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
# public static final String VERSION = "GRADLETOKEN_VERSION"; # public static final String VERSION = "GRADLETOKEN_VERSION";
@@ -76,13 +103,20 @@ accessTransformersFile =
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
usesMixins = false usesMixins = false
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
# Mixin classes will have access to "main" classes, but not the other way around.
separateMixinSourceSet =
# Adds some debug arguments like verbose output and class export. # Adds some debug arguments like verbose output and class export.
usesMixinDebug = false 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. 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 = 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!
@@ -108,9 +142,15 @@ minimizeShadowedDependencies = true
# If disabled, won't rename the shadowed classes. # If disabled, won't rename the shadowed classes.
relocateShadowedDependencies = true relocateShadowedDependencies = true
# Adds the GTNH maven, CurseMaven, Modrinth, and some more well-known 1.7.10 repositories. # Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
includeWellKnownRepositories = true includeWellKnownRepositories = true
# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
# list of strings, with the acceptable keys being(case does not matter):
# cursemaven
# modrinth
excludeWellKnownRepositories =
# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven. # Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables. # Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle. # If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
@@ -131,7 +171,7 @@ modrinthProjectId = codechickencore-unofficial
# type can be one of [project, version], # type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod. # and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech # Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true # Note: UniMixins is automatically set as a required dependency if usesMixins = true.
modrinthRelations = modrinthRelations =
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens. # Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
@@ -140,6 +180,11 @@ 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],
@@ -152,6 +197,12 @@ 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.
@@ -181,3 +232,6 @@ 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
@@ -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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-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
+9 -10
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 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.
@@ -15,10 +15,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
# Gradle start up script for POSIX generated by Gradle. # gradlew start up script for POSIX generated by Gradle.
# #
# Important for running: # Important for running:
# #
@@ -27,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 Gradle # ksh gradlew
# #
# 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:
@@ -55,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/HEAD/subprojects/plugins/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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -112,7 +114,6 @@ 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.
@@ -170,7 +171,6 @@ 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" )
@@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.
Vendored
+14 -24
View File
@@ -13,16 +13,18 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem gradlew startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables, and ensure extensions are enabled
if "%OS%"=="Windows_NT" setlocal setlocal EnableExtensions
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@@ -49,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
goto fail "%COMSPEC%" /c exit 1
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
@@ -63,30 +65,18 @@ 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
goto fail "%COMSPEC%" /c exit 1
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute gradlew
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* @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 :exitWithErrorLevel
@rem End local scope for the variables with windows NT shell @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
if %ERRORLEVEL% equ 0 goto mainEnd "%COMSPEC%" /c exit %ERRORLEVEL%
: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
View File
@@ -1,4 +1,5 @@
// Add any additional repositiroes for your dependencies here // Add any additional repositiroes for your dependencies here
repositories { repositories {
mavenLocal()
} }
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
} }
plugins { plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.23' id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.30'
} }
@@ -1,16 +1,17 @@
package codechicken.core; package codechicken.core;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipFile;
import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.Launch;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -22,6 +23,7 @@ import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModClassLoader; import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.relauncher.CoreModManager; import cpw.mods.fml.relauncher.CoreModManager;
@SuppressWarnings("unused")
public class ClassDiscoverer { public class ClassDiscoverer {
public IStringMatcher matcher; public IStringMatcher matcher;
@@ -35,17 +37,12 @@ public class ClassDiscoverer {
for (int i = 0; i < superclasses.length; i++) for (int i = 0; i < superclasses.length; i++)
this.superclasses[i] = superclasses[i].getName().replace('.', '/'); this.superclasses[i] = superclasses[i].getName().replace('.', '/');
classes = new ArrayList<Class<?>>(); classes = new ArrayList<>();
modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader(); modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader();
} }
public ClassDiscoverer(Class<?>... superclasses) { public ClassDiscoverer(Class<?>... superclasses) {
this(new IStringMatcher() { this(test -> true, superclasses);
public boolean matches(String test) {
return true;
}
}, superclasses);
} }
public ArrayList<Class<?>> findClasses() { public ArrayList<Class<?>> findClasses() {
@@ -63,7 +60,7 @@ public class ClassDiscoverer {
byte[] bytes = Launch.classLoader.getClassBytes(classname); byte[] bytes = Launch.classLoader.getClassBytes(classname);
if (bytes == null) return; if (bytes == null) return;
ClassNode cnode = ASMHelper.createClassNode(bytes); ClassNode cnode = ASMHelper.createClassNode(bytes, ClassReader.SKIP_CODE);
for (String superclass : superclasses) for (String superclass : superclasses)
if (!cnode.interfaces.contains(superclass) && !cnode.superName.equals(superclass)) return; if (!cnode.interfaces.contains(superclass) && !cnode.superName.equals(superclass)) return;
@@ -86,7 +83,7 @@ public class ClassDiscoverer {
List<String> knownLibraries = ImmutableList.<String>builder().addAll(modClassLoader.getDefaultLibraries()) List<String> knownLibraries = ImmutableList.<String>builder().addAll(modClassLoader.getDefaultLibraries())
.addAll(CoreModManager.getLoadedCoremods()).build(); .addAll(CoreModManager.getLoadedCoremods()).build();
File[] minecraftSources = modClassLoader.getParentSources(); File[] minecraftSources = modClassLoader.getParentSources();
HashSet<String> searchedSources = new HashSet<String>(); HashSet<String> searchedSources = new HashSet<>();
for (File minecraftSource : minecraftSources) { for (File minecraftSource : minecraftSources) {
if (searchedSources.contains(minecraftSource.getAbsolutePath())) continue; if (searchedSources.contains(minecraftSource.getAbsolutePath())) continue;
searchedSources.add(minecraftSource.getAbsolutePath()); searchedSources.add(minecraftSource.getAbsolutePath());
@@ -114,21 +111,19 @@ public class ClassDiscoverer {
} }
private void readFromZipFile(File file) throws IOException { private void readFromZipFile(File file) throws IOException {
FileInputStream fileinputstream = new FileInputStream(file); try (ZipFile zipFile = new ZipFile(file)) {
ZipInputStream zipinputstream = new ZipInputStream(fileinputstream); Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
do { while (zipEntries.hasMoreElements()) {
ZipEntry zipentry = zipinputstream.getNextEntry(); ZipEntry zipentry = zipEntries.nextElement();
if (zipentry == null) { String fullname = zipentry.getName().replace('\\', '/');
break; 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)) {
checkAddClass(fullname);
}
} }
String fullname = zipentry.getName().replace('\\', '/'); }
int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) {
checkAddClass(fullname);
}
} while (true);
fileinputstream.close();
} }
private void readFromDirectory(File directory, File basedirectory) { private void readFromDirectory(File directory, File basedirectory) {
@@ -2,12 +2,16 @@ 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;
@@ -65,4 +69,16 @@ 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,6 +36,7 @@ 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;
@@ -148,10 +149,11 @@ 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, 0xDDDDDD); GuiDraw.fontRenderer.drawSplitString(description, x1, y1draw, x2 - x1, LocalizedColours.MOD_DESCRIPTION_TEXT);
if (needsScroll) { if (needsScroll) {
GL11.glTranslated(0, height + 20, 0); 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(); GL11.glPopMatrix();
@@ -89,6 +89,22 @@ 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];
@@ -179,6 +195,21 @@ 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,32 +5,19 @@ 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 boolean makeAllPublic; private static final Field f_classNameBiMap;
private static Field f_classNameBiMap; @SuppressWarnings("FieldMayBeFinal")
private static Object emptyMap = ImmutableBiMap.of(); private static Object emptyMap = ImmutableBiMap.of();
public CodeChickenAccessTransformer() throws IOException { static {
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);
@@ -39,16 +26,18 @@ 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 = makeAllPublic && name.startsWith("net.minecraft."); boolean setPublic = 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 void restoreClassMap() { private static void restoreClassMap() {
try { try {
f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap); f_classNameBiMap.set(FMLDeobfuscatingRemapper.INSTANCE, emptyMap);
} catch (Exception e) { } catch (Exception e) {
@@ -56,7 +45,7 @@ public class CodeChickenAccessTransformer extends AccessTransformer {
} }
} }
private void setClassMap(String name) { private static 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,6 +1,5 @@
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;
@@ -13,7 +12,6 @@ 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;
@@ -26,14 +24,6 @@ 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());
} }
@@ -75,6 +65,7 @@ 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,35 +7,50 @@ 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.LaunchClassLoader; import net.minecraft.launchwrapper.Launch;
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 LaunchClassLoader cl = (LaunchClassLoader) ClassHeirachyManager.class.getClassLoader(); private static final HashMap<String, InterfaceImpl> impls = new HashMap<>();
private static ClassNode getClassNode(String name) { public static void registerDefaultImpl(String iname, String cname) {
try { if (impls.isEmpty()) {
return ASMHelper.createClassNode(cl.getClassBytes(name.replace('/', '.'))); String name = DefaultImplementationTransformer.class.getName();
} catch (IOException e) { FMLRelaunchLog.finer("Registering transformer %s", name);
throw new RuntimeException(e); 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 { static class InterfaceImpl {
public final String iname; public final String iname;
public ArrayList<MethodNode> impls = new ArrayList<MethodNode>(); public ArrayList<MethodNode> impls = new ArrayList<>();
public InterfaceImpl(String iname, String cname) { public InterfaceImpl(String iname, String cname) {
this.iname = iname; this.iname = iname;
HashSet<String> names = new HashSet<String>(); HashSet<String> names = new HashSet<>();
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);
@@ -47,7 +62,7 @@ public class DefaultImplementationTransformer implements IClassTransformer {
} }
public boolean patch(ClassNode cnode) { public boolean patch(ClassNode cnode) {
LinkedList<String> names = new LinkedList<String>(); LinkedList<String> names = new LinkedList<>();
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);
@@ -69,25 +84,13 @@ public class DefaultImplementationTransformer implements IClassTransformer {
} }
return changed; return changed;
} }
}
private static HashMap<String, InterfaceImpl> impls = new HashMap<String, InterfaceImpl>(); private static ClassNode getClassNode(String name) {
try {
public static void registerDefaultImpl(String iname, String cname) { return ASMHelper.createClassNode(Launch.classLoader.getClassBytes(name.replace('/', '.')));
impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname)); } catch (IOException e) {
} 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,7 +1,5 @@
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;
@@ -11,7 +9,9 @@ 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,14 +21,18 @@ 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 ArrayList<IClassTransformer> delegatedTransformers; private static final ArrayList<IClassTransformer> delegatedTransformers;
private static Method m_defineClass; private static final Method m_defineClass;
private static Field f_cachedClasses; private static final Field f_cachedClasses;
private static IClassTransformer[] transformers;
public DelegatedTransformer() { static {
delegatedTransformers = new ArrayList<IClassTransformer>(); delegatedTransformers = new ArrayList<>();
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);
@@ -41,14 +45,75 @@ public class DelegatedTransformer implements IClassTransformer {
} }
@Override @Override
public byte[] transform(String name, String tname, byte[] bytes) { public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (bytes == null) return null; if (basicClass == null) return null;
for (IClassTransformer trans : delegatedTransformers) bytes = trans.transform(name, tname, bytes); for (IClassTransformer t : transformers) {
return bytes; 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) { public static void addTransformer(String transformer, JarFile jar, File jarFile) {
logger.debug("Adding CCTransformer: " + transformer); CodeChickenCorePlugin.logger.debug("Adding CCTransformer: " + transformer);
try { try {
byte[] bytes; byte[] bytes;
bytes = Launch.classLoader.getClassBytes(transformer); bytes = Launch.classLoader.getClassBytes(transformer);
@@ -107,7 +172,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);
logger.debug("Defining dependancy: " + dependancy); CodeChickenCorePlugin.logger.debug("Defining dependancy: " + dependancy);
defineClass(dependancy.replace('/', '.'), depbytes); defineClass(dependancy.replace('/', '.'), depbytes);
} }
@@ -128,7 +193,6 @@ 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,7 +24,6 @@ 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;
@@ -35,15 +34,11 @@ 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 new String[0]; return null;
} }
@Override @Override
@@ -118,26 +113,20 @@ public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes,
} }
public static void load() { 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") if (ObfMapping.obfuscated) {
.setComment("set to true to completely deobfuscate mcp names") ObfMapping.loadMCPRemapper();
.getBooleanValue(!ObfMapping.obfuscated)) { run.setSeargeConstants();
run = new ObfuscationRun( getTransformers().add(instance);
false, } else {
ObfMapping.MCPRemapper.getConfFiles(), getTransformers().add(0, instance); // insert transformer as first.
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,14 +5,15 @@ 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;
@@ -20,20 +21,29 @@ 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 {
static { private static final ModularASMTransformer transformer = new ModularASMTransformer();
ASMInit.init(); private static final Map<String, ASMBlock> blocks = ASMReader
} .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() {
CodeChickenCoreModContainer.loadConfig(); loadTransformersFromConfig();
tweaks = CodeChickenCoreModContainer.config.getTag("tweaks") 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(); .setComment("Various tweaks that can be applied to game mechanics.").useBraces();
tweaks.removeTag("persistantLava"); tweaks.removeTag("persistantLava");
@@ -103,7 +113,7 @@ public class TweakTransformer implements IClassTransformer, Opcodes {
} }
@Override @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); return transformer.transform(name, bytes);
} }
} }
@@ -6,6 +6,8 @@ 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;
@@ -73,7 +75,8 @@ public class GuiCCButton extends GuiWidget {
} }
public int getTextColour(int mousex, int mousey) { 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) { public GuiCCButton setActionCommand(String string) {
@@ -5,6 +5,8 @@ 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;
@@ -120,8 +122,8 @@ public class GuiCCTextField extends GuiWidget {
} }
public void drawBackground() { public void drawBackground() {
drawRect(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); drawRect(x - 1, y - 1, x + width + 1, y + height + 1, LocalizedColours.TEXT_FIELD_BORDER);
drawRect(x, y, x + width, y + height, 0xff000000); drawRect(x, y, x + width, y + height, LocalizedColours.TEXT_FIELD_BACKGROUND);
} }
public String getDrawText() { public String getDrawText() {
@@ -135,7 +137,7 @@ public class GuiCCTextField extends GuiWidget {
} }
public int getTextColour() { public int getTextColour() {
return isEnabled ? 0xe0e0e0 : 0x707070; return isEnabled ? LocalizedColours.TEXT_FIELD_TEXT : LocalizedColours.TEXT_FIELD_TEXT_DISABLED;
} }
public GuiCCTextField setMaxStringLength(int i) { public GuiCCTextField setMaxStringLength(int i) {
@@ -3,6 +3,7 @@ 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;
@@ -184,28 +185,29 @@ public abstract class GuiScrollPane extends GuiWidget {
} }
public void drawBackground(float frame) { 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 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, 0xffa0a0a0); // top drawRect(x, y - 1, x + width, y, LocalizedColours.SCROLL_PANE_OVERLAY_TOP);
drawRect(x, y + height, x + width, y + height + 1, 0xffa0a0a0); // bottom drawRect(x, y + height, x + width, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_BOTTOM);
drawRect(x - 1, y - 1, x, y + height + 1, 0xffa0a0a0); // left 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, 0xffa0a0a0); // right drawRect(x + width, y - 1, x + width + 1, y + height + 1, LocalizedColours.SCROLL_PANE_OVERLAY_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, 0xFF8B8B8B); // corners 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, 0xFFF0F0F0); // topleft up 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, 0xFF555555); // bottom right down 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, 0xFFC6C6C6); // scrollbar drawRect(r.x + 1, r.y + 1, r.x + r.width - 1, r.y + r.height - 1, LocalizedColours.SCROLLBAR_FILL);
int algn = scrollbarGuideAlignment(); 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.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;
@@ -21,48 +18,87 @@ 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;
@TransformerExclusions(value = { "codechicken.core.asm", "codechicken.obfuscator" }) @IFMLLoadingPlugin.MCVersion("1.7.10")
@MCVersion("1.7.10") @IFMLLoadingPlugin.TransformerExclusions({ "codechicken.core.asm", "codechicken.obfuscator", "codechicken.lib.asm",
public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook { "codechicken.lib.config" })
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; // get called twice, once for IFMLCallHook if (minecraftDir != null) return;
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();
}
}
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() { 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);
@@ -76,7 +112,7 @@ public class CodeChickenCorePlugin implements IFMLLoadingPlugin, IFMLCallHook {
0, 0,
new String[0])); new String[0]));
} catch (Exception e) { } 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); 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;
}
}
@@ -83,7 +83,7 @@ public class ClassConstantPoolParser {
if (strLen == bytes.length) { if (strLen == bytes.length) {
for (int j = index + 3; j < index + 3 + strLen; j++) { for (int j = index + 3; j < index + 3 + strLen; j++) {
if (basicClass[j] != bytes[j - (index + 3)]) { if (basicClass[j] != bytes[j - (index + 3)]) {
break label; continue label;
} }
} }
return true; return true;
@@ -1,14 +1,13 @@
package codechicken.lib.asm; package codechicken.lib.asm;
import java.util.ArrayList; import java.util.Map;
import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.HashSet; import java.util.function.Function;
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.tree.ClassNode; import org.objectweb.asm.ClassReader;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; 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 { public class ClassHeirachyManager implements IClassTransformer {
private static final Map<String, ClassInfo> superclasses = new ConcurrentHashMap<>(1000);
static { static {
ASMInit.init(); superclasses.put("java.lang.Object", ClassInfo.OBJECT);
} }
public static class SuperCache { @Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
String superclass; if (bytes == null) return null;
public HashSet<String> parents = new HashSet<String>(); if (!superclasses.containsKey(transformedName)) {
private boolean flattened; declareASM(transformedName, bytes);
public void add(String parent) {
parents.add(parent);
} }
return bytes;
}
public void flatten() { private static ClassInfo declareASM(String name, byte[] bytes) {
if (flattened) return; 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)) { private static ClassInfo declareReflection(String name) throws ClassNotFoundException {
SuperCache c = declareClass(s); final Class<?> aclass = Class.forName(name);
if (c != null) { final ClassInfo classInfo = new ClassInfo(aclass);
c.flatten(); superclasses.put(name, classInfo);
parents.addAll(c.parents); 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 String unKey(String name) {
private static LaunchClassLoader cl = Launch.classLoader; if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
public static String toKey(String name) { }
if (ObfMapping.obfuscated) return name.replace('/', '.');
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;
} }
/** /**
@@ -72,83 +84,99 @@ public class ClassHeirachyManager implements IClassTransformer {
if (name.equals(superclass)) return true; if (name.equals(superclass)) return true;
SuperCache cache = declareClass(name); ClassInfo classInfo = superclasses.get(name);
if (cache == null) // just can't handle this if (classInfo == null) classInfo = declareClass(name);
return false; if (classInfo == null) return false;
cache.flatten(); return classInfo.hasSuper(superclass);
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";
cache.flatten(); ClassInfo classInfo = superclasses.get(name);
String s = cache.superclass; if (classInfo == null) classInfo = declareClass(name);
if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s); if (classInfo == null || classInfo.superclass == null) return "java.lang.Object";
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,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) { public void add(ClassNodeTransformer t) {
ClassNodeTransformerList list = transformers.get(t.className()); 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); 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);
} }
@@ -23,11 +23,12 @@ import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.Textifier; import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor; import org.objectweb.asm.util.TraceClassVisitor;
public class RedirectorTransformer implements IClassTransformer { import codechicken.core.launch.CodeChickenCorePlugin;
public class RedirectorTransformer implements IClassTransformer, Opcodes {
private static final boolean DUMP_CLASSES = Boolean.parseBoolean(System.getProperty("ccl.dumpClass", "false")); private static final boolean DUMP_CLASSES = Boolean.parseBoolean(System.getProperty("ccl.dumpClass", "false"));
private static final String RenderStateClass = "codechicken/lib/render/CCRenderState"; private static final String RenderStateClass = "codechicken/lib/render/CCRenderState";
@@ -58,7 +59,12 @@ public class RedirectorTransformer implements IClassTransformer {
"hasBrightness", "hasBrightness",
"brightness", "brightness",
"side", "side",
"lc" "lc",
"normalAttrib",
"colourAttrib",
"lightCoordAttrib",
"sideAttrib",
"lightingAttrib"
); );
Collections.addAll(redirectedSimpleMethods, "reset", "pullLightmap", "pushLightmap", "setDynamic", "draw"); Collections.addAll(redirectedSimpleMethods, "reset", "pullLightmap", "pushLightmap", "setDynamic", "draw");
@@ -90,61 +96,63 @@ public class RedirectorTransformer implements IClassTransformer {
cr.accept(cn, 0); cr.accept(cn, 0);
boolean changed = false; boolean changed = false;
// spotless:off
for (MethodNode mn : cn.methods) { for (MethodNode mn : cn.methods) {
for (AbstractInsnNode node : mn.instructions.toArray()) { for (AbstractInsnNode node : mn.instructions.toArray()) {
if (node instanceof FieldInsnNode fNode) { if (node instanceof FieldInsnNode fNode) {
if (node.getOpcode() == Opcodes.GETSTATIC && redirectedFields.contains(fNode.name) if (node.getOpcode() == GETSTATIC && RenderStateClass.equals(fNode.owner) && redirectedFields.contains(fNode.name)) {
&& fNode.owner.equals(RenderStateClass)) { mn.instructions.insertBefore(fNode,
mn.instructions.insertBefore( new MethodInsnNode(
fNode, INVOKESTATIC,
new MethodInsnNode( fNode.owner,
Opcodes.INVOKESTATIC, "instance",
fNode.owner, "()Lcodechicken/lib/render/CCRenderState;",
"instance", false));
"()Lcodechicken/lib/render/CCRenderState;")); fNode.setOpcode(GETFIELD);
fNode.setOpcode(Opcodes.GETFIELD);
changed = true; changed = true;
} else if (node.getOpcode() == Opcodes.PUTSTATIC } else if (node.getOpcode() == PUTSTATIC && RenderStateClass.equals(fNode.owner) && redirectedFields.contains(fNode.name)) {
&& (redirectedFields.contains(fNode.name) && fNode.owner.equals(RenderStateClass))) { InsnList list = new InsnList();
InsnList beforePut = new InsnList(); list.add(new MethodInsnNode(
beforePut.add( INVOKESTATIC,
new MethodInsnNode( fNode.owner,
Opcodes.INVOKESTATIC, "instance",
fNode.owner, "()Lcodechicken/lib/render/CCRenderState;",
"instance", false));
"()Lcodechicken/lib/render/CCRenderState;")); list.add(new InsnNode(SWAP));
beforePut.add(new InsnNode(Opcodes.SWAP)); mn.instructions.insertBefore(fNode, list);
mn.instructions.insertBefore(fNode, beforePut); fNode.setOpcode(PUTFIELD);
fNode.setOpcode(Opcodes.PUTFIELD);
changed = true;
}
} else if (node.getOpcode() == Opcodes.INVOKESTATIC && node instanceof MethodInsnNode mNode) {
if (redirectedMethods.contains(mNode.name) && mNode.owner.equals(RenderStateClass)) {
mNode.name = mNode.name + "Static";
changed = true; changed = true;
} else if (redirectedSimpleMethods.contains(mNode.name) && mNode.owner.equals(RenderStateClass)) { }
mn.instructions.insertBefore( } else if (node instanceof MethodInsnNode mNode) {
mNode, if (node.getOpcode() == INVOKESTATIC && RenderStateClass.equals(mNode.owner) && redirectedSimpleMethods.contains(mNode.name)) {
new MethodInsnNode( mn.instructions.insertBefore(mNode,
Opcodes.INVOKESTATIC, new MethodInsnNode(
mNode.owner, INVOKESTATIC,
"instance", mNode.owner,
"()Lcodechicken/lib/render/CCRenderState;")); "instance",
mNode.setOpcode(Opcodes.INVOKEVIRTUAL); "()Lcodechicken/lib/render/CCRenderState;",
false));
mNode.setOpcode(INVOKEVIRTUAL);
mNode.name = mNode.name + "Instance";
changed = true;
} else if (node.getOpcode() == INVOKEVIRTUAL && RenderStateClass.equals(mNode.owner)
&& (redirectedSimpleMethods.contains(mNode.name) || redirectedMethods.contains(mNode.name))) {
// Handle mods that updated to previously new API
mNode.name = mNode.name + "Instance";
changed = true; changed = true;
} }
} }
} }
} }
// spotless:on
if (changed) { if (changed) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw); cn.accept(cw);
final byte[] bytes = cw.toByteArray(); final byte[] bytes = cw.toByteArray();
if (DUMP_CLASSES) { if (DUMP_CLASSES) {
saveTransformedClass(bytes, transformedName); saveTransformedClass(basicClass, transformedName + "_PRE");
saveTransformedClass(basicClass, transformedName + "_original"); saveTransformedClass(bytes, transformedName + "_POST");
} }
return bytes; return bytes;
} }
@@ -153,13 +161,9 @@ public class RedirectorTransformer implements IClassTransformer {
private File outputDir = null; private File outputDir = null;
private void saveTransformedClass(final byte[] data, final String transformedName) { private void saveTransformedClass(final byte[] data, final String classname) {
if (!DUMP_CLASSES) {
return;
}
if (outputDir == null) { if (outputDir == null) {
outputDir = new File(Launch.minecraftHome, "ASM_REDIRECTOR"); outputDir = new File(Launch.minecraftHome, "ASM_CCL" + File.separatorChar + "REDIRECTOR");
try { try {
FileUtils.deleteDirectory(outputDir); FileUtils.deleteDirectory(outputDir);
} catch (IOException ignored) {} } catch (IOException ignored) {}
@@ -168,40 +172,34 @@ public class RedirectorTransformer implements IClassTransformer {
outputDir.mkdirs(); outputDir.mkdirs();
} }
} }
final String fileName = classname.replace('.', File.separatorChar);
final String fileName = transformedName.replace('.', File.separatorChar);
final File classFile = new File(outputDir, fileName + ".class"); final File classFile = new File(outputDir, fileName + ".class");
final File bytecodeFile = new File(outputDir, fileName + "_BYTE.txt"); final File bytecodeFile = new File(outputDir, fileName + "_BYTE.txt");
final File asmifiedFile = new File(outputDir, fileName + "_ASM.txt");
final File outDir = classFile.getParentFile(); final File outDir = classFile.getParentFile();
if (!outDir.exists()) { if (!outDir.exists()) {
// noinspection ResultOfMethodCallIgnored
outDir.mkdirs(); outDir.mkdirs();
} }
if (classFile.exists()) { if (classFile.exists()) {
// noinspection ResultOfMethodCallIgnored
classFile.delete(); classFile.delete();
} }
try (final OutputStream output = Files.newOutputStream(classFile.toPath())) { try (final OutputStream output = Files.newOutputStream(classFile.toPath())) {
output.write(data); output.write(data);
CodeChickenCorePlugin.logger.info("Saved class (byte[]) to " + classFile.toPath());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); CodeChickenCorePlugin.logger.error("Could not save class (byte[]) " + classname);
} }
if (bytecodeFile.exists()) { if (bytecodeFile.exists()) {
// noinspection ResultOfMethodCallIgnored
bytecodeFile.delete(); bytecodeFile.delete();
} }
try (final OutputStream output = Files.newOutputStream(bytecodeFile.toPath())) { try (final OutputStream output = Files.newOutputStream(bytecodeFile.toPath())) {
final ClassReader classReader = new ClassReader(data); final ClassReader classReader = new ClassReader(data);
classReader.accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(output)), 0); classReader.accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(output)), 0);
CodeChickenCorePlugin.logger.info("Saved class (bytecode) to " + bytecodeFile.toPath());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); CodeChickenCorePlugin.logger.error("Could not save class (bytecode) " + classname);
}
if (asmifiedFile.exists()) {
asmifiedFile.delete();
}
try (final OutputStream output = Files.newOutputStream(asmifiedFile.toPath())) {
final ClassReader classReader = new ClassReader(data);
classReader.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(output)), 0);
} catch (IOException e) {
e.printStackTrace();
} }
} }
} }
@@ -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;
}
}
}
@@ -6,8 +6,10 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap; import java.util.TreeMap;
public abstract class ConfigTagParent { public abstract class ConfigTagParent {
@@ -21,25 +23,30 @@ public abstract class ConfigTagParent {
} }
public int compare(ConfigTag o1, ConfigTag o2) { public int compare(ConfigTag o1, ConfigTag o2) {
if (o1.position != o2.position) return compareInt(o1.position, o2.position); if (o1.position != o2.position) return Integer.compare(o1.position, o2.position);
if (o1.brace != o2.brace) return o1.brace ? 1 : -1; // braced one goes after if (o1.brace != o2.brace) return o1.brace ? 1 : -1; // braced one goes after
switch (sortMode) {
case 1: if (sortMode == 1) {
if (o1.value == o2.value) return 0; if (Objects.equals(o1.value, o2.value)) return 0;
if (o1.value == null) return 1; if (o1.value == null) return 1;
if (o2.value == null) return -1; if (o2.value == null) return -1;
return o1.value.compareTo(o2.value); return o1.value.compareTo(o2.value);
default: } else {
return o1.name.compareTo(o2.name); return o1.name.compareTo(o2.name);
} }
} }
private int compareInt(int a, int b) {
return a == b ? 0 : a < b ? -1 : 1;
}
} }
private TreeMap<String, ConfigTag> childtags = new TreeMap<String, ConfigTag>(); /**
* The immediate child tags of this tag
*/
private final TreeMap<String, ConfigTag> childtags = new TreeMap<>();
/**
* A map of all tags in the tree, meaning all tags in this tag and all tags in child tags (recursively). Allows for
* quick access to any tag in the tree, without the need to traverse the tree part by part.
*/
private final Map<String, ConfigTag> allTagsInTree = new HashMap<>();
public String comment; public String comment;
/** /**
* 0 = name, 1 = value * 0 = name, 1 = value
@@ -88,7 +95,7 @@ public abstract class ConfigTagParent {
} }
public boolean containsTag(String tagname) { public boolean containsTag(String tagname) {
return getTag(tagname, false) != null; return allTagsInTree.get(tagname) != null;
} }
public ConfigTag getNewTag(String tagname) { public ConfigTag getNewTag(String tagname) {
@@ -96,6 +103,12 @@ public abstract class ConfigTagParent {
} }
public ConfigTag getTag(String tagname, boolean create) { public ConfigTag getTag(String tagname, boolean create) {
ConfigTag tag = allTagsInTree.get(tagname);
if (tag == null && create) return getTagRecursive(tagname, create);
return tag;
}
public ConfigTag getTagRecursive(String tagname, boolean create) {
int dotpos = tagname.indexOf("."); int dotpos = tagname.indexOf(".");
String basetagname = dotpos == -1 ? tagname : tagname.substring(0, dotpos); String basetagname = dotpos == -1 ? tagname : tagname.substring(0, dotpos);
ConfigTag basetag = childtags.get(basetagname); ConfigTag basetag = childtags.get(basetagname);
@@ -115,6 +128,8 @@ public abstract class ConfigTagParent {
} }
public boolean removeTag(String tagname) { public boolean removeTag(String tagname) {
removeChildFromFlatMap(tagname);
ConfigTag tag = getTag(tagname, false); ConfigTag tag = getTag(tagname, false);
if (tag == null) return false; if (tag == null) return false;
@@ -131,6 +146,22 @@ public abstract class ConfigTagParent {
public void addChild(ConfigTag tag) { public void addChild(ConfigTag tag) {
childtags.put(tag.name, tag); childtags.put(tag.name, tag);
addChildToFlatMap(tag.name, tag);
}
private void addChildToFlatMap(String path, ConfigTag tag) {
allTagsInTree.put(path, tag);
// Stupid cast but these two garbage classes are interlinked anyway
if ((this instanceof ConfigTag t) && t.parent != null) t.parent.addChildToFlatMap(t.name + "." + path, tag);
}
private void removeChildFromFlatMap(String path) {
allTagsInTree.remove(path);
// Stupid cast but these two garbage classes are interlinked anyway
if ((this instanceof ConfigTag t) && t.parent != null) t.parent.removeChildFromFlatMap(t.name + "." + path);
} }
public <T extends ConfigTag> ArrayList<T> getSortedTagList() { public <T extends ConfigTag> ArrayList<T> getSortedTagList() {
@@ -64,15 +64,15 @@ public class Canvas9Seg {
public void draw(CCRenderState state, int x, int y, int w, int h) { public void draw(CCRenderState state, int x, int y, int w, int h) {
CCRenderState.changeTexture(tex); CCRenderState.changeTexture(tex);
state.reset(); state.resetInstance();
state.startDrawing(); state.startDrawingInstance();
int[] sw = new int[] { x, x + seg_w[0], x + w - seg_w[2], x + w }; int[] sw = new int[] { x, x + seg_w[0], x + w - seg_w[2], x + w };
int[] sh = new int[] { y, y + seg_h[0], y + h - seg_h[2], y + h }; int[] sh = new int[] { y, y + seg_h[0], y + h - seg_h[2], y + h };
for (int seg = 0; seg < 9; seg++) drawSeg(sw, sh, seg); for (int seg = 0; seg < 9; seg++) drawSeg(sw, sh, seg);
state.draw(); state.drawInstance();
} }
public void draw(int x, int y, int w, int h) { public void draw(int x, int y, int w, int h) {
+41 -14
View File
@@ -18,6 +18,7 @@ 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;
@@ -162,6 +163,19 @@ 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(
font,
x,
y,
list,
LocalizedColours.TOOLTIP_BG_START,
LocalizedColours.TOOLTIP_BG_END,
LocalizedColours.TOOLTIP_BORDER_START,
LocalizedColours.TOOLTIP_BORDER_END);
}
public static void drawMultilineTip(FontRenderer font, int x, int y, List<String> list, int bgStart, int bgEnd,
int borderStart, int borderEnd) {
if (list.isEmpty()) return; if (list.isEmpty()) return;
GL11.glDisable(GL12.GL_RESCALE_NORMAL); GL11.glDisable(GL12.GL_RESCALE_NORMAL);
@@ -189,14 +203,14 @@ public class GuiDraw {
y = (int) MathHelper.clip(y, 8, displaySize().height - 8 - h); y = (int) MathHelper.clip(y, 8, displaySize().height - 8 - h);
gui.incZLevel(300); gui.incZLevel(300);
drawTooltipBox(x - 4, y - 4, w + 7, h + 7); drawTooltipBox(x - 4, y - 4, w + 7, h + 7, bgStart, bgEnd, borderStart, borderEnd);
for (String s : list) { for (String s : list) {
ITooltipLineHandler line = getTipLine(s); ITooltipLineHandler line = getTipLine(s);
if (line != null) { if (line != null) {
line.draw(x, y); line.draw(x, y);
y += line.getSize().height; y += line.getSize().height;
} else { } else {
font.drawStringWithShadow(s, x, y, -1); font.drawStringWithShadow(s, x, y, LocalizedColours.TOOLTIP_TEXT);
y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10; y += s.endsWith(TOOLTIP_LINESPACE) ? 12 : 10;
} }
} }
@@ -210,18 +224,31 @@ 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) {
int bg = 0xf0100010; drawTooltipBox(
drawGradientRect(x + 1, y, w - 1, 1, bg, bg); x,
drawGradientRect(x + 1, y + h, w - 1, 1, bg, bg); y,
drawGradientRect(x + 1, y + 1, w - 1, h - 1, bg, bg); // center w,
drawGradientRect(x, y + 1, 1, h - 1, bg, bg); h,
drawGradientRect(x + w, y + 1, 1, h - 1, bg, bg); LocalizedColours.TOOLTIP_BG_START,
int grad1 = 0x505000ff; LocalizedColours.TOOLTIP_BG_END,
int grad2 = 0x5028007F; LocalizedColours.TOOLTIP_BORDER_START,
drawGradientRect(x + 1, y + 2, 1, h - 3, grad1, grad2); LocalizedColours.TOOLTIP_BORDER_END);
drawGradientRect(x + w - 1, y + 2, 1, h - 3, grad1, grad2); }
drawGradientRect(x + 1, y + 1, w - 1, 1, grad1, grad1); public static void drawTooltipBox(int x, int y, int w, int h, int bgStart, int bgEnd, int borderStart,
drawGradientRect(x + 1, y + h - 1, w - 1, 1, grad2, grad2); int borderEnd) {
// spotless:off
// draw background
drawGradientRect(x + 1, y, w - 1, 1, bgStart, bgStart); // top
drawGradientRect(x + 1, y + h, w - 1, 1, bgEnd, bgEnd); // bottom
drawGradientRect(x + 1, y + 1, w - 1, h - 1, bgStart, bgEnd); // center
drawGradientRect(x, y + 1, 1, h - 1, bgStart, bgEnd); // left
drawGradientRect(x + w, y + 1, 1, h - 1, bgStart, bgEnd); // right
// draw inner border
drawGradientRect(x + 1, y + 2, 1, h - 3, borderStart, borderEnd); // left
drawGradientRect(x + w - 1, y + 2, 1, h - 3, borderStart, borderEnd); // right
drawGradientRect(x + 1, y + 1, w - 1, 1, borderStart, borderStart); // top
drawGradientRect(x + 1, y + h - 1, w - 1, 1, borderEnd, borderEnd); // bottom
// spotless:on
} }
} }
@@ -128,8 +128,8 @@ public class LightMatrix implements CCRenderState.IVertexOperation {
float[] a = ao(lc.side); float[] a = ao(lc.side);
float f = (a[0] * lc.fa + a[1] * lc.fb + a[2] * lc.fc + a[3] * lc.fd); float f = (a[0] * lc.fa + a[1] * lc.fb + a[2] * lc.fc + a[3] * lc.fd);
int[] b = brightness(lc.side); int[] b = brightness(lc.side);
state.setColour(ColourRGBA.multiplyC(state.colour, f)); state.setColourInstance(ColourRGBA.multiplyC(state.colour, f));
state.setBrightness((int) (b[0] * lc.fa + b[1] * lc.fb + b[2] * lc.fc + b[3] * lc.fd) & 0xFF00FF); state.setBrightnessInstance((int) (b[0] * lc.fa + b[1] * lc.fb + b[2] * lc.fc + b[3] * lc.fd) & 0xFF00FF);
} }
@Override @Override
@@ -81,14 +81,14 @@ public class LightModel implements CCRenderState.IVertexOperation {
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
if (!state.computeLighting) return false; if (!state.computeLighting) return false;
state.pipeline.addDependency(CCRenderState.normalAttrib); state.pipeline.addDependency(CCRenderState.normalAttrib());
state.pipeline.addDependency(CCRenderState.colourAttrib); state.pipeline.addDependency(CCRenderState.colourAttrib());
return true; return true;
} }
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
state.setColour(apply(state.colour, state.normal)); state.setColourInstance(apply(state.colour, state.normal));
} }
@Override @Override
@@ -40,14 +40,14 @@ public class PlanarLightMatrix extends PlanarLightModel {
@Override @Override
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
state.pipeline.addDependency(CCRenderState.sideAttrib); state.pipeline.addDependency(CCRenderState.sideAttrib());
return true; return true;
} }
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
super.operate(state); super.operate(state);
state.setBrightness(brightness(state.side)); state.setBrightnessInstance(brightness(state.side));
} }
@Override @Override
@@ -20,14 +20,14 @@ public class PlanarLightModel implements CCRenderState.IVertexOperation {
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
if (!state.computeLighting) return false; if (!state.computeLighting) return false;
state.pipeline.addDependency(CCRenderState.sideAttrib); state.pipeline.addDependency(CCRenderState.sideAttrib());
state.pipeline.addDependency(CCRenderState.colourAttrib); state.pipeline.addDependency(CCRenderState.colourAttrib());
return true; return true;
} }
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
state.setColour(ColourRGBA.multiply(state.colour, colours[state.side])); state.setColourInstance(ColourRGBA.multiply(state.colour, colours[state.side]));
} }
@Override @Override
@@ -12,7 +12,12 @@ 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();
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 IBlockAccess access;
public BlockCoord pos = new BlockCoord(); public BlockCoord pos = new BlockCoord();
@@ -40,13 +45,13 @@ public class SimpleBrightnessModel implements CCRenderState.IVertexOperation {
@Override @Override
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
state.pipeline.addDependency(CCRenderState.sideAttrib); state.pipeline.addDependency(CCRenderState.sideAttrib());
return true; return true;
} }
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
state.setBrightness(sample(state.side)); state.setBrightnessInstance(sample(state.side));
} }
@Override @Override
@@ -2,6 +2,7 @@ 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;
@@ -50,6 +51,8 @@ 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 {
} }
@@ -190,7 +193,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf = payload; byteBuf = payload;
type = byteBuf.readUnsignedByte(); type = byteBuf.readUnsignedByte();
if (type > 0x80) decompress(); if (type > 0x80) decompressPayload();
type &= 0x7F; type &= 0x7F;
} }
@@ -204,44 +207,61 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf.writeByte(type); byteBuf.writeByte(type);
} }
/** private void decompressPayload() {
* Decompresses the remaining ByteBuf (after type has been read) using Snappy if (byteBuf.readableBytes() < 4) throw new IllegalArgumentException("Missing compressed packet length");
*/ int declaredLength = byteBuf.readInt();
private void decompress() { if (declaredLength < 0 || declaredLength > MAX_DECOMPRESSED_SIZE)
throw new IllegalArgumentException("Invalid decompressed packet length: " + declaredLength);
Inflater inflater = new Inflater(); Inflater inflater = new Inflater();
ByteBuf decodedPayload = null;
try { try {
int len = byteBuf.readInt(); byte[] compressedInput = new byte[byteBuf.readableBytes()];
ByteBuf out = Unpooled.buffer(len); byteBuf.readBytes(compressedInput);
inflater.setInput(byteBuf.array(), byteBuf.readerIndex(), byteBuf.readableBytes()); inflater.setInput(compressedInput);
inflater.inflate(out.array()); decodedPayload = Unpooled.buffer(Math.min(declaredLength, 8192), declaredLength);
out.writerIndex(len); byte[] inflatedChunk = new byte[Math.min(declaredLength + 1, 8192)];
byteBuf = out; while (!inflater.finished()) {
} catch (Exception e) { int inflatedBytes = inflater.inflate(inflatedChunk);
throw new RuntimeException(e); if (inflatedBytes > declaredLength - decodedPayload.writerIndex())
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() {
* Compresses the payload ByteBuf after the type byte if ((byteBuf.getUnsignedByte(0) & 0x80) != 0) return;
*/
private void do_compress() { int payloadLength = byteBuf.writerIndex() - 1;
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);
int len = byteBuf.readableBytes(); deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), payloadLength);
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), len);
deflater.finish(); deflater.finish();
ByteBuf out = Unpooled.buffer(len + 5); ByteBuf compressedPayload = Unpooled.buffer(payloadLength + 5);
int clen = deflater.deflate(out.array(), 5, len); int compressedLength = deflater.deflate(compressedPayload.array(), 5, payloadLength);
if (clen >= len - 5 || !deflater.finished()) // not worth compressing, gets larger if (compressedLength >= payloadLength - 5 || !deflater.finished()) return;
return;
out.setByte(0, type | 0x80); compressedPayload.setByte(0, type | 0x80);
out.setInt(1, len); compressedPayload.setInt(1, payloadLength);
out.writerIndex(clen + 5); compressedPayload.writerIndex(compressedLength + 5);
byteBuf = out; byteBuf = compressedPayload;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
@@ -466,7 +486,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) fluid = FluidRegistry.WATER; if (fluid == null) return null;
return new FluidStack(fluid, readVarInt(), readNBTTagCompound()); return new FluidStack(fluid, readVarInt(), readNBTTagCompound());
} }
@@ -474,7 +494,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) do_compress(); if (byteBuf.readableBytes() > 32000 || (type & 0x80) != 0) compressPayload();
// 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);
@@ -20,12 +20,12 @@ public class BlockRenderer {
@Override @Override
public <T> T getAttributes(CCRenderState.VertexAttribute<T> attr) { public <T> T getAttributes(CCRenderState.VertexAttribute<T> attr) {
return attr == CCRenderState.lightCoordAttrib && lcComputed ? (T) lightCoords : null; return attr == CCRenderState.lightCoordAttrib() && lcComputed ? (T) lightCoords : null;
} }
@Override @Override
public boolean hasAttribute(CCRenderState.VertexAttribute<?> attr) { public boolean hasAttribute(CCRenderState.VertexAttribute<?> attr) {
return attr == CCRenderState.sideAttrib || attr == CCRenderState.lightCoordAttrib && lcComputed; return attr == CCRenderState.sideAttrib() || attr == CCRenderState.lightCoordAttrib() && lcComputed;
} }
@Override @Override
@@ -136,12 +136,12 @@ public class BlockRenderer {
@Override @Override
public <T> T getAttributes(VertexAttribute<T> attr) { public <T> T getAttributes(VertexAttribute<T> attr) {
return attr == CCRenderState.lightCoordAttrib ? (T) lightCoords : null; return attr == CCRenderState.lightCoordAttrib() ? (T) lightCoords : null;
} }
@Override @Override
public boolean hasAttribute(VertexAttribute<?> attr) { public boolean hasAttribute(VertexAttribute<?> attr) {
return attr == CCRenderState.sideAttrib || attr == CCRenderState.lightCoordAttrib; return attr == CCRenderState.sideAttrib() || attr == CCRenderState.lightCoordAttrib();
} }
@Override @Override
@@ -154,7 +154,7 @@ public class BlockRenderer {
// public static void renderFullBlock(int sideMask) { // public static void renderFullBlock(int sideMask) {
public static void renderFullBlock(CCRenderState state, int sideMask) { public static void renderFullBlock(CCRenderState state, int sideMask) {
state.setModel(fullBlock); state.setModelInstance(fullBlock);
renderFaces(state, sideMask); renderFaces(state, sideMask);
} }
@@ -164,15 +164,15 @@ public class BlockRenderer {
/** /**
* Renders faces of a block-like model based on a sideMask. Eg for side 2, verts 8-11 will be rendered * Renders faces of a block-like model based on a sideMask. Eg for side 2, verts 8-11 will be rendered
* *
* @param sideMask A mask of faces not to render * @param sideMask A mask of faces not to render
*/ */
// public static void renderFaces(int sideMask) { // public static void renderFaces(int sideMask) {
public static void renderFaces(CCRenderState state, int sideMask) { public static void renderFaces(CCRenderState state, int sideMask) {
if (sideMask == 0x3F) return; if (sideMask == 0x3F) return;
for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) { for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) {
state.setVertexRange(s * 4, (s + 1) * 4); state.setVertexRangeInstance(s * 4, (s + 1) * 4);
state.render(); state.renderInstance();
} }
} }
@@ -184,17 +184,17 @@ public class BlockRenderer {
/** /**
* Renders faces of a cuboid with texture coordinates mapped to match a standard minecraft block * Renders faces of a cuboid with texture coordinates mapped to match a standard minecraft block
* *
* @param bounds The bounding cuboid to render * @param bounds The bounding cuboid to render
* @param sideMask A mask of faces not to render * @param sideMask A mask of faces not to render
*/ */
public static void renderCuboid(CCRenderState state, Cuboid6 bounds, int sideMask) { public static void renderCuboid(CCRenderState state, Cuboid6 bounds, int sideMask) {
if (sideMask == 0x3F) return; if (sideMask == 0x3F) return;
state.setModel(face); state.setModelInstance(face);
for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) { for (int s = 0; s < 6; s++) if ((sideMask & 1 << s) == 0) {
face.loadCuboidFace(bounds, s); face.loadCuboidFace(bounds, s);
state.render(); state.renderInstance();
} }
} }
@@ -68,7 +68,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
} }
public Vector3[] normals() { public Vector3[] normals() {
return getAttributes(CCRenderState.normalAttrib); return getAttributes(CCRenderState.normalAttrib());
} }
@Override @Override
@@ -102,7 +102,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Each pixel corresponds to one unit of position when generating the model * Each pixel corresponds to one unit of position when generating the model
* *
* @param i Vertex index to start generating at * @param i Vertex index to start generating at
* @param x1 The minX bound of the box * @param x1 The minX bound of the box
* @param y1 The minY bound of the box * @param y1 The minY bound of the box
@@ -195,7 +195,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Generates a box, uv mapped to be the same as a minecraft block with the same bounds * Generates a box, uv mapped to be the same as a minecraft block with the same bounds
* *
* @param i The vertex index to start generating at * @param i The vertex index to start generating at
* @param bounds The bounds of the block, 0 to 1 * @param bounds The bounds of the block, 0 to 1
* @return The generated model. When rendering an icon will need to be supplied for the UV transformation. * @return The generated model. When rendering an icon will need to be supplied for the UV transformation.
@@ -222,7 +222,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Generates a box, uv mapped to be the same as a minecraft block with the same bounds * Generates a box, uv mapped to be the same as a minecraft block with the same bounds
* *
* @param i The vertex index to start generating at * @param i The vertex index to start generating at
* @param x1 minX * @param x1 minX
* @param y1 minY * @param y1 minY
@@ -311,7 +311,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Computes the normals of all faces in the model. Uses the cross product of the vectors along 2 sides of the face * Computes the normals of all faces in the model. Uses the cross product of the vectors along 2 sides of the face
* *
* @param start The first vertex to generate normals for * @param start The first vertex to generate normals for
* @param length The number of vertices to generate normals for. Note this must be a multiple of 3 for triangles or * @param length The number of vertices to generate normals for. Note this must be a multiple of 3 for triangles or
* 4 for quads * 4 for quads
@@ -321,7 +321,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
if (length % vp != 0 || start % vp != 0) if (length % vp != 0 || start % vp != 0)
throw new IllegalArgumentException("Cannot generate normals across polygons"); throw new IllegalArgumentException("Cannot generate normals across polygons");
Vector3[] normals = getOrAllocate(CCRenderState.normalAttrib); Vector3[] normals = getOrAllocate(CCRenderState.normalAttrib());
for (int k = 0; k < length; k += vp) { for (int k = 0; k < length; k += vp) {
int i = k + start; int i = k + start;
Vector3 diff1 = verts[i + 1].vec.copy().subtract(verts[i].vec); Vector3 diff1 = verts[i + 1].vec.copy().subtract(verts[i].vec);
@@ -336,14 +336,14 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Computes lighting using the normals add a light model If the model is rotated, the lighting will no longer be * Computes lighting using the normals add a light model If the model is rotated, the lighting will no longer be
* valid * valid
* *
* @return The model * @return The model
*/ */
public CCModel computeLighting(LightModel light) { public CCModel computeLighting(LightModel light) {
Vector3[] normals = normals(); Vector3[] normals = normals();
int[] colours = getAttributes(CCRenderState.lightingAttrib); int[] colours = getAttributes(CCRenderState.lightingAttrib());
if (colours == null) { if (colours == null) {
colours = getOrAllocate(CCRenderState.lightingAttrib); colours = getOrAllocate(CCRenderState.lightingAttrib());
Arrays.fill(colours, -1); Arrays.fill(colours, -1);
} }
for (int k = 0; k < verts.length; k++) colours[k] = light.apply(colours[k], normals[k]); for (int k = 0; k < verts.length; k++) colours[k] = light.apply(colours[k], normals[k]);
@@ -351,18 +351,18 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
} }
public CCModel setColour(int c) { public CCModel setColour(int c) {
int[] colours = getOrAllocate(CCRenderState.colourAttrib); int[] colours = getOrAllocate(CCRenderState.colourAttrib());
Arrays.fill(colours, c); Arrays.fill(colours, c);
return this; return this;
} }
/** /**
* Computes the minecraft lighting coordinates for use with a LightMatrix * Computes the minecraft lighting coordinates for use with a LightMatrix
* *
* @return The model * @return The model
*/ */
public CCModel computeLightCoords() { public CCModel computeLightCoords() {
LC[] lcs = getOrAllocate(CCRenderState.lightCoordAttrib); LC[] lcs = getOrAllocate(CCRenderState.lightCoordAttrib());
Vector3[] normals = normals(); Vector3[] normals = normals();
for (int i = 0; i < verts.length; i++) lcs[i] = new LC().compute(verts[i].vec, normals[i]); for (int i = 0; i < verts.length; i++) lcs[i] = new LC().compute(verts[i].vec, normals[i]);
return this; return this;
@@ -370,7 +370,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Averages all normals at the same position to produce a smooth lighting effect. * Averages all normals at the same position to produce a smooth lighting effect.
* *
* @return The model * @return The model
*/ */
public CCModel smoothNormals() { public CCModel smoothNormals() {
@@ -461,14 +461,14 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Renders vertices start through start+length-1 of the model * Renders vertices start through start+length-1 of the model
* *
* @param start The first vertex index to render * @param start The first vertex index to render
* @param end The vertex index to render until * @param end The vertex index to render until
* @param ops Operations to apply * @param ops Operations to apply
*/ */
public void render(CCRenderState state, int start, int end, CCRenderState.IVertexOperation... ops) { public void render(CCRenderState state, int start, int end, CCRenderState.IVertexOperation... ops) {
state.setPipeline(this, start, end, ops); state.setPipelineInstance(this, start, end, ops);
state.render(); state.renderInstance();
} }
public void render(int start, int end, CCRenderState.IVertexOperation... ops) { public void render(int start, int end, CCRenderState.IVertexOperation... ops) {
@@ -520,7 +520,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file * Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
* *
* @param input An input stream to a obj file * @param input An input stream to a obj file
* @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS) * @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS)
* @param coordSystem The cooridnate system transformation to apply * @param coordSystem The cooridnate system transformation to apply
@@ -624,7 +624,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file * Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
* *
* @param res The resource for the obj file * @param res The resource for the obj file
* @return A map of group names to models * @return A map of group names to models
*/ */
@@ -634,7 +634,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file * Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
* *
* @param res The resource for the obj file * @param res The resource for the obj file
* @param coordSystem The cooridnate system transformation to apply * @param coordSystem The cooridnate system transformation to apply
* @return A map of group names to models * @return A map of group names to models
@@ -652,7 +652,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file * Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
* *
* @param res The resource for the obj file * @param res The resource for the obj file
* @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS) * @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS)
* @param coordSystem The cooridnate system transformation to apply * @param coordSystem The cooridnate system transformation to apply
@@ -678,7 +678,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
boolean hasNormals = polys.get(0)[2] > 0; boolean hasNormals = polys.get(0)[2] > 0;
CCModel model = CCModel.newModel(vertexMode, polys.size()); CCModel model = CCModel.newModel(vertexMode, polys.size());
if (hasNormals) model.getOrAllocate(CCRenderState.normalAttrib); if (hasNormals) model.getOrAllocate(CCRenderState.normalAttrib());
for (int i = 0; i < polys.size(); i++) { for (int i = 0; i < polys.size(); i++) {
int[] ai = polys.get(i); int[] ai = polys.get(i);
@@ -807,7 +807,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Generate models rotated to the other 5 sides of the block * Generate models rotated to the other 5 sides of the block
* *
* @param models An array of 6 models * @param models An array of 6 models
* @param side The side of this model * @param side The side of this model
* @param point The rotation point * @param point The rotation point
@@ -822,7 +822,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Generate models rotated to the other 3 horizontal of the block * Generate models rotated to the other 3 horizontal of the block
* *
* @param models An array of 4 models * @param models An array of 4 models
* @param side The side of this model * @param side The side of this model
* @param point The rotation point * @param point The rotation point
@@ -841,7 +841,7 @@ public class CCModel implements CCRenderState.IVertexSource, Copyable<CCModel> {
/** /**
* Generates copies of faces with clockwise vertices * Generates copies of faces with clockwise vertices
* *
* @return The model * @return The model
*/ */
public static CCModel generateBackface(CCModel src, int srcpos, CCModel dst, int destpos, int length) { public static CCModel generateBackface(CCModel src, int srcpos, CCModel dst, int destpos, int length) {
@@ -5,7 +5,6 @@ import static codechicken.lib.math.MathHelper.phi;
import codechicken.lib.vec.Matrix4; import codechicken.lib.vec.Matrix4;
import codechicken.lib.vec.Quat; import codechicken.lib.vec.Quat;
import codechicken.lib.vec.Rotation; import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Scale;
import codechicken.lib.vec.Vector3; import codechicken.lib.vec.Vector3;
public class CCModelLibrary { public class CCModelLibrary {
@@ -82,7 +81,12 @@ public class CCModelLibrary {
i++; i++;
} }
@Deprecated
public static Matrix4 getRenderMatrix(Vector3 position, Rotation rotation, double scale) { public static Matrix4 getRenderMatrix(Vector3 position, Rotation rotation, double scale) {
return new Matrix4().translate(position).apply(new Scale(scale)).apply(rotation); return new Matrix4().translate(position).scale(scale).apply(rotation);
}
public static Matrix4 getRenderMatrix(double posX, double posY, double posZ, Rotation rotation, double scale) {
return new Matrix4().translate(posX, posY, posZ).scale(scale).apply(rotation);
} }
} }
@@ -49,7 +49,7 @@ public class CCRenderPipeline {
public void render() { public void render() {
rebuild(); rebuild();
renderState.render(); renderState.renderInstance();
} }
} }
@@ -1,6 +1,7 @@
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;
@@ -38,9 +39,9 @@ public class CCRenderState {
return instances.get(); return instances.get();
} }
private static int nextOperationIndex; private static volatile int nextOperationIndex;
public static int registerOperation() { public static synchronized int registerOperation() {
return nextOperationIndex++; return nextOperationIndex++;
} }
@@ -87,33 +88,82 @@ public class CCRenderState {
int operationID(); int operationID();
} }
private static ArrayList<VertexAttribute<?>> vertexAttributes = new ArrayList<>(); public static final class AttributeKey<T> {
private static int registerVertexAttribute(VertexAttribute<?> attr) { private static final HashMap<String, AttributeKey<?>> nameMap = new HashMap<>();
vertexAttributes.add(attr); private static final ArrayList<AttributeKey<?>> attributeKeys = new ArrayList<>();
return vertexAttributes.size() - 1; 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) { public static VertexAttribute<?> getAttribute(int index) {
return vertexAttributes.get(index); return AttributeKey.get(index).canonical;
} }
/** /**
* Management class for a vertex attrute such as colour, normal etc This class should handle the loading of the * Management class for a vertex attrute such as colour, normal etc This class should handle the loading of the
* attrute from an array provided by IVertexSource.getAttributes or the computation of this attrute from others * attrute from an array provided by IVertexSource.getAttributes or the computation of this attrute from others
* *
* @param <T> The array type for this attrute eg. int[], Vector3[] * @param <T> The array type for this attrute eg. int[], Vector3[]
*/ */
public abstract static class VertexAttribute<T> implements IVertexOperation { public abstract static class VertexAttribute<T> implements IVertexOperation {
public final int attributeIndex = registerVertexAttribute(this); public final int attributeIndex;
private final int operationIndex = registerOperation(); private final int operationIndex;
/** /**
* 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
*/ */
@@ -146,7 +196,7 @@ public class CCRenderState {
/** /**
* Gets an array of vertex attrutes * Gets an array of vertex attrutes
* *
* @param attr The vertex attrute to get * @param attr The vertex attrute to get
* @param <T> The attrute array type * @param <T> The attrute array type
* @return An array, or null if not computed * @return An array, or null if not computed
@@ -169,7 +219,27 @@ public class CCRenderState {
} }
} }
public static VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>() { public static VertexAttribute<Vector3[]> normalAttrib() {
return instances.get().normalAttrib;
}
public static VertexAttribute<int[]> colourAttrib() {
return instances.get().colourAttrib;
}
public static VertexAttribute<LC[]> lightCoordAttrib() {
return instances.get().lightCoordAttrib;
}
public static VertexAttribute<int[]> sideAttrib() {
return instances.get().sideAttrib;
}
public static VertexAttribute<int[]> lightingAttrib() {
return instances.get().lightingAttrib;
}
public VertexAttribute<Vector3[]> normalAttrib = new VertexAttribute<>(NORMAL_KEY) {
private Vector3[] normalRef; private Vector3[] normalRef;
@@ -193,11 +263,11 @@ public class CCRenderState {
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
if (normalRef != null) state.setNormal(normalRef[state.vertexIndex]); if (normalRef != null) state.setNormalInstance(normalRef[state.vertexIndex]);
else state.setNormal(Rotation.axes[state.side]); else state.setNormalInstance(Rotation.axes[state.side]);
} }
}; };
public static VertexAttribute<int[]> colourAttrib = new VertexAttribute<>() { public VertexAttribute<int[]> colourAttrib = new VertexAttribute<>(COLOUR_KEY) {
private int[] colourRef; private int[] colourRef;
@@ -214,11 +284,12 @@ public class CCRenderState {
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
if (colourRef != null) state.setColour(ColourRGBA.multiply(state.baseColour, colourRef[state.vertexIndex])); if (colourRef != null)
else state.setColour(state.baseColour); state.setColourInstance(ColourRGBA.multiply(state.baseColour, colourRef[state.vertexIndex]));
else state.setColourInstance(state.baseColour);
} }
}; };
public static VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>() { public VertexAttribute<int[]> lightingAttrib = new VertexAttribute<>(LIGHTING_KEY) {
private int[] colourRef; private int[] colourRef;
@@ -241,10 +312,10 @@ public class CCRenderState {
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
state.setColour(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex])); state.setColourInstance(ColourRGBA.multiply(state.colour, colourRef[state.vertexIndex]));
} }
}; };
public static VertexAttribute<int[]> sideAttrib = new VertexAttribute<>() { public VertexAttribute<int[]> sideAttrib = new VertexAttribute<>(SIDE_KEY) {
private int[] sideRef; private int[] sideRef;
@@ -271,7 +342,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 static VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>() { public VertexAttribute<LC[]> lightCoordAttrib = new VertexAttribute<>(LC_KEY) {
private LC[] lcRef; private LC[] lcRef;
private final Vector3 vec = new Vector3(); // for computation private final Vector3 vec = new Vector3(); // for computation
@@ -326,11 +397,15 @@ public class CCRenderState {
public boolean hasBrightness; public boolean hasBrightness;
public int brightness; public int brightness;
// attrute storage // attribute storage
public int side; public int side;
public LC lc = new LC(); public LC lc = new LC();
public void reset() { public static void reset() {
instance().resetInstance();
}
public void resetInstance() {
model = null; model = null;
pipeline.reset(); pipeline.reset();
useNormals = hasNormal = hasBrightness = hasColour = false; useNormals = hasNormal = hasBrightness = hasColour = false;
@@ -338,27 +413,27 @@ public class CCRenderState {
baseColour = alphaOverride = -1; baseColour = alphaOverride = -1;
} }
public void setPipeline(IVertexOperation... ops) { public void setPipelineInstance(IVertexOperation... ops) {
pipeline.setPipeline(ops); pipeline.setPipeline(ops);
} }
@Deprecated @Deprecated
public static void setPipelineStatic(IVertexOperation... ops) { public static void setPipeline(IVertexOperation... ops) {
instance().setPipeline(ops); instance().setPipelineInstance(ops);
} }
public void setPipeline(IVertexSource model, int start, int end, IVertexOperation... ops) { public void setPipelineInstance(IVertexSource model, int start, int end, IVertexOperation... ops) {
pipeline.reset(); pipeline.reset();
setModel(model, start, end); setModelInstance(model, start, end);
pipeline.setPipeline(ops); pipeline.setPipeline(ops);
} }
@Deprecated @Deprecated
public static void setPipelineStatic(IVertexSource model, int start, int end, IVertexOperation... ops) { public static void setPipeline(IVertexSource model, int start, int end, IVertexOperation... ops) {
instance().setPipeline(model, start, end, ops); instance().setPipelineInstance(model, start, end, ops);
} }
public void bindModel(IVertexSource model) { public void bindModelInstance(IVertexSource model) {
if (this.model != model) { if (this.model != model) {
this.model = model; this.model = model;
pipeline.rebuild(); pipeline.rebuild();
@@ -366,74 +441,74 @@ public class CCRenderState {
} }
@Deprecated @Deprecated
public static void bindModelStatic(IVertexSource model) { public static void bindModel(IVertexSource model) {
instance().bindModel(model); instance().bindModelInstance(model);
} }
public void setModel(IVertexSource source) { public void setModelInstance(IVertexSource source) {
setModel(source, 0, source.getVertices().length); setModelInstance(source, 0, source.getVertices().length);
} }
@Deprecated @Deprecated
public static void setModelStatic(IVertexSource source) { public static void setModel(IVertexSource source) {
instance().setModel(source); instance().setModelInstance(source);
} }
public void setModel(IVertexSource source, int start, int end) { public void setModelInstance(IVertexSource source, int start, int end) {
bindModel(source); bindModelInstance(source);
setVertexRange(start, end); setVertexRangeInstance(start, end);
} }
@Deprecated @Deprecated
public static void setModelStatic(IVertexSource source, int start, int end) { public static void setModel(IVertexSource source, int start, int end) {
instance().setModel(source, start, end); instance().setModelInstance(source, start, end);
} }
public void setVertexRange(int start, int end) { public void setVertexRangeInstance(int start, int end) {
firstVertexIndex = start; firstVertexIndex = start;
lastVertexIndex = end; lastVertexIndex = end;
} }
@Deprecated @Deprecated
public static void setVertexRangeStatic(int start, int end) { public static void setVertexRange(int start, int end) {
instance().setVertexRange(start, end); instance().setVertexRangeInstance(start, end);
} }
public void render(IVertexOperation... ops) { public void renderInstance(IVertexOperation... ops) {
setPipeline(ops); setPipelineInstance(ops);
render(); renderInstance();
} }
@Deprecated @Deprecated
public static void renderStatic(IVertexOperation... ops) { public static void render(IVertexOperation... ops) {
instance().render(ops); instance().renderInstance(ops);
} }
public void render() { public void renderInstance() {
Vertex5[] verts = model.getVertices(); Vertex5[] verts = model.getVertices();
for (vertexIndex = firstVertexIndex; vertexIndex < lastVertexIndex; vertexIndex++) { for (vertexIndex = firstVertexIndex; vertexIndex < lastVertexIndex; vertexIndex++) {
model.prepareVertex(this); model.prepareVertex(this);
vert.set(verts[vertexIndex]); vert.set(verts[vertexIndex]);
runPipeline(); runPipelineInstance();
writeVert(); writeVertInstance();
} }
} }
@Deprecated @Deprecated
public static void renderStatic() { public static void render() {
instance().render(); instance().renderInstance();
} }
public void runPipeline() { public void runPipelineInstance() {
pipeline.operate(); pipeline.operate();
} }
@Deprecated @Deprecated
public static void runPipelineStatic() { public static void runPipeline() {
instance().runPipeline(); instance().runPipelineInstance();
} }
public void writeVert() { public void writeVertInstance() {
if (hasNormal) Tessellator.instance.setNormal((float) normal.x, (float) normal.y, (float) normal.z); if (hasNormal) Tessellator.instance.setNormal((float) normal.x, (float) normal.y, (float) normal.z);
if (hasColour) Tessellator.instance.setColorRGBA( if (hasColour) Tessellator.instance.setColorRGBA(
colour >>> 24, colour >>> 24,
@@ -445,71 +520,83 @@ public class CCRenderState {
} }
@Deprecated @Deprecated
public static void writeVertStatic() { public static void writeVert() {
instance().writeVert(); instance().writeVertInstance();
} }
public void setNormal(double x, double y, double z) { public void setNormalInstance(double x, double y, double z) {
hasNormal = true; hasNormal = true;
normal.set(x, y, z); normal.set(x, y, z);
} }
@Deprecated @Deprecated
public static void setNormalStatic(double x, double y, double z) { public static void setNormal(double x, double y, double z) {
instance().setNormal(x, y, z); instance().setNormalInstance(x, y, z);
} }
public void setNormal(Vector3 n) { public void setNormalInstance(Vector3 n) {
hasNormal = true; hasNormal = true;
normal.set(n); normal.set(n);
} }
@Deprecated @Deprecated
public static void setNormalStatic(Vector3 n) { public static void setNormal(Vector3 n) {
instance().setNormal(n); instance().setNormalInstance(n);
} }
public void setColour(int c) { public void setColourInstance(int c) {
hasColour = true; hasColour = true;
colour = c; colour = c;
} }
@Deprecated @Deprecated
public static void setColourStatic(int c) { public static void setColour(int c) {
instance().setColour(c); instance().setColourInstance(c);
} }
public void setBrightness(int b) { public void setBrightnessInstance(int b) {
hasBrightness = true; hasBrightness = true;
brightness = b; brightness = b;
} }
@Deprecated @Deprecated
public static void setBrightnessStatic(int b) { public static void setBrightness(int b) {
instance().setBrightness(b); instance().setBrightnessInstance(b);
} }
public void setBrightness(IBlockAccess world, int x, int y, int z) { public void setBrightnessInstance(IBlockAccess world, int x, int y, int z) {
setBrightness(world.getBlock(x, y, z).getMixedBrightnessForBlock(world, x, y, z)); setBrightnessInstance(world.getBlock(x, y, z).getMixedBrightnessForBlock(world, x, y, z));
} }
@Deprecated @Deprecated
public static void setBrightnessStatic(IBlockAccess world, int x, int y, int z) { public static void setBrightness(IBlockAccess world, int x, int y, int z) {
instance().setBrightness(world, x, y, z); instance().setBrightnessInstance(world, x, y, z);
} }
public void pullLightmap() { public static void pullLightmap() {
setBrightness((int) OpenGlHelper.lastBrightnessY << 16 | (int) OpenGlHelper.lastBrightnessX); instance().pullLightmapInstance();
} }
public void pushLightmap() { public void pullLightmapInstance() {
setBrightnessInstance((int) OpenGlHelper.lastBrightnessY << 16 | (int) OpenGlHelper.lastBrightnessX);
}
public static void pushLightmap() {
instance().pushLightmapInstance();
}
public void pushLightmapInstance() {
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness & 0xFFFF, brightness >>> 16); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness & 0xFFFF, brightness >>> 16);
} }
/** /**
* Compact helper for setting dynamic rendering context. Uses normals and doesn't compute lighting * Compact helper for setting dynamic rendering context. Uses normals and doesn't compute lighting
*/ */
public void setDynamic() { public static void setDynamic() {
instance().setDynamicInstance();
}
public void setDynamicInstance() {
useNormals = true; useNormals = true;
computeLighting = false; computeLighting = false;
} }
@@ -522,16 +609,16 @@ public class CCRenderState {
Minecraft.getMinecraft().renderEngine.bindTexture(texture); Minecraft.getMinecraft().renderEngine.bindTexture(texture);
} }
public void startDrawing() { public void startDrawingInstance() {
startDrawing(7); startDrawingInstance(7);
} }
@Deprecated @Deprecated
public static void startDrawingStatic() { public static void startDrawing() {
instance().startDrawing(); instance().startDrawingInstance();
} }
public void startDrawing(int mode) { public void startDrawingInstance(int mode) {
Tessellator.instance.startDrawing(mode); Tessellator.instance.startDrawing(mode);
if (hasColour) Tessellator.instance.setColorRGBA( if (hasColour) Tessellator.instance.setColorRGBA(
colour >>> 24, colour >>> 24,
@@ -542,11 +629,15 @@ public class CCRenderState {
} }
@Deprecated @Deprecated
public static void startDrawingStatic(int mode) { public static void startDrawing(int mode) {
instance().startDrawing(mode); instance().startDrawingInstance(mode);
} }
public void draw() { public static void draw() {
Tessellator.instance.draw();
}
public void drawInstance() {
Tessellator.instance.draw(); Tessellator.instance.draw();
} }
} }
@@ -23,7 +23,7 @@ public class ColourMultiplier implements CCRenderState.IVertexOperation {
@Override @Override
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
if (colour == -1) { if (colour == -1) {
state.setColour(-1); state.setColourInstance(-1);
return false; return false;
} }
@@ -33,7 +33,7 @@ public class ColourMultiplier implements CCRenderState.IVertexOperation {
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
state.setColour(ColourRGBA.multiply(state.colour, colour)); state.setColourInstance(ColourRGBA.multiply(state.colour, colour));
} }
@Override @Override
@@ -1,5 +1,7 @@
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;
@@ -11,6 +13,9 @@ 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;
@@ -19,6 +24,18 @@ 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;
@@ -90,8 +107,24 @@ 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();
@@ -106,13 +139,34 @@ 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;
effectRenderer.addEffect( EntityDigIconFX fx = new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon);
new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon).multiplyVelocity(0.2F) effectRenderer.addEffect(fx.applyColourMultiplier(colour).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
.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);
@@ -121,19 +175,20 @@ 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 x = bounds.min.x + (i + 0.5) * diff.x / density.x; double px = bounds.min.x + (i + 0.5) * diff.x / density.x;
double y = bounds.min.y + (j + 0.5) * diff.y / density.y; double py = bounds.min.y + (j + 0.5) * diff.y / density.y;
double z = bounds.min.z + (k + 0.5) * diff.z / density.z; double pz = bounds.min.z + (k + 0.5) * diff.z / density.z;
effectRenderer.addEffect( int iconSide = world.rand.nextInt(icons.length);
new EntityDigIconFX( EntityDigIconFX fx = new EntityDigIconFX(
world, world,
x, px,
y, py,
z, pz,
x - center.x, px - center.x,
y - center.y, py - center.y,
z - center.z, pz - center.z,
icons[world.rand.nextInt(icons.length)])); icons[iconSide]);
effectRenderer.addEffect(fx.applyColourMultiplier(colours[iconSide]));
} }
} }
} }
@@ -6,6 +6,8 @@ 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;
@@ -53,7 +55,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, 0xFFFFFF); fontRenderer.drawStringWithShadow(quantity, 0, 0, LocalizedColours.ITEM_QUANTITY_TEXT);
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);
@@ -13,6 +13,7 @@ import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon; import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraftforge.client.IItemRenderer; import net.minecraftforge.client.IItemRenderer;
import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.Fluid;
@@ -26,87 +27,130 @@ import codechicken.lib.vec.Vector3;
public class RenderUtils { public class RenderUtils {
static Vector3[] vectors = new Vector3[8]; static RenderItem uniformRenderItem;
static RenderItem uniformRenderItem = new RenderItem() {
public boolean shouldBob() {
return false;
}
};
static EntityItem entityItem; static EntityItem entityItem;
static { static {
for (int i = 0; i < vectors.length; i++) vectors[i] = new Vector3(); uniformRenderItem = new RenderItem() {
public boolean shouldBob() {
return false;
}
};
uniformRenderItem.setRenderManager(RenderManager.instance); uniformRenderItem.setRenderManager(RenderManager.instance);
entityItem = new EntityItem(null); entityItem = new EntityItem(null);
entityItem.hoverStart = 0; entityItem.hoverStart = 0;
} }
@Deprecated
public static void renderFluidQuad(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, IIcon icon, public static void renderFluidQuad(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, IIcon icon,
double res) { double res) {
// spotless:off
renderFluidQuad( renderFluidQuad(
point2, point2.x, point2.y, point2.z,
vectors[0].set(point4).subtract(point1), point4.x - point1.x, point4.y - point1.y, point4.z - point1.z,
vectors[1].set(point1).subtract(point2), point1.x - point2.x, point1.y - point2.y, point1.z - point2.z,
icon, icon, res);
res);
} }
/** /**
* Draws a tessellated quadrilateral bottom to top, left to right * Draws a tessellated quadrilateral bottom to top, left to right
* *
* @param base The bottom left corner of the quad * @param base The bottom left corner of the quad
* @param wide The bottom of the quad * @param wide The bottom of the quad
* @param high The left side of the quad * @param high The left side of the quad
* @param res Units per icon * @param res Units per icon
*/ */
@Deprecated
public static void renderFluidQuad(Vector3 base, Vector3 wide, Vector3 high, IIcon icon, double res) { public static void renderFluidQuad(Vector3 base, Vector3 wide, Vector3 high, IIcon icon, double res) {
Tessellator t = Tessellator.instance; renderFluidQuad(
base.x, base.y, base.z,
wide.x, wide.y, wide.z,
wide.x, wide.y, wide.z,
icon, res);
}
double u1 = icon.getMinU(); /**
* Draws a tessellated quadrilateral bottom to top, left to right
* <p>
* base : The bottom left corner of the quad
* <p>
* wide : The bottom of the quad
* <p>
* high : The left side of the quad
* <p>
* res : Units per icon
*/
public static void renderFluidQuad(
double baseX, double baseY, double baseZ,
double wideX, double wideY, double wideZ,
double highX, double highY, double highZ,
IIcon icon, double res) {
Tessellator tessellator = Tessellator.instance;
double u = icon.getMinU();
double du = icon.getMaxU() - icon.getMinU(); double du = icon.getMaxU() - icon.getMinU();
double v2 = icon.getMaxV(); double v = icon.getMinV();
double dv = icon.getMaxV() - icon.getMinV(); double dv = icon.getMaxV() - icon.getMinV();
double wlen = wide.mag(); double wideLen = Math.sqrt(wideX * wideX + wideY * wideY + wideZ * wideZ);
double hlen = high.mag(); double highLen = Math.sqrt(highX * highX + highY * highY + highZ * highZ);
double x = 0; double x = 0;
while (x < wlen) { while (x < wideLen) {
double rx = wlen - x; double rx = wideLen - x;
if (rx > res) rx = res; if (rx > res) rx = res;
double y = 0; double y = 0;
while (y < hlen) { while (y < highLen) {
double ry = hlen - y; double ry = highLen - y;
if (ry > res) ry = res; if (ry > res) ry = res;
Vector3 dx1 = vectors[2].set(wide).multiply(x / wlen); final double mult1 = x / wideLen;
Vector3 dx2 = vectors[3].set(wide).multiply((x + rx) / wlen); double dx1X = wideX * mult1;
Vector3 dy1 = vectors[4].set(high).multiply(y / hlen); double dx1Y = wideY * mult1;
Vector3 dy2 = vectors[5].set(high).multiply((y + ry) / hlen); double dx1Z = wideZ * mult1;
t.addVertexWithUV( final double mult2 = (x + rx) / wideLen;
base.x + dx1.x + dy2.x, double dx2X = wideX * mult2;
base.y + dx1.y + dy2.y, double dx2Y = wideY * mult2;
base.z + dx1.z + dy2.z, double dx2Z = wideZ * mult2;
u1,
v2 - ry / res * dv); final double mult3 = y / highLen;
t.addVertexWithUV(base.x + dx1.x + dy1.x, base.y + dx1.y + dy1.y, base.z + dx1.z + dy1.z, u1, v2); double dy1X = highX * mult3;
t.addVertexWithUV( double dy1Y = highY * mult3;
base.x + dx2.x + dy1.x, double dy1Z = highZ * mult3;
base.y + dx2.y + dy1.y,
base.z + dx2.z + dy1.z, final double mult4 = (y + ry) / highLen;
u1 + rx / res * du, double dy2X = highX * mult4;
v2); double dy2Y = highY * mult4;
t.addVertexWithUV( double dy2Z = highZ * mult4;
base.x + dx2.x + dy2.x,
base.y + dx2.y + dy2.y, tessellator.addVertexWithUV(
base.z + dx2.z + dy2.z, baseX + dx1X + dy2X,
u1 + rx / res * du, baseY + dx1Y + dy2Y,
v2 - ry / res * dv); baseZ + dx1Z + dy2Z,
u,
v + ry / res * dv);
tessellator.addVertexWithUV(
baseX + dx1X + dy1X,
baseY + dx1Y + dy1Y,
baseZ + dx1Z + dy1Z,
u,
v);
tessellator.addVertexWithUV(
baseX + dx2X + dy1X,
baseY + dx2Y + dy1Y,
baseZ + dx2Z + dy1Z,
u + rx / res * du,
v);
tessellator.addVertexWithUV(
baseX + dx2X + dy2X,
baseY + dx2Y + dy2Y,
baseZ + dx2Z + dy2Z,
u + rx / res * du,
v + ry / res * dv);
y += ry; y += ry;
} }
@@ -115,132 +159,130 @@ public class RenderUtils {
} }
} }
public static void translateToWorldCoords(Entity entity, float frame) { public static void translateToWorldCoords(Entity entity, float partialTicks) {
double interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * frame; double interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks;
double interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * frame; double interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks;
double interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * frame; double interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks;
GL11.glTranslated(-interpPosX, -interpPosY, -interpPosZ); GL11.glTranslated(-interpPosX, -interpPosY, -interpPosZ);
} }
public static void drawCuboidOutline(Cuboid6 c) { public static void drawCuboidOutline(Cuboid6 c) {
Tessellator var2 = Tessellator.instance; Tessellator tess = Tessellator.instance;
var2.startDrawing(3); tess.startDrawing(3);
var2.addVertex(c.min.x, c.min.y, c.min.z); tess.addVertex(c.min.x, c.min.y, c.min.z);
var2.addVertex(c.max.x, c.min.y, c.min.z); tess.addVertex(c.max.x, c.min.y, c.min.z);
var2.addVertex(c.max.x, c.min.y, c.max.z); tess.addVertex(c.max.x, c.min.y, c.max.z);
var2.addVertex(c.min.x, c.min.y, c.max.z); tess.addVertex(c.min.x, c.min.y, c.max.z);
var2.addVertex(c.min.x, c.min.y, c.min.z); tess.addVertex(c.min.x, c.min.y, c.min.z);
var2.draw(); tess.draw();
var2.startDrawing(3); tess.startDrawing(3);
var2.addVertex(c.min.x, c.max.y, c.min.z); tess.addVertex(c.min.x, c.max.y, c.min.z);
var2.addVertex(c.max.x, c.max.y, c.min.z); tess.addVertex(c.max.x, c.max.y, c.min.z);
var2.addVertex(c.max.x, c.max.y, c.max.z); tess.addVertex(c.max.x, c.max.y, c.max.z);
var2.addVertex(c.min.x, c.max.y, c.max.z); tess.addVertex(c.min.x, c.max.y, c.max.z);
var2.addVertex(c.min.x, c.max.y, c.min.z); tess.addVertex(c.min.x, c.max.y, c.min.z);
var2.draw(); tess.draw();
var2.startDrawing(1); tess.startDrawing(1);
var2.addVertex(c.min.x, c.min.y, c.min.z); tess.addVertex(c.min.x, c.min.y, c.min.z);
var2.addVertex(c.min.x, c.max.y, c.min.z); tess.addVertex(c.min.x, c.max.y, c.min.z);
var2.addVertex(c.max.x, c.min.y, c.min.z); tess.addVertex(c.max.x, c.min.y, c.min.z);
var2.addVertex(c.max.x, c.max.y, c.min.z); tess.addVertex(c.max.x, c.max.y, c.min.z);
var2.addVertex(c.max.x, c.min.y, c.max.z); tess.addVertex(c.max.x, c.min.y, c.max.z);
var2.addVertex(c.max.x, c.max.y, c.max.z); tess.addVertex(c.max.x, c.max.y, c.max.z);
var2.addVertex(c.min.x, c.min.y, c.max.z); tess.addVertex(c.min.x, c.min.y, c.max.z);
var2.addVertex(c.min.x, c.max.y, c.max.z); tess.addVertex(c.min.x, c.max.y, c.max.z);
var2.draw(); tess.draw();
} }
public static void renderFluidCuboid(CCRenderState state, Cuboid6 bound, IIcon tex, double res) { public static void renderFluidCuboid(CCRenderState state, Cuboid6 bound, IIcon tex, double res) {
renderFluidQuad( // bottom renderFluidCuboid(bound, tex, res);
new Vector3(bound.min.x, bound.min.y, bound.min.z),
new Vector3(bound.max.x, bound.min.y, bound.min.z),
new Vector3(bound.max.x, bound.min.y, bound.max.z),
new Vector3(bound.min.x, bound.min.y, bound.max.z),
tex,
res);
renderFluidQuad( // top
new Vector3(bound.min.x, bound.max.y, bound.min.z),
new Vector3(bound.min.x, bound.max.y, bound.max.z),
new Vector3(bound.max.x, bound.max.y, bound.max.z),
new Vector3(bound.max.x, bound.max.y, bound.min.z),
tex,
res);
renderFluidQuad( // -x
new Vector3(bound.min.x, bound.max.y, bound.min.z),
new Vector3(bound.min.x, bound.min.y, bound.min.z),
new Vector3(bound.min.x, bound.min.y, bound.max.z),
new Vector3(bound.min.x, bound.max.y, bound.max.z),
tex,
res);
renderFluidQuad( // +x
new Vector3(bound.max.x, bound.max.y, bound.max.z),
new Vector3(bound.max.x, bound.min.y, bound.max.z),
new Vector3(bound.max.x, bound.min.y, bound.min.z),
new Vector3(bound.max.x, bound.max.y, bound.min.z),
tex,
res);
renderFluidQuad( // -z
new Vector3(bound.max.x, bound.max.y, bound.min.z),
new Vector3(bound.max.x, bound.min.y, bound.min.z),
new Vector3(bound.min.x, bound.min.y, bound.min.z),
new Vector3(bound.min.x, bound.max.y, bound.min.z),
tex,
res);
renderFluidQuad( // +z
new Vector3(bound.min.x, bound.max.y, bound.max.z),
new Vector3(bound.min.x, bound.min.y, bound.max.z),
new Vector3(bound.max.x, bound.min.y, bound.max.z),
new Vector3(bound.max.x, bound.max.y, bound.max.z),
tex,
res);
} }
public static void renderFluidCuboid(Cuboid6 bound, IIcon tex, double res) { public static void renderFluidCuboid(Cuboid6 bound, IIcon tex, double res) {
renderFluidCuboid(CCRenderState.instance(), bound, tex, res); final double minX = bound.min.x;
final double minY = bound.min.y;
final double minZ = bound.min.z;
final double maxX = bound.max.x;
final double maxY = bound.max.y;
final double maxZ = bound.max.z;
renderFluidQuad( // bottom
minX, minY, minZ,
maxX - minX, 0, 0,
0, 0, maxZ - minZ,
tex, res);
renderFluidQuad( // top
minX, maxY, minZ,
0, 0, maxZ - minZ,
maxX - minX, 0, 0,
tex, res);
renderFluidQuad( // -x
minX, maxY, minZ,
0, minY - maxY, 0,
0, 0, maxZ - minZ,
tex, res);
renderFluidQuad( // +x
maxX, maxY, maxZ,
0, minY - maxY, 0,
0, 0, minZ - maxZ,
tex, res);
renderFluidQuad( // -z
maxX, maxY, minZ,
0, minY - maxY, 0,
minX - maxX, 0, 0,
tex, res);
renderFluidQuad( // +z
minX, maxY, maxZ,
0, minY - maxY, 0,
maxX - minX, 0, 0,
tex, res);
} }
// spotless:on
public static void renderBlockOverlaySide(int x, int y, int z, int side, double tx1, double tx2, double ty1, public static void renderBlockOverlaySide(int x, int y, int z, int side, double tx1, double tx2, double ty1,
double ty2) { double ty2) {
double[] points = new double[] { x - 0.009, x + 1.009, y - 0.009, y + 1.009, z - 0.009, z + 1.009 };
Tessellator tessellator = Tessellator.instance; Tessellator tessellator = Tessellator.instance;
final double minX = x - 0.009;
final double maxX = x + 1.009;
final double minY = y - 0.009;
final double maxY = y + 1.009;
final double minZ = z - 0.009;
final double maxZ = z + 1.009;
switch (side) { switch (side) {
case 0: case 0:
tessellator.addVertexWithUV(points[0], points[2], points[4], tx1, ty1); tessellator.addVertexWithUV(minX, minY, minZ, tx1, ty1);
tessellator.addVertexWithUV(points[1], points[2], points[4], tx2, ty1); tessellator.addVertexWithUV(maxX, minY, minZ, tx2, ty1);
tessellator.addVertexWithUV(points[1], points[2], points[5], tx2, ty2); tessellator.addVertexWithUV(maxX, minY, maxZ, tx2, ty2);
tessellator.addVertexWithUV(points[0], points[2], points[5], tx1, ty2); tessellator.addVertexWithUV(minX, minY, maxZ, tx1, ty2);
break; break;
case 1: case 1:
tessellator.addVertexWithUV(points[1], points[3], points[4], tx2, ty1); tessellator.addVertexWithUV(maxX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(points[0], points[3], points[4], tx1, ty1); tessellator.addVertexWithUV(minX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(points[0], points[3], points[5], tx1, ty2); tessellator.addVertexWithUV(minX, maxY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(points[1], points[3], points[5], tx2, ty2); tessellator.addVertexWithUV(maxX, maxY, maxZ, tx2, ty2);
break; break;
case 2: case 2:
tessellator.addVertexWithUV(points[0], points[3], points[4], tx2, ty1); tessellator.addVertexWithUV(minX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(points[1], points[3], points[4], tx1, ty1); tessellator.addVertexWithUV(maxX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(points[1], points[2], points[4], tx1, ty2); tessellator.addVertexWithUV(maxX, minY, minZ, tx1, ty2);
tessellator.addVertexWithUV(points[0], points[2], points[4], tx2, ty2); tessellator.addVertexWithUV(minX, minY, minZ, tx2, ty2);
break; break;
case 3: case 3:
tessellator.addVertexWithUV(points[1], points[3], points[5], tx2, ty1); tessellator.addVertexWithUV(maxX, maxY, maxZ, tx2, ty1);
tessellator.addVertexWithUV(points[0], points[3], points[5], tx1, ty1); tessellator.addVertexWithUV(minX, maxY, maxZ, tx1, ty1);
tessellator.addVertexWithUV(points[0], points[2], points[5], tx1, ty2); tessellator.addVertexWithUV(minX, minY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(points[1], points[2], points[5], tx2, ty2); tessellator.addVertexWithUV(maxX, minY, maxZ, tx2, ty2);
break; break;
case 4: case 4:
tessellator.addVertexWithUV(points[0], points[3], points[5], tx2, ty1); tessellator.addVertexWithUV(minX, maxY, maxZ, tx2, ty1);
tessellator.addVertexWithUV(points[0], points[3], points[4], tx1, ty1); tessellator.addVertexWithUV(minX, maxY, minZ, tx1, ty1);
tessellator.addVertexWithUV(points[0], points[2], points[4], tx1, ty2); tessellator.addVertexWithUV(minX, minY, minZ, tx1, ty2);
tessellator.addVertexWithUV(points[0], points[2], points[5], tx2, ty2); tessellator.addVertexWithUV(minX, minY, maxZ, tx2, ty2);
break; break;
case 5: case 5:
tessellator.addVertexWithUV(points[1], points[3], points[4], tx2, ty1); tessellator.addVertexWithUV(maxX, maxY, minZ, tx2, ty1);
tessellator.addVertexWithUV(points[1], points[3], points[5], tx1, ty1); tessellator.addVertexWithUV(maxX, maxY, maxZ, tx1, ty1);
tessellator.addVertexWithUV(points[1], points[2], points[5], tx1, ty2); tessellator.addVertexWithUV(maxX, minY, maxZ, tx1, ty2);
tessellator.addVertexWithUV(points[1], points[2], points[4], tx2, ty2); tessellator.addVertexWithUV(maxX, minY, minZ, tx2, ty2);
break; break;
} }
} }
@@ -259,7 +301,7 @@ public class RenderUtils {
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
Fluid fluid = stack.getFluid(); Fluid fluid = stack.getFluid();
state.setColour(fluid.getColor(stack) << 8 | alpha); state.setColourInstance(fluid.getColor(stack) << 8 | alpha);
TextureUtils.bindAtlas(fluid.getSpriteNumber()); TextureUtils.bindAtlas(fluid.getSpriteNumber());
return TextureUtils.safeIcon(fluid.getIcon(stack)); return TextureUtils.safeIcon(fluid.getIcon(stack));
} }
@@ -276,66 +318,76 @@ public class RenderUtils {
GL11.glDisable(GL11.GL_BLEND); GL11.glDisable(GL11.GL_BLEND);
} }
public static double fluidDensityToAlpha(double density) { public static double fluidDensityToAlpha(double d) {
return Math.pow(density, 0.4); return Math.pow(d, 0.4);
} }
/** /**
* Renders a fluid within a bounding box. If the fluid is a liquid it will render as a normal tank with height equal * Renders a fluid within a bounding box. If the fluid is a liquid it will render as a normal tank with height equal
* to density/bound.height. If the fluid is a gas, it will render the full box with an alpha equal to density. * to fillRate/bound.height. If the fluid is a gas, it will render the full box with an alpha equal to fillRate.
* Warning, bound will be mutated if the fluid is a liquid * Warning, bound will be mutated if the fluid is a liquid
* *
* @param stack The fluid to render. * @param stack The fluid to render.
* @param bound The box within which the fluid is contained. * @param bound The bounding box within which the fluid is contained.
* @param density The volume of fluid / the capacity of the tank. For gases this determines the alpha, for liquids * @param fillRate The volume of fluid / the capacity of the tank. This is a double between 0 and 1.
* this determines the height. * @param res The resolution to render at.
* @param res The resolution to render at.
*/ */
public static void renderFluidCuboid(CCRenderState state, FluidStack stack, Cuboid6 bound, double density, public static void renderFluidCuboid(CCRenderState state, FluidStack stack, Cuboid6 bound, double fillRate,
double res) { double res) {
if (!shouldRenderFluid(stack)) return; if (!shouldRenderFluid(stack)) return;
fillRate = MathHelper.clamp_double(fillRate, 0d, 1d);
int alpha = 255; int alpha = 255;
if (stack.getFluid().isGaseous()) alpha = (int) (fluidDensityToAlpha(density) * 255); if (stack.getFluid().isGaseous()) {
else bound.max.y = bound.min.y + (bound.max.y - bound.min.y) * density; alpha = (int) (fluidDensityToAlpha(fillRate) * 255);
} else {
bound.max.y = bound.min.y + (bound.max.y - bound.min.y) * fillRate;
}
IIcon tex = prepareFluidRender(state, stack, alpha); IIcon tex = prepareFluidRender(state, stack, alpha);
state.startDrawing(); state.startDrawingInstance();
renderFluidCuboid(state, bound, tex, res); renderFluidCuboid(bound, tex, res);
state.draw(); state.drawInstance();
postFluidRender(); postFluidRender();
} }
public static void renderFluidCuboid(FluidStack stack, Cuboid6 bound, double density, double res) { /**
renderFluidCuboid(CCRenderState.instance(), stack, bound, density, res); * Renders a fluid within a bounding box. If the fluid is a liquid it will render as a normal tank with height equal
* to fillRate/bound.height. If the fluid is a gas, it will render the full box with an alpha equal to fillRate.
* Warning, bound will be mutated if the fluid is a liquid
*
* @param stack The fluid to render.
* @param bound The bounding box within which the fluid is contained.
* @param fillRate The volume of fluid / the capacity of the tank. This is a double between 0 and 1.
* @param res The resolution to render at.
*/
public static void renderFluidCuboid(FluidStack stack, Cuboid6 bound, double fillRate, double res) {
renderFluidCuboid(CCRenderState.instance(), stack, bound, fillRate, res);
} }
public static void renderFluidGauge(CCRenderState state, FluidStack stack, Rectangle4i rect, double density, public static void renderFluidGauge(CCRenderState state, FluidStack stack, Rectangle4i rect, double fillRate,
double res) { double res) {
if (!shouldRenderFluid(stack)) return; if (!shouldRenderFluid(stack)) return;
fillRate = MathHelper.clamp_double(fillRate, 0d, 1d);
int alpha = 255; int alpha = 255;
if (stack.getFluid().isGaseous()) alpha = (int) (fluidDensityToAlpha(density) * 255); if (stack.getFluid().isGaseous()) {
else { alpha = (int) (fluidDensityToAlpha(fillRate) * 255);
int height = (int) (rect.h * density); } else {
int height = (int) (rect.h * fillRate);
rect.y += rect.h - height; rect.y += rect.h - height;
rect.h = height; rect.h = height;
} }
IIcon tex = prepareFluidRender(state, stack, alpha); IIcon tex = prepareFluidRender(state, stack, alpha);
state.startDrawing(); state.startDrawingInstance();
renderFluidQuad( renderFluidQuad(rect.x, rect.y + rect.h, 0, rect.w, 0, 0, 0, -rect.h, 0, tex, res);
new Vector3(rect.x, rect.y + rect.h, 0), state.drawInstance();
new Vector3(rect.w, 0, 0),
new Vector3(0, -rect.h, 0),
tex,
res);
state.draw();
postFluidRender(); postFluidRender();
} }
public static void renderFluidGauge(FluidStack stack, Rectangle4i rect, double density, double res) { public static void renderFluidGauge(FluidStack stack, Rectangle4i rect, double fillRate, double res) {
renderFluidGauge(CCRenderState.instance(), stack, rect, density, res); renderFluidGauge(CCRenderState.instance(), stack, rect, fillRate, res);
} }
/** /**
@@ -347,7 +399,7 @@ public class RenderUtils {
/** /**
* Renders items and blocks in the world at 0,0,0 with transformations that size them appropriately * Renders items and blocks in the world at 0,0,0 with transformations that size them appropriately
* *
* @param spin The spin angle of the item around the y axis in degrees * @param spin The spin angle of the item around the y axis in degrees
*/ */
public static void renderItemUniform(ItemStack item, double spin) { public static void renderItemUniform(ItemStack item, double spin) {
+29 -1
View File
@@ -19,7 +19,10 @@ public class Matrix4 extends Transformation implements Copyable<Matrix4> {
.asDoubleBuffer(); .asDoubleBuffer();
// m<row><column> // m<row><column>
public double m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; public double m00, m01, m02, m03;
public double m10, m11, m12, m13;
public double m20, m21, m22, m23;
public double m30, m31, m32, m33;
public Matrix4() { public Matrix4() {
m00 = m11 = m22 = m33 = 1; m00 = m11 = m22 = m33 = 1;
@@ -65,6 +68,15 @@ public class Matrix4 extends Transformation implements Copyable<Matrix4> {
return this; return this;
} }
public Matrix4 translate(double x, double y, double z) {
m03 += m00 * x + m01 * y + m02 * z;
m13 += m10 * x + m11 * y + m12 * z;
m23 += m20 * x + m21 * y + m22 * z;
m33 += m30 * x + m31 * y + m32 * z;
return this;
}
public Matrix4 scale(Vector3 vec) { public Matrix4 scale(Vector3 vec) {
m00 *= vec.x; m00 *= vec.x;
m10 *= vec.x; m10 *= vec.x;
@@ -82,6 +94,22 @@ public class Matrix4 extends Transformation implements Copyable<Matrix4> {
return this; return this;
} }
public Matrix4 scale(double scale) {
m00 *= scale;
m10 *= scale;
m20 *= scale;
m30 *= scale;
m01 *= scale;
m11 *= scale;
m21 *= scale;
m31 *= scale;
m02 *= scale;
m12 *= scale;
m22 *= scale;
m32 *= scale;
return this;
}
public Matrix4 rotate(double angle, Vector3 axis) { public Matrix4 rotate(double angle, Vector3 axis) {
double c = Math.cos(angle); double c = Math.cos(angle);
double s = Math.sin(angle); double s = Math.sin(angle);
@@ -14,14 +14,14 @@ public abstract class Transformation extends ITransformation<Vector3, Transforma
/** /**
* Applies this transformation to a normal (doesn't translate) * Applies this transformation to a normal (doesn't translate)
* *
* @param normal The normal to transform * @param normal The normal to transform
*/ */
public abstract void applyN(Vector3 normal); public abstract void applyN(Vector3 normal);
/** /**
* Applies this transformation to a matrix as a multiplication on the right hand side. * Applies this transformation to a matrix as a multiplication on the right hand side.
* *
* @param mat The matrix to combine this transformation with * @param mat The matrix to combine this transformation with
*/ */
public abstract void apply(Matrix4 mat); public abstract void apply(Matrix4 mat);
@@ -39,14 +39,14 @@ public abstract class Transformation extends ITransformation<Vector3, Transforma
@Override @Override
public boolean load(CCRenderState state) { public boolean load(CCRenderState state) {
state.pipeline.addRequirement(CCRenderState.normalAttrib.operationID()); state.pipeline.addRequirement(CCRenderState.normalAttrib().operationID());
return !isRedundant(); return !isRedundant();
} }
@Override @Override
public void operate(CCRenderState state) { public void operate(CCRenderState state) {
apply(state.vert.vec); apply(state.vert.vec);
if (CCRenderState.normalAttrib.active) applyN(state.normal); if (CCRenderState.normalAttrib().active) applyN(state.normal);
} }
@Override @Override
@@ -29,10 +29,10 @@ public class Vector3 implements Copyable<Vector3> {
public Vector3() {} public Vector3() {}
public Vector3(double d, double d1, double d2) { public Vector3(double x, double y, double z) {
x = d; this.x = x;
y = d1; this.y = y;
z = d2; this.z = z;
} }
public Vector3(Vector3 vec) { public Vector3(Vector3 vec) {
@@ -398,7 +398,7 @@ public class Vector3 implements Copyable<Vector3> {
/** /**
* Equals method with tolerance * Equals method with tolerance
* *
* @return true if this is equal to v within +-1E-5 * @return true if this is equal to v within +-1E-5
*/ */
public boolean equalsT(Vector3 v) { public boolean equalsT(Vector3 v) {
@@ -55,7 +55,10 @@ 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;
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()); if (event.world.isRemote) removeChunk(event.world, event.getChunk());
} }
@@ -79,17 +82,25 @@ public class WorldExtensionManager {
@SubscribeEvent @SubscribeEvent
public void onChunkWatch(Watch event) { public void onChunkWatch(Watch event) {
Chunk chunk = event.player.worldObj.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos); WorldExtension[] extensions = worldMap.get(event.player.worldObj);
for (WorldExtension extension : worldMap.get(event.player.worldObj))
extension.watchChunk(chunk, event.player); if (extensions != null) {
Chunk chunk = event.player.worldObj
.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos);
for (WorldExtension extension : extensions) extension.watchChunk(chunk, event.player);
}
} }
@SubscribeEvent @SubscribeEvent
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public void onChunkUnWatch(UnWatch event) { public void onChunkUnWatch(UnWatch event) {
Chunk chunk = event.player.worldObj.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos); WorldExtension[] extensions = worldMap.get(event.player.worldObj);
for (WorldExtension extension : worldMap.get(event.player.worldObj))
extension.unwatchChunk(chunk, event.player); if (extensions != null) {
Chunk chunk = event.player.worldObj
.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos);
for (WorldExtension extension : extensions) extension.unwatchChunk(chunk, event.player);
}
} }
@SubscribeEvent @SubscribeEvent
@@ -110,7 +121,7 @@ public class WorldExtensionManager {
} }
private static boolean initialised; private static boolean initialised;
private static ArrayList<WorldExtensionInstantiator> extensionIntialisers = new ArrayList<WorldExtensionInstantiator>(); private static ArrayList<WorldExtensionInstantiator> extensionIntialisers = new ArrayList<>();
public static void registerWorldExtension(WorldExtensionInstantiator init) { public static void registerWorldExtension(WorldExtensionInstantiator init) {
if (!initialised) init(); if (!initialised) init();
@@ -125,7 +136,7 @@ public class WorldExtensionManager {
FMLCommonHandler.instance().bus().register(new WorldExtensionEventHandler()); FMLCommonHandler.instance().bus().register(new WorldExtensionEventHandler());
} }
private static HashMap<World, WorldExtension[]> worldMap = new HashMap<World, WorldExtension[]>(); private static HashMap<World, WorldExtension[]> worldMap = new HashMap<>();
private static void onWorldLoad(World world) { private static void onWorldLoad(World world) {
WorldExtension[] extensions = new WorldExtension[extensionIntialisers.size()]; WorldExtension[] extensions = new WorldExtension[extensionIntialisers.size()];
@@ -4,13 +4,15 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.PrintStream; import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.commons.RemappingClassAdapter; import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import com.google.common.base.Function; import com.google.common.base.Function;
@@ -181,7 +183,40 @@ public class ObfuscationRun implements ILogStreams {
public void remap(ClassNode cnode, ClassVisitor cv) { public void remap(ClassNode cnode, ClassVisitor cv) {
cstMappper.transform(cnode); cstMappper.transform(cnode);
cnode.accept(new RemappingClassAdapter(cv, obfMapper)); cnode.accept(newRemapper(cv, obfMapper));
}
private static ClassVisitor newRemapper(ClassVisitor visitor, Remapper remapper) {
try {
return theConstructor.newInstance(visitor, remapper);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static final Constructor<? extends ClassVisitor> theConstructor;
static {
Class<? extends ClassVisitor> remapperClass;
try {
// noinspection unchecked
remapperClass = (Class<? extends ClassVisitor>) Class.forName("org.objectweb.asm.commons.ClassRemapper");
} catch (ClassNotFoundException e) {
try {
// noinspection unchecked
remapperClass = (Class<? extends ClassVisitor>) Class
.forName("org.objectweb.asm.commons.RemappingClassAdapter");
} catch (ClassNotFoundException ex) {
RuntimeException err = new RuntimeException(ex);
err.addSuppressed(e);
throw err;
}
}
try {
theConstructor = remapperClass.getConstructor(ClassVisitor.class, Remapper.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
} }
public static List<String> getParents(ClassNode cnode) { public static List<String> getParents(ClassNode cnode) {
@@ -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,218 @@
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();
}
}
}
@@ -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());
}
}