Compare commits

..
Author SHA1 Message Date
ah-OOG-ah 4b2d57a32d Update model API (#842)
* Bump GTNHLib

* Change test block's properties

This makes it easier to test the block overlay

* Update VanillaModels.java

# Conflicts:
#	src/main/java/com/gtnewhorizons/angelica/models/VanillaModels.java

* Revert rename

* Rename models

* Sledgehammer out removed abstractions

* Switch to ModelISBRH

* Replace old models

With a single new and interesting model!

* Adapt to GTNHLib

* Remove dead code

* Fix JSON model detection + remove dead code

* Remove direct model renderer

* yet more yote

* Remove ColorABGR

* Remove unused line methods

* Yeet color fully

* publicize nextLong

Technically you don't *need* this on Java 8, but it silences a warning and doesn't break anything so 🤷

* yoten

* Register Angelica for automatic texture loading

* Remove TX-Loader dep

* Make the list mutable

* Remove rebase mistake

* Bump BS + deps
2025-11-06 17:40:45 +00:00
Darren Eberly 06bf71126f Add several missing built-in uniforms (#1101) 2025-11-05 15:38:57 -08:00
Darren Eberly 4c0f2f2685 Disable hardcoded custom uniforms by default 2025-11-05 12:25:18 -08:00
Jason Mitchell 78334717e7 Fix TAA:
* Return an immutable view of the underlying camera position to avoid mutating it
* Update Camera Uniforms to utilize the view instead of mutating in place
2025-11-05 09:59:47 -08:00
MalTeeez a77765a2c9 Allow avoiding other mods spash screen handlers (#1080) 2025-10-31 16:06:28 -07:00
sisyphussy 1a9fe45ee9 Add VAO test (#1089) 2025-10-31 10:10:01 -07:00
Jason Mitchell 21ef5905c8 Roll back some of the hacks (#1085) 2025-10-30 20:50:43 -07:00
ABKQPO 5b67eaa474 Quick fix PBR Resourcepack loader error (#1043) 2025-10-29 08:01:52 -07:00
SuperSoupr c9d59c3b90 Allow overriding of blend function in batching font renderer (#1075) 2025-10-29 07:59:27 -07:00
Death Fuel fa7ff74fec make an exception for the splash screen (#1069) 2025-10-29 07:57:41 -07:00
Kotmatross a34a3ebd39 Fix missing depth buffer writing before GUI rendering (#1073) 2025-10-29 07:56:45 -07:00
Nikita Manchuk 2e7476d287 render: set frustum position before BE culling (#1077) 2025-10-29 07:23:25 -07:00
Death Fuel 3244071012 font shader changes (#1071) 2025-10-24 14:44:32 -07:00
DarkShadow44 7035dbde02 Rework ANGELICA_VERSION into an integer (#873) 2025-10-22 01:19:15 +00:00
sisyphussy 97ae04c2fa Fix CME (#1008) 2025-10-20 14:38:30 -07:00
sisyphussy e7fa7f62d0 Fix framebuffer depth not clearing (#1051) 2025-10-20 14:36:02 -07:00
Wilhelm Schuster ea39712749 Add null check for zoom reset (#1061) 2025-10-20 14:35:29 -07:00
Death Fuel 66dd3eaa94 Custom/Smooth Font follow-up (#1040) 2025-10-20 14:35:06 -07:00
ah-OOG-ah 270697cd10 Refactor vendor detection (#1049)
* Refactor vendor detection

Only one vendor can be active at any given time, so make it an enum.

* lowercase vendor string
2025-10-13 15:48:21 -07:00
sisyphussy 2b53a851ef Fix intel build tests (#1048) 2025-10-13 10:31:48 -07:00
Jason Mitchell 34b6efde7b Maybe fix TAA (#1047) 2025-10-12 17:40:41 -07:00
Jason Mitchell 86edb721da GLSM updates (#1046) 2025-10-12 14:51:56 -07:00
Kotmatross 39c5f4bf7b Fix missing GL20 in cstPoolParser (#1042) 2025-10-09 12:26:55 -04:00
Death Fuel 5dc54bb1da remove implicit float cast (#1037) 2025-10-05 05:03:50 -07:00
Death Fuel 7924769ab1 Custom/Smooth Font (#1017) 2025-10-04 11:20:38 -07:00
sisyphussy 8afe2e2ccf Optimize in-world item rendering (#1034) 2025-10-01 09:05:06 -07:00
Wilhelm Schuster e8dd60e088 Check if module is enabled before resetting zoom (#1032) 2025-09-28 09:01:23 -07:00
Kotmatross 9a621405ba Fix Tileentities not casting shadows (#1024) 2025-09-26 12:04:46 -07:00
slprime 45a677030d Disabled force unicode (#1029) 2025-09-26 09:59:08 -07:00
Wilhelm Schuster 4d531022d2 Improve zoom key handling (reset on GUI enter and compatibility with mouse buttons) (#1016) 2025-09-26 09:41:08 -07:00
Pedro Ortiz Bledow 48ba2160ad Fix fast clouds rendering with setting off after game restart (#1028) 2025-09-26 13:04:47 +02:00
Eldrinn-Elantey 334ec81f9b Font rendering: configurable shadow & glyph fix (#1011) 2025-09-03 08:53:39 -07:00
Alexdoru 86c726e50b Revert "Port FIX_BLOCK_CRACK from FalseTweaks (#999)" (#1005)
This reverts commit dce5013ffb.
2025-08-16 19:09:18 -07:00
AlexdoruandJason Mitchell e6d778dad3 ASM Housekeeping (#1003)
Co-authored-by: Jason Mitchell <[email protected]>
2025-08-16 17:29:50 -07:00
Kotmatross dce5013ffb Port FIX_BLOCK_CRACK from FalseTweaks (#999) 2025-08-16 22:10:55 +00:00
glowredman 6eef63619c Update BatchingFontRenderer.java (#1002) 2025-08-10 22:27:29 +02:00
Alexdoru a8153fa0f7 remove access transformer to invoke EntityRenderer#getNightVisionBrightness (#1001) 2025-08-10 16:31:30 +00:00
Niclas Klugmann 49c04cf776 German translation (#997) 2025-07-27 20:02:26 +07:00
Alexdoru 85b0277a66 use EnumChatFormatting and not Realms enum to avoid crash (#996) 2025-07-21 10:59:04 +02:00
Alexdoru 0525b31262 Cache Tessellator#instance in local var (#995) 2025-07-18 12:44:03 -07:00
boubou19 fdb2053c50 Speed up BlockError event registration (#993) 2025-07-17 20:38:13 +02:00
Alexdoru 31abbcce32 Convert to new IMixins and MixinsBuilder system (#991) 2025-07-15 08:54:06 -07:00
063b2242e8 Initial FluidLogged compat (#985)
* Initial FluidLogged compat

* FluidLogged works(probably mostly)

* Make FluidLogged optional

* Remove a leftover print

* Fix fluidlogged fluids rendering incorrectly with shaders on

* Rmemove unused code

* Cleanup unused imports

---------

Co-authored-by: embeddedt <[email protected]>
Co-authored-by: Jason Mitchell <[email protected]>
2025-07-09 10:31:45 -04:00
Rad 577f905dc5 Update README.MD (#981) 2025-07-06 13:56:20 -07:00
Jason MitchellandAlexdoru 071d2d4a39 Add a guard to only init once (#984)
Co-authored-by: Alexdoru <[email protected]>
2025-07-01 10:33:49 -07:00
Alexdoru 891715b113 limit the amount of dropped items rendered exactly to the config value (#973) 2025-06-16 07:55:28 +02:00
jss2a98aj 30a35ef25e Misc. CTM resource tweaks 2025-06-13 18:04:31 -07:00
jss2a98aj 33453b3b8c Fix crash with OpenAutism
Brandyyn says this fixes the crash but I have not tested it myself.
2025-06-13 18:04:31 -07:00
jss2a98aj 62e699f6d0 Remove unneeded debug print 2025-06-13 18:04:31 -07:00
Jason Mitchell f107d173b7 Stop loading HoloInventory too early in the compat class, which was causing fake fluids to not render properly. 2025-06-09 14:22:12 -07:00
glowredman d0948b36f3 No longer use Reflection for SplashProgress.fontRenderer (#966)
* Access SplashProgress font renderer via mixins

* Use AT
2025-06-07 12:07:14 +02:00
YannickMG ae1211f280 Battlegear 2 for Backhand compat 2025-05-31 08:01:28 -07:00
David Lindström 3d76cb3073 Use instanceof instead of direct cast to protect against scenarios where mods like FMB use the same rendering code for world, inventory and preview, where the two latter happens in passes that are not rendered using a CapturingTesselator 2025-05-27 14:10:07 -07:00
kotmatross28729 45fb120223 Fix COLOR_BUFFER_BIT leak in ShaderPackScreen 2025-05-25 08:25:59 -07:00
Jason Mitchell d7a196eb5c Use invokeExact instead of invoke 2025-05-25 08:25:05 -07:00
Jason Mitchell 4d53e2af9b Holoinventory compat via reflection 2025-05-25 08:25:05 -07:00
Jason Mitchell cb6336a365 Backhand Compat via Reflection 2025-05-25 08:25:05 -07:00
Jason Mitchell 5095021e0e Capture shader outline 2025-05-16 18:08:27 -07:00
Caedis f88ecbf68d Replace Baubles with Baubles Expanded (#949) 2025-05-13 10:26:50 -04:00
Darren Eberly 7da9b9eb1c Compat for security craft (#948) 2025-05-12 23:34:15 -04:00
Pxx500 02c394108f enable ctm, disable everything unnecessary 2025-05-05 12:44:46 -07:00
Fabian Maurer 720aaca1b7 Add functionality to fake blockId for shaders 2025-04-24 14:11:24 -07:00
Midnight145 1e71b1c4d8 liteloader docs update 2025-04-17 17:41:58 -07:00
くぁーりぃ df4c423e93 fix config load (#914) 2025-04-07 19:10:39 +00:00
JuliaandJason Mitchell 78e0074e5a Apply linear filter to unifont glyphs at odd scale factors (#907)
* Apply linear filter to unifont glyphs at odd scale factors

* Prevent glyph texture bleeding by adjusting UV coordinates with a slight offset

* Tiny bit of tuning

---------

Co-authored-by: Jason Mitchell <[email protected]>
2025-04-07 17:37:46 +02:00
くぁーりぃ 0350ea2cb6 fix stencil buffer does not exist (#912) 2025-03-20 07:53:27 -07:00
Darren Eberly bed724ba59 Support for custom uniforms (#902)
* wip backport of custom uniforms

* Functioning custom uniforms maybe?

* Workon separating dynamic/non-dynamic uniforms and making variables available to custom uniforms (currently the JVM dies horribly)

* wip

* It no longer crashes horrifically

* star imports
2025-03-18 10:04:31 -07:00
Jason Mitchell 6fa973dbc8 Adds two new overloads (#906) 2025-03-13 08:24:57 -07:00
RecursivePineapple c4ad460f34 Update (#899) 2025-03-09 23:04:46 +00:00
Glease 2a8404c6c2 add an API to retrieve world instance from WorldSlice (#897) 2025-03-08 23:00:58 -08:00
ah-OOG-ah 108db9031b Add an option to disable terrain fog (#896)
* Add an option to disable terrain fog

* forgot a remap = false
2025-03-09 05:38:30 +00:00
amyandJason Mitchell d6d8a5dad6 Catch CancellationException while building chunks (#889)
Co-authored-by: Jason Mitchell <[email protected]>
2025-03-08 20:45:10 -08:00
RecursivePineappleandJason Mitchell ad2b73edf9 Fix hologlasses crash (#891)
Co-authored-by: Jason Mitchell <[email protected]>
2025-03-08 20:44:36 -08:00
glowredman bb5526598e Various Mixin-related Fixes (#890)
* Update deps

* Remove dead code

* Add missing `@Override` annotations

* Fix generics

* Add `serialVersionUID`s

* Address deprecation warnings

* Use `@WrapWithCondition` v2

* Misc cleanup

* Use AccessTransformers if possible

* Fix mixin conflicts

* Add `@Mutable` annotations

* Fix injection targets

* Update deps
2025-03-08 20:41:47 -08:00
RecursivePineapple 611bbf02c8 Optimize NEI rendering 2025-02-28 11:32:26 -08:00
OBlankenship ca16036604 Update GTNHLib dependency version in README (#885) 2025-02-27 09:22:04 +01:00
Caedis f118211257 Fix IC2 universal cell rendering w/offhand (#876) 2025-02-18 10:49:29 -08:00
RecursivePineapple 81d03ea552 Fix PBR specular & normal textures getting ignored (#874)
* Fix PBR textures not loading

- Stack was being overfilled by MemoryStack
- Added tons of logging for ignored important exceptions
- Added missing interface on mixin (should it be moved to gtnhlib?)

* Move PBR debug to angelica config

* Free malloc'd memory
2025-02-18 18:34:38 +00:00
RecursivePineapple 67c8628aeb Fix a handful of broken animations (#868)
* Fix warning

* Improve animated sprite tracking

* Fix NPE in animation code

* Fix animations for abnormally rendered items

* Swap Stack for ArrayDeque
2025-02-18 18:28:54 +00:00
embeddedt f9b9873637 Avoid checking frustum visibility of infinite bounding boxes 2025-02-18 10:24:09 -08:00
DarkShadow44 c66bf8658e Fix issue with dynamic lights world mixin not respecting opaque blocks (#872) 2025-02-17 10:36:34 -06:00
DarkShadow44 052824017d Fix crash when keycode is negative (#871) 2025-02-16 21:43:15 +00:00
Caedis 6a5d914a43 add offhand support via Backhand (up required version to 1.6.9) (#859) 2025-02-04 12:41:47 -05:00
Calvin Reveraster 208373deaa Fix any dimension without a sky getting flagged as the nether (#857)
* fix crash when connected textures are disabled, but custom colors are enabled.

* Dimension Check Fix

Dimension check set any dimension with no sky as the nether; this includes the end, and several custom dims such as Witchery's mirror dim, etc.
2025-02-01 09:18:29 -08:00
TotallyNotOndre f22a3f612b Fix slider dragging in iris options (#858) 2025-02-01 09:17:26 -08:00
TotallyNotOndre 9ff1015d19 Fix shadow render distance reset to 32 (#856) 2025-01-31 11:18:09 -08:00
Calvin Reveraster 0023fe3da2 fix crash when connected textures are disabled, but custom colors are enabled. (#855) 2025-01-29 08:19:50 -08:00
Raven Szewczyk 7e2399714c Use GTNHLib MemoryUtils instead of the builtin compat ones (#853) 2025-01-26 11:20:43 -08:00
George Paton 9029182353 Fix Hodgepodge animation speedup breaking animated textures (#850) 2025-01-25 15:34:51 -08:00
Caedis e6039691b5 Remove forced even unicode gui scaling (#840) 2025-01-15 14:39:21 -06:00
paulzzh 5db1edba33 fix MC-76356 Bold unicode characters appear doubled (#834) 2025-01-05 15:24:16 +00:00
Martin Robertz d450461726 update bs + deps (#823) 2025-01-04 13:50:46 -08:00
embeddedt 95f8afc0f3 Remove use of empty build tasks, submit empty data directly to result queue (#821) 2024-12-27 21:39:05 -08:00
Caedis 19635e20ec Disable optimizeInWorldItemRendering by default for now (#820) 2024-12-25 23:25:42 -06:00
BucketBrigade ead4e11376 Expose texture updates to API (#805)
* Expose texture updates to API

* Expose for blocks as well

* Fix mistake

* Fixed API
2024-12-19 19:04:24 -08:00
FM Bellomy b0ceb5306a the most boring typo PR of all time (#810) 2024-12-19 13:25:56 -08:00
embeddedt 99a81369f0 fix origin weirdness (#799) 2024-12-17 10:09:22 -08:00
nshepperd e2ee822948 Only clear the translucency sorting render queue when we're not rendering shadows. (#798) 2024-12-16 13:30:20 -08:00
nshepperd 9fe7e800f6 Remove broken reflection in ChunkBufferSorter. (#797) 2024-12-16 21:11:05 +00:00
embeddedt debfcdfda0 Properly prevent block face culling when TS is active (#796) 2024-12-16 13:07:00 -08:00
embeddedt df662188f4 Fix terrain rendering incorrectly when far from origin (#790) 2024-12-13 18:14:00 +00:00
embeddedt 842916405f Fix rebuild check being wrong when shaders are on (#788) 2024-12-13 16:55:58 +00:00
embeddedt 09afe81290 Fix chunk render manager state not swapping, causing lower render distance with shaders on 2024-12-13 08:52:32 -08:00
Caedis 292809d8e8 Actually fix xaero's crosshair (#781) 2024-12-11 21:50:04 +00:00
Caedis 47503ce254 Fix invisible crosshair with Xaero's Minimap (#775) 2024-12-11 16:32:49 -05:00
ah-OOG-ah f738adbdef Frametime exploration (#742)
* Sleep before swap

* Add a config

* I think this is slightly less jittery
2024-12-06 09:09:26 -08:00
Alexdoru 70d15f2383 Set render distance for mob inside mod spawner to 16 blocks (#761)
* set render distance for mob inside mod spawner to 16 blocks

* add config in angelica

* add slider in video setting menu
2024-12-02 15:20:44 -10:00
kurrycatandDarren Eberly d66b5577f4 add missing GL calls in GLStateManager (#767)
Co-authored-by: Darren Eberly <[email protected]>
2024-12-02 09:12:44 -05:00
ah-OOG-ah e293f12fb8 Refresh Notfine options on world load (#760) 2024-12-01 00:03:21 +00:00
kurrycat b69eca6749 add missing Pipeline::syncProgram for glCallList (#762) 2024-11-30 10:38:00 -10:00
Jason Mitchell 03342f08c6 Update README.MD 2024-11-27 09:03:01 -08:00
ah-OOG-ah b85614f983 Null check (#756) 2024-11-26 22:19:05 +00:00
456 changed files with 10688 additions and 7577 deletions

No files matched your search

-2
View File
@@ -36,5 +36,3 @@ addon.local.gradle
addon.local.gradle.kts
addon.late.local.gradle
addon.late.local.gradle.kts
/*.csv
!gl_functions.csv
+9 -8
View File
@@ -7,40 +7,41 @@
* It's a cute angle -mitchej123
* It's the angel that pulls you out of OptiFine's hell -Roadhog360
## **Beta Quality - Here there be Dragons**
* Shaders sneak preview - Please don't report issues yet, but please do submit fixes
## **Beta Quality - Here there be Dragons**
* Shaders - Beta
* Complementary & BSL mostly function, but have some bugs
* Sildurs, Seus, and any other shader probably doesn't work
# Required Dependencies
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.14
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.4.2
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.19
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.7.0
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
# Known (temporary) Incompatibilities
* Forge Relocation - Currently non Thread Safe; safe if not in use
* ProjectRed Frames - Currently non Thread Safe; safe if not in use'
* ProjectRed Frames - Currently non Thread Safe; safe if not in use
# Potential Incompatibilities
* EndlessIDs - Compat commits have been made, might work, might not - not fully tested
# Known Incompatibilities
* FalseTweaks - Not currently planned (Looking for volunteers) [Might work if you effectively disable most of the mod]
* SmoothFont - Not currently planned (Looking for volunteers)
* RPLE - Not currently planned (Looking for volunteers)
* LiteLoader - Install [LiteloaderLoader](https://github.com/Midnight145/LiteloaderLoader)
# Permanent Incompatibilities
* OptiFine [Disabled, won't fix]
* FastCraft [Disabled, won't fix]
* BetterFPS [Disabled, won't fix]
* OptimizationsAndTweaks [Disabled, won't fix]
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - use 1.2.2+
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - Use 1.2.2+
* [LWJGL3ify 1.5.4 and below](https://github.com/GTNewHorizons/lwjgl3ify/releases/) - Use the latest version available, 1.5.7+
* [ArchaicFix 0.6.2 and below](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) - Use 0.7.0 or above
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 0.4.5 or above
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)
* SmoothFont - Equivalent functionality added
# Credits
* ShadersMod
@@ -64,7 +65,7 @@
* Makamys for [Neodymium](https://github.com/makamys/Neodymium) - Various Tessellator -> Quad functionality taken under the LGPL 3
* MCPatcherForge
* Original [MCPatcher](https://bitbucket.org/prupe/mcpatcher/src/master/): Paul Rupe
* [Forge version](https://bitbucket.org/prupe/mcpatcher/src/master/): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
* [Forge version](https://github.com/mist475/MCPatcherForge): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
* HUDCaching
* Created by [Moulberry](https://github.com/Moulberry/MCHUDCaching), backported from [Patcher](https://github.com/Sk1erLLC/Patcher) mod by Alexdoru under the CC-BY-SA-NC 4.0 license
* Angelica
+2
View File
@@ -4,6 +4,8 @@ plugins {
minecraft {
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
//extraRunJvmArguments.add("-Dangelica.redirectorLogspam=true")
}
tasks.test {
+20 -25
View File
@@ -49,19 +49,16 @@ configurations {
}
dependencies {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.5.78")
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.11")
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:0.4.0") { transitive = false }
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
compileOnly("org.projectlombok:lombok:1.18.22") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.22")
// Use modern models+textures to inject QuadProviders
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.7.2:dev')
compileOnly("org.projectlombok:lombok:1.18.34") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.34")
// Iris Shaders
compileOnly('org.jetbrains:annotations:24.0.1')
api("com.github.GTNewHorizons:GTNHLib:0.5.20:dev")
compileOnly('org.jetbrains:annotations:26.0.2')
api("com.github.GTNewHorizons:GTNHLib:0.8.0:dev")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-4.g6b42bca") {
exclude module: "antlr4" // we only want to shadow the runtime module
@@ -70,21 +67,20 @@ dependencies {
compileOnly "org.apache.ant:ant:1.8.2"
// Because who doesn't want NEI
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.6.45-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.3.10:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.31-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
// Notfine Deps
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles:1.0.4:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.6.35:dev") { transitive = false }
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.13:dev") { transitive = false }
compileOnly(rfg.deobf('curse.maven:witchery-69673:2234410'))
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.12.14-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.7.5:dev")
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.6-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.8.9:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.6.4:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.4:dev")
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.1:dev") { transitive = false }
compileOnly(rfg.deobf('maven.modrinth:backhand:1.4.1')) { transitive = false }
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { transitive = false }
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { transitive = false }
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { transitive = false }
@@ -94,15 +90,16 @@ dependencies {
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.6:dev") // Mixin Version
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev") // Mixin Version
compileOnly("com.github.GTNewHorizons:NotEnoughIds-Legacy:1.4.7:dev") // ASM Version
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly("com.github.GTNewHorizons:HoloInventory:2.4.13-GTNH:dev")
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
runtimeOnlyNonPublishable(rfg.deobf("CoreTweaks:CoreTweaks:0.3.3.2"))
// For testing alternative splash screens, can be sourced from https://github.com/MalTeeez/ModernSplash/releases
//runtimeOnly(rfg.deobf(files("dependencies/modernsplash-1.7.10-1.2.2-dev.jar")))
// Hodgepodge
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
@@ -111,12 +108,10 @@ dependencies {
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.0-GTNH:dev")
// Tests
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation(platform('org.junit:junit-bom:5.11.4'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation("org.mockito:mockito-core:3.+")
testImplementation("org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209")
devOnlyNonPublishable("com.github.javaparser:javaparser-symbol-solver-core:3.26.2") {
exclude module: "guava"
}
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
}
-2084
View File
File diff suppressed because it is too large. Load diff
+11 -3
View File
@@ -14,7 +14,7 @@ modId = angelica
modGroup = com.gtnewhorizons.angelica
# 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
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
@@ -96,7 +96,9 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
mixinsPackage = mixins
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -151,7 +153,7 @@ modrinthProjectId = angelica
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: 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 = required-project\:gtnhlib
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
@@ -172,6 +174,12 @@ curseForgeRelations = requiredDependency\:gtnhlib
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+2 -3
View File
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || 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.
MAX_FD=maximum
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# 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.
# * 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.
+7
View File
@@ -8,6 +8,13 @@ repositories {
includeGroup 'com.falsepattern'
}
}
maven {
name = 'mega'
url = 'https://mvn.falsepattern.com/gtmega_releases/'
content {
includeGroup 'mega'
}
}
exclusiveContent {
forRepository {
ivy {
+1 -1
View File
@@ -16,5 +16,5 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.48'
}
-120
View File
@@ -1,120 +0,0 @@
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Used to generate gl_functions.csv. Which contains a mapping of OpenGL functions provided by LWJGL
* corresponding to the earliest GL version they appear in.
*/
public class GLFunctionParser {
final static String glPath = "util/gl"; // Path to LWJGL OpenGL bindings source code
public static void main(String[] args) throws FileNotFoundException {
HashMap<String, Set<String>> byFunction = new HashMap<>();
File dir = new File(glPath);
for (File file : dir.listFiles()) {
final String filename = file.getName();
if (filename.endsWith(".java") && filename.startsWith("GL") && Character.isDigit(filename.charAt(2))) {
Path p = Paths.get(filename);
String parsedFile = removeFileExtension(p.getFileName().toString());
CompilationUnit cu = StaticJavaParser.parse(file);
cu.accept(new VoidVisitorWithDefaults<>() {
@Override
public void defaultAction(Node n, Object arg) {
for( Node child : n.getChildNodes()) {
if(child instanceof ClassOrInterfaceDeclaration cd) {
for (MethodDeclaration md : cd.getMethods()) {
String methodName = md.getNameAsString() + "(";
for (Parameter p : md.getParameters()) {
methodName = methodName + p.getTypeAsString();
}
methodName = methodName + ")";
if(methodName.startsWith("n")) continue;
Set<String> functionSet = byFunction.computeIfAbsent(methodName, k -> new HashSet<>());
functionSet.add(parsedFile);
}
}
}
}
}, null);
}
}
Map<String, String> functionVersionMap = new HashMap<>();
for(String function : byFunction.keySet()) {
Set<String> versionsForFunction = byFunction.get(function);
int lowestInt = 46;
String lowestString = "GL46";
for(String version : versionsForFunction) {
int i = Integer.parseInt(version.substring(2, 4));
if (i < lowestInt) {
lowestInt = i;
lowestString = version;
}
}
functionVersionMap.put(function, lowestString);
}
List<String[]> outputLines = new ArrayList<>();
for (Map.Entry<String, String> entry : functionVersionMap.entrySet()) {
outputLines.add(new String[] {entry.getKey(), entry.getValue()});
}
File output = new File("output.csv");
try (PrintWriter writer = new PrintWriter(output)) {
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
}
}
public static String convertToCSV(String[] data) {
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
}
public static String escapeSpecialCharacters(String data) {
if (data == null) {
throw new IllegalArgumentException("Input data cannot be null");
}
String escapedData = data.replaceAll("\\R", " ");
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
data = data.replace("\"", "\"\"");
escapedData = "\"" + data + "\"";
}
return escapedData;
}
public static String removeFileExtension(String filename) {
if (filename == null || filename.isEmpty()) {
return filename;
}
String extPattern = "(?<!^)[.]" + "[^.]*$";
return filename.replaceAll(extPattern, "");
}
}
-135
View File
@@ -1,135 +0,0 @@
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Used to compare OpenGL functions from gl_functions.csv to current GLStateManager
* source code to determine what functions have an implementation in GLSM, and what ones don't.
* Only considers functions which appear in GL 2.1 and lower
*/
public class GLSMFunctionComparison {
/**
* If set to true, then the outputted CSV will show functions which do have an implementation in GLSM
*/
final static boolean INCLUDE_TRUE = false;
final static String glFunctionsCsvPath = "gl_functions.csv";
final static String glsmPath = "src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java";
public static Map<String, String> parseGLFunctions() {
Map<String, String> map = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(glFunctionsCsvPath))) {
String line;
while ((line = br.readLine()) != null) {
String[] values = line.split(",");
if (values.length == 2) {
String key = values[0].trim();
String value = values[1].trim();
int versionInt = Integer.parseInt(value.substring(2, 4));
if (versionInt < 30) map.put(key, value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
public static void main(String[] args) throws FileNotFoundException {
//GLSM will fail to parse without increasing language level
StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17);
Map<String, String> functionMap = parseGLFunctions();
Set<String> glsmFunctions = new HashSet<>();
File glsmFile = new File(glsmPath);
CompilationUnit cu = StaticJavaParser.parse(glsmFile);
cu.accept(new VoidVisitorWithDefaults<>() {
@Override
public void defaultAction(Node n, Object arg) {
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration cd) {
for (MethodDeclaration md : cd.getMethods()) {
String methodName = md.getNameAsString() + "(";
for (Parameter p : md.getParameters()) {
methodName = methodName + p.getTypeAsString();
}
methodName = methodName + ")";
glsmFunctions.add(methodName);
}
}
}
}
}, null);
Map<String, String> functionGLSMMap = new HashMap<>();
for(String function : functionMap.keySet()) {
if (!glsmFunctions.contains(function)) {
functionGLSMMap.put(function, "false");
} else if (INCLUDE_TRUE) {
functionGLSMMap.put(function, "true");
}
}
List<String[]> outputLines = new ArrayList<>();
for (Map.Entry<String, String> entry : functionGLSMMap.entrySet()) {
outputLines.add(new String[] {entry.getKey(), functionMap.get(entry.getKey()), entry.getValue()});
}
File output = new File("glsm_function_comparison.csv");
try (PrintWriter writer = new PrintWriter(output)) {
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
}
}
public static String convertToCSV(String[] data) {
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
}
public static String escapeSpecialCharacters(String data) {
if (data == null) {
throw new IllegalArgumentException("Input data cannot be null");
}
String escapedData = data.replaceAll("\\R", " ");
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
data = data.replace("\"", "\"\"");
escapedData = "\"" + data + "\"";
}
return escapedData;
}
public static String removeFileExtension(String filename) {
if (filename == null || filename.isEmpty()) {
return filename;
}
String extPattern = "(?<!^)[.]" + "[^.]*$";
return filename.replaceAll(extPattern, "");
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizons.angelica.mixins.Mixins;
import java.util.List;
@@ -9,6 +10,7 @@ import java.util.Set;
@LateMixin
public class AngelicaLateMixins implements ILateMixinLoader {
@Override
public String getMixinConfig() {
return "mixins.angelica.late.json";
@@ -16,7 +18,6 @@ public class AngelicaLateMixins implements ILateMixinLoader {
@Override
public List<String> getMixins(Set<String> loadedMods) {
return Mixins.getLateMixins(loadedMods);
return IMixins.getLateMixins(Mixins.class, loadedMods);
}
}
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica;
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
import com.gtnewhorizons.angelica.common.BlockError;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.proxy.CommonProxy;
@@ -12,15 +14,22 @@ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
@Mod(
modid = "angelica",
modid = MOD_ID,
name = "Angelica",
version = Tags.VERSION,
dependencies = " before:lwjgl3ify@[1.5.3,);" + " after:hodgepodge@[2.4.4,);" + " after:CodeChickenCore@[1.2.0,);"
+ " after:archaicfix@[0.7.0,);" + " required-after:gtnhlib@[0.4.2,);",
dependencies = """
required-after:gtnhlib@[0.6.6,);\
before:lwjgl3ify@[1.5.3,);\
after:hodgepodge@[2.4.4,);\
after:CodeChickenCore@[1.2.0,);\
after:archaicfix@[0.7.0,);\
""",
acceptedMinecraftVersions = "[1.7.10]",
acceptableRemoteVersions = "*",
guiFactory = "com.gtnewhorizons.angelica.config.AngelicaGuiConfigFactory")
public class AngelicaMod {
public static final String MOD_ID = "angelica";
@SidedProxy(clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy", serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
public static CommonProxy proxy;
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.api;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
public interface IBlockAccessExtended extends IBlockAccess {
World getWorld();
}
@@ -0,0 +1,27 @@
package com.gtnewhorizons.angelica.api;
import com.gtnewhorizons.angelica.mixins.interfaces.IPatchedTextureAtlasSprite;
import com.gtnewhorizons.angelica.mixins.interfaces.ITexturesCache;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.util.IIcon;
/**
* Exposes a method to the API that allow mods to add texture update compat
*/
@SuppressWarnings("unused")
public class TextureServices {
@SuppressWarnings("unused")
public static void updateBlockTextureAnimation(IIcon icon, RenderBlocks renderBlocks) {
if (renderBlocks instanceof ITexturesCache texturesCache) {
texturesCache.getRenderedTextures().add(icon);
}
}
@SuppressWarnings("unused")
public static void updateTextureAnimation(IIcon icon) {
if(icon instanceof IPatchedTextureAtlasSprite patchedSprite) {
patchedSprite.markNeedsAnimationUpdate();
}
}
}
@@ -1,25 +1,30 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
import cpw.mods.fml.client.SplashProgress;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import jss.util.RandomXoshiro256StarStar;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.BufferUtils;
import org.apache.commons.io.IOUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.Objects;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
/**
* A batching replacement for {@code FontRenderer}
*
@@ -29,8 +34,6 @@ public class BatchingFontRenderer {
/** The underlying FontRenderer object that's being accelerated */
protected FontRenderer underlying;
/** Cached locations for each unicode page atlas */
private final ResourceLocation[] unicodePageLocations;
/** Array of width of all the characters in default.png */
protected int[] charWidth = new int[256];
/** Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory. */
@@ -42,50 +45,59 @@ public class BatchingFontRenderer {
private int[] colorCode;
/** Location of the primary font atlas to bind. */
protected final ResourceLocation locationFontTexture;
/** The RenderEngine used to load and setup glyph textures. */
private final TextureManager renderEngine;
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
/** The full list of characters present in the default Minecraft font, excluding the Unicode font */
@SuppressWarnings("UnnecessaryUnicodeEscape")
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private final int AAMode;
private final int AAStrength;
private final int texBoundAttrLocation;
private final int fontShaderId;
private static final short[] MCFONT_ASCII_LUT = new short[512];
private static final Char2ShortOpenHashMap MCFONT_UNI_LUT = new Char2ShortOpenHashMap();
final boolean isSGA;
final boolean isSplash;
static {
Arrays.fill(MCFONT_ASCII_LUT, (short) -1);
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
char ch = MCFONT_CHARS.charAt(i);
if (ch < MCFONT_ASCII_LUT.length) {
MCFONT_ASCII_LUT[ch] = i;
} else {
MCFONT_UNI_LUT.put(ch, i);
private static class FontAAShader {
private static Program fontShader = null;
public static Program getProgram() {
if (fontShader == null) {
String vsh, fsh;
try {
fsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.fsh"))), StandardCharsets.UTF_8);
vsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.vsh"))), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
ProgramBuilder builder = ProgramBuilder.begin("fontFilter", vsh, null, fsh, ImmutableSet.of(0));
fontShader = builder.build();
}
return fontShader;
}
}
public static int lookupMcFontPosition(char ch) {
if (ch < MCFONT_ASCII_LUT.length) {
return MCFONT_ASCII_LUT[ch];
} else {
return MCFONT_UNI_LUT.getOrDefault(ch, (short) -1);
}
}
public BatchingFontRenderer(FontRenderer underlying, ResourceLocation[] unicodePageLocations, int[] charWidth,
byte[] glyphWidth, int[] colorCode, ResourceLocation locationFontTexture, TextureManager renderEngine) {
public BatchingFontRenderer(FontRenderer underlying, int[] charWidth, byte[] glyphWidth,
int[] colorCode, ResourceLocation locationFontTexture) {
this.underlying = underlying;
this.unicodePageLocations = unicodePageLocations;
this.charWidth = charWidth;
this.glyphWidth = glyphWidth;
this.colorCode = colorCode;
this.locationFontTexture = locationFontTexture;
this.renderEngine = renderEngine;
for (int i = 0; i < 64; i++) {
batchCommandPool.add(new FontDrawCmd());
}
this.isSGA = Objects.equals(this.locationFontTexture.getResourcePath(), "textures/font/ascii_sga.png");
this.isSplash = FontStrategist.isSplashFontRendererActive(underlying);
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
FontProviderUnicode.get().glyphWidth = this.glyphWidth;
//noinspection deprecation
fontShaderId = FontAAShader.getProgram().getProgramId();
AAMode = GL20.glGetUniformLocation(fontShaderId, "aaMode");
AAStrength = GL20.glGetUniformLocation(fontShaderId, "strength");
texBoundAttrLocation = GL20.glGetAttribLocation(fontShaderId, "texBounds");
}
// === Batched rendering
@@ -97,24 +109,29 @@ public class BatchingFontRenderer {
private static final int INITIAL_BATCH_SIZE = 256;
private static final ResourceLocation DUMMY_RESOURCE_LOCATION = new ResourceLocation("angelica$dummy",
"this is invalid!");
private FloatBuffer batchVtxPositions = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
private ByteBuffer batchVtxColors = BufferUtils.createByteBuffer(INITIAL_BATCH_SIZE * 4);
private FloatBuffer batchVtxTexCoords = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
private IntBuffer batchIndices = BufferUtils.createIntBuffer(INITIAL_BATCH_SIZE / 2 * 3);
private FloatBuffer batchVtxPositions = memAllocFloat(INITIAL_BATCH_SIZE * 2);
private ByteBuffer batchVtxColors = memAlloc(INITIAL_BATCH_SIZE * 4);
private FloatBuffer batchVtxTexCoords = memAllocFloat(INITIAL_BATCH_SIZE * 2);
private IntBuffer batchIndices = memAllocInt(INITIAL_BATCH_SIZE / 2 * 3);
private FloatBuffer batchVtxTexBounds = memAllocFloat(INITIAL_BATCH_SIZE * 4);
private final ObjectArrayList<FontDrawCmd> batchCommands = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
private final ObjectArrayList<FontDrawCmd> batchCommandPool = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
/** */
private void pushVtx(float x, float y, int rgba, float u, float v) {
private void pushVtx(float x, float y, int rgba, float u, float v, float uMin, float uMax, float vMin, float vMax) {
final int oldCap = batchVtxPositions.capacity() / 2;
if (vtxWriterIndex >= oldCap) {
final int newCap = oldCap * 2;
batchVtxPositions = CompatMemoryUtil.memReallocDirect(batchVtxPositions, newCap * 2);
batchVtxColors = CompatMemoryUtil.memReallocDirect(batchVtxColors, newCap * 4);
batchVtxTexCoords = CompatMemoryUtil.memReallocDirect(batchVtxTexCoords, newCap * 2);
batchVtxPositions = memRealloc(batchVtxPositions, newCap * 2);
batchVtxColors = memRealloc(batchVtxColors, newCap * 4);
batchVtxTexCoords = memRealloc(batchVtxTexCoords, newCap * 2);
batchVtxTexBounds = memRealloc(batchVtxTexBounds, newCap * 4);
final int oldIdxCap = batchIndices.capacity();
final int newIdxCap = oldIdxCap * 2;
batchIndices = CompatMemoryUtil.memReallocDirect(batchIndices, newIdxCap);
batchIndices = memRealloc(batchIndices, newIdxCap);
}
final int idx = vtxWriterIndex;
final int idx2 = idx * 2;
@@ -128,15 +145,19 @@ public class BatchingFontRenderer {
batchVtxColors.put(idx4 + 3, (byte) ((rgba >> 24) & 0xFF));
batchVtxTexCoords.put(idx2, u);
batchVtxTexCoords.put(idx2 + 1, v);
batchVtxTexBounds.put(idx4, uMin);
batchVtxTexBounds.put(idx4 + 1, uMax);
batchVtxTexBounds.put(idx4 + 2, vMin);
batchVtxTexBounds.put(idx4 + 3, vMax);
vtxWriterIndex++;
}
private void pushUntexRect(float x, float y, float w, float h, int rgba) {
final int vtxId = vtxWriterIndex;
pushVtx(x, y, rgba, 0, 0);
pushVtx(x, y + h, rgba, 0, 0);
pushVtx(x + w, y, rgba, 0, 0);
pushVtx(x + w, y + h, rgba, 0, 0);
pushVtx(x, y, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x, y + h, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x + w, y, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x + w, y + h, rgba, 0, 0, 0, 0, 0, 0);
pushQuadIdx(vtxId);
}
@@ -153,7 +174,7 @@ public class BatchingFontRenderer {
return idx;
}
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture) {
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture, boolean isUnicode) {
if (!batchCommands.isEmpty()) {
final FontDrawCmd lastCmd = batchCommands.get(batchCommands.size() - 1);
final int prevEndVtx = lastCmd.startVtx + lastCmd.idxCount;
@@ -169,7 +190,7 @@ public class BatchingFontRenderer {
}
}
final FontDrawCmd cmd = batchCommandPool.pop();
cmd.reset(startIdx, idxCount, texture);
cmd.reset(startIdx, idxCount, texture, isUnicode);
batchCommands.add(cmd);
}
@@ -177,12 +198,14 @@ public class BatchingFontRenderer {
public int startVtx;
public int idxCount;
public boolean isUnicode;
public ResourceLocation texture;
public void reset(int startVtx, int vtxCount, ResourceLocation texture) {
public void reset(int startVtx, int vtxCount, ResourceLocation texture, boolean isUnicode) {
this.startVtx = startVtx;
this.idxCount = vtxCount;
this.texture = texture;
this.isUnicode = isUnicode;
}
@Override
@@ -241,6 +264,9 @@ public class BatchingFontRenderer {
}
}
int lastActiveProgram;
int fontAAModeLast = -1;
int fontAAStrengthLast = -1;
private void flushBatch() {
// Sort&Draw
batchCommands.sort(FontDrawCmd.DRAW_ORDER_COMPARATOR);
@@ -253,9 +279,23 @@ public class BatchingFontRenderer {
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.glShadeModel(GL11.GL_FLAT);
if (FontConfig.fontAAMode != 0) {
GL20.glVertexAttribPointer(texBoundAttrLocation, 4, false, 0, batchVtxTexBounds);
GL20.glEnableVertexAttribArray(texBoundAttrLocation);
lastActiveProgram = GLStateManager.getActiveProgram();
GLStateManager.glUseProgram(fontShaderId);
if (FontConfig.fontAAMode != fontAAModeLast) {
fontAAModeLast = FontConfig.fontAAMode;
GL20.glUniform1i(AAMode, FontConfig.fontAAMode);
}
if (FontConfig.fontAAStrength != fontAAStrengthLast) {
fontAAStrengthLast = FontConfig.fontAAStrength;
GL20.glUniform1f(AAStrength, FontConfig.fontAAStrength / 120.f);
}
}
GL11.glTexCoordPointer(2, 0, batchVtxTexCoords);
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 0, batchVtxColors);
@@ -283,8 +323,13 @@ public class BatchingFontRenderer {
}
batchIndices.limit(cmd.startVtx + cmd.idxCount);
batchIndices.position(cmd.startVtx);
GL11.glDrawElements(GL11.GL_TRIANGLES, batchIndices);
}
if (FontConfig.fontAAMode != 0) {
GLStateManager.glUseProgram(lastActiveProgram);
GL20.glDisableVertexAttribArray(texBoundAttrLocation);
}
GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
@@ -312,17 +357,28 @@ public class BatchingFontRenderer {
return (what >= fromInclusive) && (what <= toInclusive);
}
private ResourceLocation getUnicodePageLocation(int page) {
final ResourceLocation lookup = unicodePageLocations[page];
if (lookup == null) {
final ResourceLocation rl = new ResourceLocation(String.format(
"textures/font/unicode_page_%02x.png",
page));
unicodePageLocations[page] = rl;
return rl;
} else {
return lookup;
}
public boolean forceDefaults() {
return this.isSGA || this.isSplash;
}
public float getGlyphScaleX() {
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale * Math.pow(2, FontConfig.glyphAspect)) * (FontStrategist.customFontInUse ? 1.5f : 1);
}
public float getGlyphScaleY() {
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale / Math.pow(2, FontConfig.glyphAspect)) * (FontStrategist.customFontInUse ? 1.5f : 1);
}
public float getGlyphSpacing() {
return forceDefaults() ? 0 : FontConfig.glyphSpacing;
}
public float getWhitespaceScale() {
return forceDefaults() ? 1 : FontConfig.whitespaceScale;
}
public float getShadowOffset() {
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
}
private static final char FORMATTING_CHAR = 167; // §
@@ -335,6 +391,9 @@ public class BatchingFontRenderer {
}
final int shadowColor = (color & 0xfcfcfc) >> 2 | color & 0xff000000;
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
this.beginBatch();
float curX = anchorX;
try {
@@ -354,10 +413,14 @@ public class BatchingFontRenderer {
boolean curStrikethrough = false;
boolean curUnderline = false;
final float underlineY = anchorY + underlying.FONT_HEIGHT - 1.0f;
final float glyphScaleY = getGlyphScaleY();
final float heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY / 2);
final float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY;
final float underlineY = heightNorth + (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY;
float underlineStartX = 0.0f;
float underlineEndX = 0.0f;
final float strikethroughY = anchorY + (float) (underlying.FONT_HEIGHT / 2) - 1.0F;
final float strikethroughY = heightNorth + ((float) (underlying.FONT_HEIGHT / 2) - 1.0f) * glyphScaleY;
float strikethroughStartX = 0.0f;
float strikethroughEndX = 0.0f;
@@ -369,8 +432,8 @@ public class BatchingFontRenderer {
if (curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
pushDrawCmd(ulIdx, 6, null);
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
underlineStartX = underlineEndX;
}
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
@@ -379,9 +442,9 @@ public class BatchingFontRenderer {
strikethroughStartX,
strikethroughY,
strikethroughEndX - strikethroughStartX,
1.0f,
glyphScaleY,
curColor);
pushDrawCmd(ulIdx, 6, null);
pushDrawCmd(ulIdx, 6, null, false);
strikethroughStartX = strikethroughEndX;
}
@@ -426,66 +489,27 @@ public class BatchingFontRenderer {
continue;
}
int lutIndex = lookupMcFontPosition(chr);
if (curRandom && lutIndex != -1) {
int randomReplacementIndex;
do {
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
lutIndex = randomReplacementIndex;
chr = MCFONT_CHARS.charAt(lutIndex);
if (curRandom) {
chr = FontProviderMC.get(this.isSGA).getRandomReplacement(chr);
}
final float shadowOffset = unicodeFlag ? 0.5F : 1.0F;
FontProvider fontProvider = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, unicodeFlag);
// Check ASCII space, NBSP, NNBSP
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
curX += 4;
curX += 4 * this.getWhitespaceScale();
continue;
}
final float uStart;
final float vStart;
final float xAdvance;
final float glyphW;
final float uSz;
final float vSz;
final float uStart = fontProvider.getUStart(chr);
final float vStart = fontProvider.getVStart(chr);
final float xAdvance = fontProvider.getXAdvance(chr) * getGlyphScaleX();
final float glyphW = fontProvider.getGlyphW(chr) * getGlyphScaleX();
final float uSz = fontProvider.getUSize(chr);
final float vSz = fontProvider.getVSize(chr);
final float itOff = curItalic ? 1.0F : 0.0F; // italic offset
final ResourceLocation texture;
if (lutIndex == -1 || unicodeFlag) {
if (glyphWidth[chr] == 0) {
continue;
}
// Draw unicode char
final int uniPage = chr / 256;
texture = getUnicodePageLocation(uniPage);
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
uStart = ((float) (chr % 16 * 16) + startColumnF) / 256.0f;
vStart = ((float) ((chr & 255) / 16 * 16)) / 256.0f;
final float chrWidth = endColumnF - startColumnF - 0.02F;
glyphW = chrWidth / 2.0f + 1.0f;
xAdvance = (endColumnF - startColumnF) / 2.0F + 1.0F;
uSz = chrWidth / 256.0f;
vSz = 15.98f / 256.0f;
} else {
// Draw "ASCII" char
uStart = ((lutIndex % 16) * 8) / 128.0F;
vStart = (float) ((lutIndex / 16) * 8) / 128.0F;
xAdvance = this.charWidth[lutIndex];
if (xAdvance == 0) {
continue;
}
glyphW = xAdvance - 0.01F;
uSz = (glyphW - 1.0F) / 128.0F;
vSz = 7.99F / 128.0F;
texture = locationFontTexture;
}
final float shadowOffset = fontProvider.getShadowOffset();
final ResourceLocation texture = fontProvider.getTexture(chr);
final int vtxId = vtxWriterIndex;
final int idxId = idxWriterIndex;
@@ -493,81 +517,50 @@ public class BatchingFontRenderer {
int vtxCount = 0;
if (enableShadow) {
pushVtx(curX + itOff + shadowOffset, anchorY + shadowOffset, curShadowColor, uStart, vStart);
pushVtx(
curX - itOff + shadowOffset,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart,
vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset,
anchorY + shadowOffset,
curShadowColor,
uStart + uSz,
vStart);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart + uSz,
vStart + vSz);
pushVtx(curX + itOff + shadowOffset, heightNorth + shadowOffset, curShadowColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff + shadowOffset, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset, heightNorth + shadowOffset, curShadowColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
if (curBold) {
final float shadowOffset2 = 2.0f * shadowOffset;
pushVtx(curX + itOff + shadowOffset2, anchorY + shadowOffset, curShadowColor, uStart, vStart);
pushVtx(
curX - itOff + shadowOffset2,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart,
vStart + vSz);
pushVtx(
curX + glyphW - 1.0F + itOff + shadowOffset2,
anchorY + shadowOffset,
curShadowColor,
uStart + uSz,
vStart);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset2,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart + uSz,
vStart + vSz);
pushVtx(curX + itOff + shadowOffset2, heightNorth + shadowOffset, curShadowColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff + shadowOffset2, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset2, heightNorth + shadowOffset, curShadowColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset2, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
}
}
pushVtx(curX + itOff, anchorY, curColor, uStart, vStart);
pushVtx(curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
pushVtx(curX + glyphW - 1.0F - itOff, anchorY + 7.99F, curColor, uStart + uSz, vStart + vSz);
pushVtx(curX + itOff, heightNorth, curColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff, heightNorth + heightSouth, curColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff, heightNorth, curColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff, heightNorth + heightSouth, curColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
if (curBold) {
pushVtx(shadowOffset + curX + itOff, anchorY, curColor, uStart, vStart);
pushVtx(shadowOffset + curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
pushVtx(shadowOffset + curX + glyphW - 1.0F - itOff,
anchorY + 7.99F,
curColor,
uStart + uSz,
vStart + vSz);
pushVtx(shadowOffset + curX + itOff, heightNorth, curColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX - itOff, heightNorth + heightSouth, curColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F + itOff, heightNorth, curColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F - itOff, heightNorth + heightSouth, curColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
}
pushDrawCmd(idxId, vtxCount / 2 * 3, texture);
curX += xAdvance + (curBold ? shadowOffset : 0.0f);
pushDrawCmd(idxId, vtxCount / 2 * 3, texture, chr > 255);
curX += (xAdvance + (curBold ? shadowOffset : 0.0f)) + getGlyphSpacing();
underlineEndX = curX;
strikethroughEndX = curX;
}
if (curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
pushDrawCmd(ulIdx, 6, null);
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
}
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
final int ulIdx = idxWriterIndex;
@@ -575,9 +568,9 @@ public class BatchingFontRenderer {
strikethroughStartX,
strikethroughY,
strikethroughEndX - strikethroughStartX,
1.0f,
glyphScaleY,
curColor);
pushDrawCmd(ulIdx, 6, null);
pushDrawCmd(ulIdx, 6, null, false);
}
} finally {
@@ -586,4 +579,25 @@ public class BatchingFontRenderer {
return curX + (enableShadow ? 1.0f : 0.0f);
}
public float getCharWidthFine(char chr) {
if (chr == FORMATTING_CHAR) { return -1; }
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
return 4 * this.getWhitespaceScale();
}
FontProvider fp = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, underlying.getUnicodeFlag());
return fp.getXAdvance(chr) * this.getGlyphScaleX();
}
public void overrideBlendFunc(int srcRgb, int dstRgb) {
blendSrcRGB = srcRgb;
blendDstRGB = dstRgb;
}
public void resetBlendFunc() {
blendSrcRGB = GL11.GL_SRC_ALPHA;
blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
}
}
@@ -0,0 +1,20 @@
package com.gtnewhorizons.angelica.client.font;
import net.minecraft.util.ResourceLocation;
public interface FontProvider {
/**
* For use with §k. Should fetch a character of the same width as provided.
*/
char getRandomReplacement(char chr);
boolean isGlyphAvailable(char chr);
float getUStart(char chr);
float getVStart(char chr);
float getXAdvance(char chr);
float getGlyphW(char chr);
float getUSize(char chr);
float getVSize(char chr);
float getShadowOffset();
ResourceLocation getTexture(char chr);
}
@@ -0,0 +1,300 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizons.angelica.config.FontConfig;
import lombok.Setter;
import lombok.Value;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.ITextureObject;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.ResourceLocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.imageio.ImageIO;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
public final class FontProviderCustom implements FontProvider {
public static final Logger LOGGER = LogManager.getLogger("Angelica");
public static final String FONT_DIR = "fonts/custom/";
static final int ATLAS_SIZE = 128;
static final int ATLAS_COUNT = 512;
private final byte id; // 0 - primary font, 1 - fallback font
private FontAtlas[] fontAtlases = new FontAtlas[ATLAS_COUNT];
private float currentFontQuality = FontConfig.customFontQuality;
@Setter
private Font font;
private FontProviderCustom(byte id) {
this.id = id;
Font[] availableFonts = FontStrategist.getAvailableFonts();
if (availableFonts.length == 0) {
LOGGER.fatal("There seem to be no fonts available on this system! Disabling custom font and throwing an exception in an attempt to restore the session.");
FontConfig.enableCustomFont = false;
ConfigurationManager.save(FontConfig.class);
throw new RuntimeException();
}
String myFontName = switch (this.id) {
case 0 -> FontConfig.customFontNamePrimary;
case 1 -> FontConfig.customFontNameFallback;
default -> null;
};
if (Objects.equals(myFontName, "(none)")) {
font = null;
return;
}
int fontPos = -1;
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(myFontName, availableFonts[i].getFontName())) {
fontPos = i;
break;
}
}
if (fontPos == -1) {
LOGGER.info("Could not find previously set font \"{}\". ", myFontName);
font = null;
return;
}
font = availableFonts[fontPos].deriveFont(currentFontQuality);
}
private static class InstLoader {
static final FontProviderCustom instance0 = new FontProviderCustom((byte)0);
static final FontProviderCustom instance1 = new FontProviderCustom((byte)1);
}
public static FontProviderCustom getPrimary() { return InstLoader.instance0; }
public static FontProviderCustom getFallback() { return InstLoader.instance1; }
public void reloadFont(int fontID) {
currentFontQuality = FontConfig.customFontQuality;
font = FontStrategist.getAvailableFonts()[fontID].deriveFont(currentFontQuality);
File[] files = new File(getFontDir()).listFiles();
if (files != null) {
for (File f : files) {
if (!Files.isSymbolicLink(f.toPath())) {
f.delete();
}
}
}
TextureManager tm = Minecraft.getMinecraft().getTextureManager();
Map mapTextureObjects = tm.mapTextureObjects;
for (int i = 0; i < ATLAS_COUNT; i++) {
ResourceLocation key = new ResourceLocation(getAtlasResourceName(i));
if (mapTextureObjects.containsKey(key)) {
ITextureObject obj = (ITextureObject) mapTextureObjects.get(key);
TextureUtil.deleteTexture(obj.getGlTextureId());
mapTextureObjects.remove(key);
}
}
fontAtlases = new FontAtlas[ATLAS_COUNT];
}
private String getFontDir() {
return FONT_DIR + "f" + this.id + "/";
}
private String getAtlasFilename(int atlasId) {
return "f" + this.id + "p" + atlasId;
}
String getAtlasResourceName(int atlasId) {
return "minecraft:angelica_c" + getAtlasFilename(atlasId);
}
String getAtlasFullPath(int atlasId) {
return getFontDir() + getAtlasFilename(atlasId) + ".png";
}
@Value
private class GlyphData {
float uStart;
float vStart;
float xAdvance;
float glyphW;
float uSz;
float vSz;
}
private class FontAtlas {
GlyphData[] glyphData = new GlyphData[ATLAS_SIZE];
private ResourceLocation texture;
private final int id;
FontAtlas(int id) {
this.id = id;
}
void construct(Font font) {
float separator = currentFontQuality / 3f;
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
g2d.dispose();
int width = 0;
int actualChars = 0;
for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
}
}
if (actualChars == 0) {
return;
}
final int atlasTilesX = (int) Math.ceil(Math.sqrt(actualChars) * 1.5f);
final int atlasTilesY = (int) Math.ceil((double) actualChars / atlasTilesX);
LOGGER.info("constructing custom font atlas (ID {}) of nominal size {} chars, actual: {}, {} rows by {} columns",
this.id, ATLAS_SIZE, actualChars, atlasTilesX, atlasTilesY);
width = 0;
actualChars = 0;
int maxRowWidth = 0;
for (int i = 0; i < ATLAS_SIZE; i++) {
if (actualChars % atlasTilesX == 0) {
maxRowWidth = Math.max(maxRowWidth, width);
width = 0;
}
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
}
}
maxRowWidth = Math.max(maxRowWidth, width);
final int lineHeight = fm.getHeight();
final float desc = fm.getDescent();
final int imageWidth = (int) (maxRowWidth + separator);
final int imageHeight = (int) ((separator + lineHeight) * atlasTilesY + separator);
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setFont(font);
fm = g2d.getFontMetrics();
int tileX = 0, tileY = 0; // position in atlas tiles
int imgX = (int) separator; // position in pixels
for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (!font.canDisplay(ch)) { continue; }
if (tileX >= atlasTilesX) {
tileX = 0;
imgX = (int) separator;
tileY++;
}
final int charWidth = fm.charWidth(ch);
final float charAspectRatio = (float) charWidth / lineHeight;
final float inset = currentFontQuality / 16;
g2d.drawString(Character.toString(ch), imgX, (lineHeight + separator) * (tileY + 1) - desc);
final float uStart = (float) (imgX - inset * charAspectRatio) / imageWidth;
final float vStart = ((lineHeight + separator) * (tileY + 1) - lineHeight - inset) / imageHeight;
final float xAdvance = charAspectRatio * 8 * charWidth / (charWidth + 2 * inset * charAspectRatio);
final float glyphW = charAspectRatio * 8 + 1;
final float uSz = (float) (charWidth + 2 * inset * charAspectRatio) / imageWidth;
final float vSz = (float) (lineHeight + 2 * inset) / imageHeight;
glyphData[i] = new GlyphData(uStart, vStart, xAdvance, glyphW, uSz, vSz);
imgX += (int) (charWidth + separator);
tileX++;
}
g2d.dispose();
try {
LOGGER.info("writing custom font atlas texture ({}x{} px) to {}", imageWidth, imageHeight, getAtlasFullPath(this.id));
Files.createDirectories(Paths.get(getFontDir()));
ImageIO.write(image, "png", new File(getAtlasFullPath(this.id)));
} catch (IOException e) {
e.printStackTrace();
}
this.texture = new ResourceLocation(getAtlasResourceName(this.id));
}
}
private FontAtlas getAtlas(char chr) {
int id = chr / ATLAS_SIZE;
FontAtlas fa = fontAtlases[id];
if (fa == null) {
fa = new FontAtlas(id);
fa.construct(font);
}
fontAtlases[id] = fa;
return fa;
}
@Override
public boolean isGlyphAvailable(char chr) {
if (font == null) { return false; }
return (getAtlas(chr).glyphData[chr % ATLAS_SIZE] != null);
}
@Override
public char getRandomReplacement(char chr) {
return chr;
}
@Override
public float getUStart(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uStart;
}
@Override
public float getVStart(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vStart;
}
@Override
public float getXAdvance(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].xAdvance;
}
@Override
public float getGlyphW(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].glyphW;
}
@Override
public float getUSize(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uSz;
}
@Override
public float getVSize(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vSz;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
return getAtlas(chr).texture;
}
}
@@ -0,0 +1,106 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.FontConfig;
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
import jss.util.RandomXoshiro256StarStar;
import net.minecraft.util.ResourceLocation;
public final class FontProviderMC implements FontProvider {
/**
* The full list of characters present in the default Minecraft font, excluding the Unicode font
*/
@SuppressWarnings("UnnecessaryUnicodeEscape")
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private FontProviderMC() {}
private static class InstLoader {
static final FontProviderMC instance = new FontProviderMC();
static final FontProviderMC instanceSGA = new FontProviderMC();
}
public static FontProviderMC get(boolean isSGA) {
return (isSGA ? InstLoader.instanceSGA : InstLoader.instance);
}
private static final Char2ShortOpenHashMap MCFONT_ASCII_MAP = new Char2ShortOpenHashMap();
ResourceLocation locationFontTexture = null;
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
public int[] charWidth;
static {
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
MCFONT_ASCII_MAP.put(MCFONT_CHARS.charAt(i), i);
}
}
public int lookupMcFontPosition(char ch) {
if (isGlyphAvailable(ch)) {
return MCFONT_ASCII_MAP.get(ch);
}
return -1;
}
@Override
public boolean isGlyphAvailable(char chr) {
return MCFONT_ASCII_MAP.containsKey(chr);
}
public char getRandomReplacement(char chr){
int lutIndex = lookupMcFontPosition(chr);
if (lutIndex != -1) {
int randomReplacementIndex;
do {
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
lutIndex = randomReplacementIndex;
return MCFONT_CHARS.charAt(lutIndex);
}
return chr;
}
@Override
public float getUStart(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return ((lutIndex % 16) * 8) / 128.0F;
}
@Override
public float getVStart(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return (float) ((lutIndex / 16) * 8) / 128.0F;
}
@Override
public float getXAdvance(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return charWidth[lutIndex];
}
@Override
public float getGlyphW(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return charWidth[lutIndex] - 0.01F;
}
@Override
public float getUSize(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return (charWidth[lutIndex] - 1.01F) / 128.0F;
}
@Override
public float getVSize(char chr) {
return 7.99F / 128.0F;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
return locationFontTexture;
}
}
@@ -0,0 +1,93 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.FontConfig;
import net.minecraft.util.ResourceLocation;
public final class FontProviderUnicode implements FontProvider {
private FontProviderUnicode() {}
private static class InstLoader { static final FontProviderUnicode instance = new FontProviderUnicode(); }
public static FontProviderUnicode get() { return FontProviderUnicode.InstLoader.instance; }
private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256];
public byte[] glyphWidth;
private ResourceLocation getUnicodePageLocation(int page) {
final ResourceLocation lookup = unicodePageLocations[page];
if (lookup == null) {
final ResourceLocation rl = new ResourceLocation(String.format(
"textures/font/unicode_page_%02x.png",
page));
unicodePageLocations[page] = rl;
return rl;
} else {
return lookup;
}
}
@Override
public boolean isGlyphAvailable(char chr) {
return true;
}
@Override
public char getRandomReplacement(char chr) {
return chr;
}
@Override
public float getUStart(char chr) {
final float startColumnF = (float)(this.glyphWidth[chr] >>> 4);
return ((float) (chr % 16 * 16) + startColumnF + 0.21f) / 256.0f;
}
@Override
public float getVStart(char chr) {
return ((float) ((chr & 255) / 16 * 16) + 0.21f) / 256.0f;
}
@Override
public float getXAdvance(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
return (endColumnF - startColumnF) / 2.0F + 1.0F;
}
@Override
public float getGlyphW(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
final float chrWidth = endColumnF - startColumnF - 0.02F;
return chrWidth / 2.0f + 1.0f;
}
@Override
public float getUSize(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
final float chrWidth = endColumnF - startColumnF - 0.02F;
return (chrWidth - 0.42f) / 256.0f;
}
@Override
public float getVSize(char chr) {
return (16.0f - 0.42f) / 256.0f;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
final int uniPage = chr / 256;
return getUnicodePageLocation(uniPage);
}
}
@@ -0,0 +1,143 @@
package com.gtnewhorizons.angelica.client.font;
import com.google.common.collect.HashMultiset;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor;
import cpw.mods.fml.client.SplashProgress;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.resources.DefaultResourcePack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
public class FontStrategist {
@Getter
private static final Font[] availableFonts;
public static final Logger LOGGER = LogManager.getLogger("Angelica");
public static boolean customFontInUse;
static {
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
HashMap<String, Font> fontSet = new HashMap<>();
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
for (Font font : availableFontsDirty) {
String fontName = font.getFontName();
if (fontSet.containsKey(fontName)) {
duplicates.add(fontName);
} else {
fontSet.put(fontName, font);
}
}
if (!duplicates.isEmpty()) {
StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: ");
for (Iterator<String> iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) {
String dupe = iter.next();
sb.append(duplicates.count(dupe)).append("x ").append(dupe);
if (iter.hasNext()) {
sb.append(", ");
}
}
sb.append(". Some fonts may be missing from the font selection menu.");
LOGGER.warn(sb.toString());
}
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
// create and add the resource pack that provides fonts
HashMap<String, File> packMap = new HashMap<>();
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
packMap.put(FontProviderCustom.getPrimary().getAtlasResourceName(i), new File(FontProviderCustom.getPrimary().getAtlasFullPath(i)));
packMap.put(FontProviderCustom.getFallback().getAtlasResourceName(i), new File(FontProviderCustom.getFallback().getAtlasFullPath(i)));
}
DefaultResourcePack fontResourcePack = new DefaultResourcePack(packMap);
List defaultResourcePacks = ((ResourceAccessor) Minecraft.getMinecraft()).angelica$getDefaultResourcePacks();
defaultResourcePacks.add(fontResourcePack);
Minecraft.getMinecraft().refreshResources();
// determine if a custom font is in use based on config values
boolean primaryFontFound = false;
boolean fallbackFontFound = false;
for (Font availableFont : availableFonts) {
if (Objects.equals(FontConfig.customFontNamePrimary, availableFont.getFontName())) {
primaryFontFound = true;
break;
}
if (Objects.equals(FontConfig.customFontNameFallback, availableFont.getFontName())) {
fallbackFontFound = true;
break;
}
}
customFontInUse = (FontConfig.enableCustomFont && (primaryFontFound || fallbackFontFound));
}
/**
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether or not
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
*/
public static FontProvider getFontProvider(BatchingFontRenderer me, char chr, boolean customFontEnabled, boolean forceUnicode) {
if (me.isSGA && FontProviderMC.get(true).isGlyphAvailable(chr)) {
return FontProviderMC.get(true);
}
if (customFontEnabled && !me.isSplash) {
FontProvider fp;
fp = FontProviderCustom.getPrimary();
if (fp.isGlyphAvailable(chr)) { return fp; }
fp = FontProviderCustom.getFallback();
if (fp.isGlyphAvailable(chr)) { return fp; }
return FontProviderUnicode.get();
} else {
if (!forceUnicode && FontProviderMC.get(false).isGlyphAvailable(chr)) {
return FontProviderMC.get(false);
} else {
return FontProviderUnicode.get();
}
}
}
public static void reloadCustomFontProviders() {
FontProviderCustom.getPrimary().setFont(null);
FontProviderCustom.getFallback().setFont(null);
boolean primaryFontFound = false;
boolean fallbackFontFound = false;
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(FontConfig.customFontNamePrimary, availableFonts[i].getFontName())) {
FontProviderCustom.getPrimary().reloadFont(i);
primaryFontFound = true;
}
if (Objects.equals(FontConfig.customFontNameFallback, availableFonts[i].getFontName())) {
FontProviderCustom.getFallback().reloadFont(i);
fallbackFontFound = true;
}
if (primaryFontFound && fallbackFontFound) { break; }
}
customFontInUse = (FontConfig.enableCustomFont && (primaryFontFound || fallbackFontFound));
}
public static boolean isSplashFontRendererActive(FontRenderer fontRenderer) {
// noinspection deprecation
boolean active = fontRenderer instanceof SplashProgress.SplashFontRenderer;
try {
Class<?> customSplashClass = Class.forName("gkappa.modernsplash.CustomSplash$SplashFontRenderer");
active = active || customSplashClass.isInstance(fontRenderer);
} catch (ClassNotFoundException ignored) {
}
return active;
}
}
@@ -0,0 +1,357 @@
package com.gtnewhorizons.angelica.client.gui;
import com.gtnewhorizon.gtnhlib.config.ConfigException;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizons.angelica.client.font.FontStrategist;
import com.gtnewhorizons.angelica.config.FontConfig;
import net.coderbot.iris.gui.element.widget.IrisButton;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class FontConfigScreen extends GuiScreen {
private static final Font[] availableFonts = FontStrategist.getAvailableFonts();
private final GuiScreen parent;
private final String title;
private final String searchPrompt;
private String currentPrimaryFontName;
private String currentFallbackFontName;
private FontList fontList;
private int selectedPrimaryFontListPos = -1;
private int selectedFallbackFontListPos = -1;
private ArrayList<Font> displayedFonts;
private GuiTextField searchBox;
public FontConfigScreen(GuiScreen parent) {
this.title = I18n.format("options.angelica.fontconfig.title");
this.searchPrompt = I18n.format("options.angelica.fontconfig.searchprompt");
this.parent = parent;
this.currentPrimaryFontName = FontConfig.customFontNamePrimary;
this.currentFallbackFontName = FontConfig.customFontNameFallback;
this.displayedFonts = new ArrayList<>(Arrays.asList(availableFonts));
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(this.currentPrimaryFontName, availableFonts[i].getFontName())) {
selectedPrimaryFontListPos = i;
}
if (Objects.equals(this.currentFallbackFontName, availableFonts[i].getFontName())) {
selectedFallbackFontListPos = i;
}
}
}
SliderClone.Option optFontQuality = new SliderClone.Option(6, 72, 6);
SliderClone.Option optShadowOffset = new SliderClone.Option(0, 2, 0.05f);
SliderClone.Option optGlyphAspect = new SliderClone.Option(-1, 1, 0.05f);
SliderClone.Option optGlyphScale = new SliderClone.Option(0.1f, 3, 0.05f);
SliderClone.Option optWhitespaceScale = new SliderClone.Option(0.1f, 3, 0.05f);
SliderClone.Option optGlyphSpacing = new SliderClone.Option(-2f, 2f, 0.05f);
SliderClone.Option optFontAAMode = new SliderClone.Option(0, 2, 1);
SliderClone.Option optFontAAStrength = new SliderClone.Option(1, 24, 1);
public void initGui() {
Keyboard.enableRepeatEvents(true);
searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 120, 24, 240, 20);
searchBox.setMaxStringLength(64);
fontList = new FontList();
initButtons();
}
private void initButtons() {
this.buttonList.add(new IrisButton(this.width / 2 - 81 - 165, this.height - 20 - 7, 162, 20,
FontConfig.enableCustomFont ? I18n.format("options.angelica.fontconfig.disable_custom_font") :
I18n.format("options.angelica.fontconfig.enable_custom_font"), this::toggleCustomFont));
this.buttonList.add(new IrisButton(this.width / 2 - 80, this.height - 20 - 7, 160, 20,
I18n.format("options.angelica.fontconfig.reset_values"), this::resetValues));
this.buttonList.add(new IrisButton(this.width / 2 - 81 + 165, this.height - 20 - 7, 162, 20,
I18n.format("gui.done"), button -> this.onClose()));
// might refactor later, feeling too lazy at the moment
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 186, this.height - 40 - 11, 120, 20,
optFontQuality, FontConfig.customFontQuality, this::setFontQuality,
value -> I18n.format("options.angelica.fontconfig.font_quality", String.format("%2.0f", value)),
"options.angelica.fontconfig.font_quality.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 186, this.height - 60 - 15, 120, 20,
optShadowOffset, FontConfig.fontShadowOffset, value -> FontConfig.fontShadowOffset = value,
value -> I18n.format("options.angelica.fontconfig.shadow_offset", String.format("x%3.2f", value)),
"options.angelica.fontconfig.shadow_offset.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 62, this.height - 40 - 11, 120, 20,
optGlyphAspect, FontConfig.glyphAspect, value -> FontConfig.glyphAspect = value,
value -> I18n.format("options.angelica.fontconfig.glyph_aspect", String.format("%3.2f", value)),
"options.angelica.fontconfig.glyph_aspect.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 62, this.height - 60 - 15, 120, 20,
optGlyphScale, FontConfig.glyphScale, value -> FontConfig.glyphScale = value,
value -> I18n.format("options.angelica.fontconfig.glyph_scale", String.format("x%3.2f", value)),
"options.angelica.fontconfig.glyph_scale.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 62, this.height - 40 - 11, 120, 20,
optWhitespaceScale, FontConfig.whitespaceScale, value -> FontConfig.whitespaceScale = value,
value -> I18n.format("options.angelica.fontconfig.whitespace_scale", String.format("x%3.2f", value)),
"options.angelica.fontconfig.whitespace_scale.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 62, this.height - 60 - 15, 120, 20,
optGlyphSpacing, FontConfig.glyphSpacing, value -> FontConfig.glyphSpacing = value,
value -> I18n.format("options.angelica.fontconfig.glyph_spacing", String.format("%3.2f", value)),
"options.angelica.fontconfig.glyph_spacing.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 186, this.height - 40 - 11, 120, 20,
optFontAAStrength, FontConfig.fontAAStrength, value -> FontConfig.fontAAStrength = value.intValue(),
value -> I18n.format("options.angelica.fontconfig.font_aa_strength", String.format("%.0f", value)),
"options.angelica.fontconfig.font_aa_strength.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 186, this.height - 60 - 15, 120, 20,
optFontAAMode, FontConfig.fontAAMode, value -> FontConfig.fontAAMode = value.intValue(),
this::fontAAModeFormat,
"options.angelica.fontconfig.aamode.tooltip"));
}
private void onClose() {
applyChanges(true);
this.mc.displayGuiScreen(parent);
}
private void applyChanges(boolean finalApply) {
int pos;
pos = selectedPrimaryFontListPos;
if (pos >= 0 && pos < displayedFonts.size()) {
FontConfig.customFontNamePrimary = displayedFonts.get(pos).getFontName();
}
pos = selectedFallbackFontListPos;
if (pos >= 0 && pos < displayedFonts.size()) {
FontConfig.customFontNameFallback = displayedFonts.get(pos).getFontName();
}
FontStrategist.reloadCustomFontProviders();
if (finalApply) {
ConfigurationManager.save(FontConfig.class);
}
}
private void resetValues(IrisButton button) {
try {
ConfigurationManager.getConfigElements(FontConfig.class).forEach(elem -> elem.set(elem.getDefault()));
} catch (ConfigException e) {
throw new RuntimeException(e);
}
selectedPrimaryFontListPos = -1;
selectedFallbackFontListPos = -1;
FontStrategist.customFontInUse = false;
super.buttonList.clear();
this.initButtons();
}
private void toggleCustomFont(IrisButton button) {
FontConfig.enableCustomFont = !FontConfig.enableCustomFont;
applyChanges(false);
button.displayString = FontConfig.enableCustomFont ?
I18n.format("options.angelica.fontconfig.disable_custom_font") :
I18n.format("options.angelica.fontconfig.enable_custom_font");
}
private int qualityLast = FontConfig.customFontQuality;
private void setFontQuality(float quality) {
FontConfig.customFontQuality = (int) quality;
if (qualityLast != (int) quality) {
applyChanges(false);
}
qualityLast = (int) quality;
}
private String fontAAModeFormat(float AAmode) {
return switch ((int) AAmode) {
case 2 -> I18n.format("options.angelica.fontconfig.aamode.aa_16x");
case 1 -> I18n.format("options.angelica.fontconfig.aamode.aa_4x");
default -> I18n.format("options.angelica.fontconfig.aamode.aa_none");
};
}
private float lastMouseX = 0;
private float lastMouseY = 0;
private long lastStillTime = 0;
@Override
public void drawScreen(int mouseX, int mouseY, float delta) {
super.drawDefaultBackground();
fontList.drawScreen(mouseX, mouseY, delta);
searchBox.drawTextBox();
// I bet you thought this was drawn inside the search box, not on top of it.
if (!this.searchBox.isFocused() && this.searchBox.getText().isEmpty()) {
this.drawCenteredString(this.fontRendererObj, this.searchPrompt,
this.searchBox.xPosition + this.fontRendererObj.getStringWidth(this.searchPrompt) / 2 + 4,
this.searchBox.yPosition + this.searchBox.height / 2 - 4, 0xFFFFFF);
}
drawCenteredString(this.fontRendererObj, this.title, (int) (this.width * 0.5), 8, 0xFFFFFF);
drawCenteredString(this.fontRendererObj, I18n.format("options.angelica.fontconfig.currentfonts",
FontConfig.customFontNamePrimary, FontConfig.customFontNameFallback), (int) (this.width * 0.5), this.height - 92, 0xFFFFFF);
super.drawScreen(mouseX, mouseY, delta);
for (GuiButton guiButton : buttonList) {
if (!(guiButton instanceof SliderClone slider)) { continue; }
final int top = slider.yPosition;
final int bot = slider.yPosition + slider.height;
final int left = slider.xPosition;
final int right = slider.xPosition + slider.width;
if (mouseY < top || mouseY >= bot || mouseX < left || mouseX >= right) { continue; }
if (mouseX == lastMouseX && mouseY == lastMouseY) {
if (lastStillTime == 0) {
lastStillTime = System.currentTimeMillis();
}
if (lastStillTime + 500L < System.currentTimeMillis()) {
displayTooltip(mouseX, mouseY, slider.tooltipKey);
}
} else {
lastStillTime = 0;
}
break;
}
lastMouseX = mouseX;
lastMouseY = mouseY;
}
private void displayTooltip(int x, int y, String langKey) {
List<String> lines = this.fontRendererObj.listFormattedStringToWidth(I18n.format(langKey), this.width / 2);
this.drawHoveringText(lines, x, y, this.fontRendererObj);
}
@Override
protected void actionPerformed(GuiButton guiButton) {
if (guiButton.enabled) {
if (guiButton instanceof IrisButton irisButton) {
irisButton.onPress();
}
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (this.searchBox.isFocused()) {
if (keyCode == Keyboard.KEY_ESCAPE) {
this.searchBox.setFocused(false);
}
this.searchBox.textboxKeyTyped(typedChar, keyCode);
this.displayedFonts = filterFonts(this.searchBox.getText().toLowerCase());
}
if (keyCode == Keyboard.KEY_ESCAPE) {
this.onClose();
}
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
super.mouseClicked(mouseX, mouseY, mouseButton);
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
this.fontList.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
public void updateScreen() {
this.searchBox.updateCursorCounter();
}
// this could be more efficient, but it's good enough as is
private ArrayList<Font> filterFonts(String search) {
ArrayList<Font> results;
if (search == null || search.isEmpty()) {
results = new ArrayList<>(Arrays.asList(availableFonts));
} else {
results = Arrays.stream(availableFonts).filter((font -> font.getFontName().toLowerCase().contains(search))).collect(Collectors.toCollection(ArrayList::new));
}
selectedPrimaryFontListPos = -1;
selectedFallbackFontListPos = -1;
for (int i = 0; i < results.size(); i++) {
if (Objects.equals(currentPrimaryFontName, results.get(i).getFontName())) {
selectedPrimaryFontListPos = i;
}
if (Objects.equals(currentFallbackFontName, results.get(i).getFontName())) {
selectedFallbackFontListPos = i;
}
}
return results;
}
class FontList extends GuiSlot {
public FontList() {
super(FontConfigScreen.this.mc, FontConfigScreen.this.width, FontConfigScreen.this.height, 52, FontConfigScreen.this.height - 100, 18);
}
private void mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (mouseY < this.top || mouseY > this.bottom) { return; }
int listLeftBound = this.width / 2 - this.getListWidth() / 2;
int listRightBound = this.width / 2 + this.getListWidth() / 2;
if (mouseX < listLeftBound || mouseX > listRightBound) { return; }
int mousePosInList = mouseY - this.top - this.headerPadding + (int)this.amountScrolled - 4;
int slotIndex = mousePosInList / this.slotHeight;
if (slotIndex < 0 || slotIndex >= this.getSize()) { return; }
if (mouseButton == 0) {
onElemClicked(slotIndex, false);
} else if (mouseButton == 1) {
onElemClicked(slotIndex, true);
}
}
protected void onElemClicked(int index, boolean rightClick) {
if (!rightClick) {
selectedPrimaryFontListPos = index;
currentPrimaryFontName = displayedFonts.get(index).getFontName();
} else {
selectedFallbackFontListPos = index;
currentFallbackFontName = displayedFonts.get(index).getFontName();
}
applyChanges(false);
}
protected boolean isSelected(int index) {
return (index == selectedPrimaryFontListPos || index == selectedFallbackFontListPos);
}
protected int getSize() {
return displayedFonts.size();
}
@Override
protected int getContentHeight() {
return this.getSize() * 18;
}
@Override
protected int getScrollBarX() {
return this.width * 11 / 12 - 5;
}
@Override
public int getListWidth() {
return this.width * 2 / 3;
}
// see onElemClicked for the proper version that supports right-clicking
protected void elementClicked(int index, boolean doubleClicked, int mouseX, int mouseY) {}
protected void drawBackground() {
drawDefaultBackground();
}
protected void drawSlot(int index, int x, int y, int p_148126_4_, Tessellator tessellator, int p_148126_6_, int p_148126_7_) {
int color = 0xffffff;
if (index == selectedPrimaryFontListPos) {
color &= 0xffff55;
}
if (index == selectedFallbackFontListPos) {
color &= 0x55ffff;
}
drawCenteredString(fontRendererObj, displayedFonts.get(index).getFontName(), this.width / 2, y + 1, color);
}
}
}
@@ -0,0 +1,114 @@
package com.gtnewhorizons.angelica.client.gui;
import lombok.AllArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import org.lwjgl.opengl.GL11;
import java.util.function.Consumer;
import java.util.function.Function;
public class SliderClone extends GuiButton {
private final Function<Float, String> displayStringFormatter;
private final Consumer<Float> valueSetter;
private final Option option;
private float sliderPercentage;
public final String tooltipKey;
public boolean mouseDragging;
@AllArgsConstructor
public static class Option {
float min;
float max;
float step;
float denormalize(float alpha) {
float lerp = min * (1 - alpha) + max * alpha;
return Math.round(lerp / step) * step;
}
float normalize(float value) {
return (value - min) / (max - min);
}
}
public SliderClone(int x, int y, int width, int height, Option option, float initialValue,
Consumer<Float> setter, Function<Float, String> formatter, String tooltipKey) {
super(0, x, y, width, height, "");
this.option = option;
this.sliderPercentage = option.normalize(initialValue);
this.displayString = formatter.apply(initialValue);
this.valueSetter = setter;
this.displayStringFormatter = formatter;
this.tooltipKey = tooltipKey;
}
/**
* Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this button and 2 if it IS hovering over
* this button.
*/
public int getHoverState(boolean mouseOver) {
return 0;
}
/**
* Fired when the mouse button is dragged. Equivalent of MouseListener.mouseDragged(MouseEvent e).
*/
protected void mouseDragged(Minecraft mc, int mouseX, int mouseY) {
if (this.visible) {
if (this.mouseDragging) {
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
if (this.sliderPercentage < 0.0F) {
this.sliderPercentage = 0.0F;
}
if (this.sliderPercentage > 1.0F) {
this.sliderPercentage = 1.0F;
}
float f = this.option.denormalize(this.sliderPercentage);
valueSetter.accept(f);
this.sliderPercentage = this.option.normalize(f);
this.displayString = displayStringFormatter.apply(f);
}
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
}
}
/**
* Returns true if the mouse has been pressed on this control. Equivalent of MouseListener.mousePressed(MouseEvent e).
*/
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY) {
if (super.mousePressed(mc, mouseX, mouseY)) {
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
if (this.sliderPercentage < 0.0F) {
this.sliderPercentage = 0.0F;
}
if (this.sliderPercentage > 1.0F) {
this.sliderPercentage = 1.0F;
}
float f = this.option.denormalize(this.sliderPercentage);
valueSetter.accept(f);
this.sliderPercentage = this.option.normalize(f);
this.displayString = displayStringFormatter.apply(f);
this.mouseDragging = true;
return true;
} else {
return false;
}
}
/**
* Fired when the mouse button is released. Equivalent of MouseListener.mouseReleased(MouseEvent e).
*/
public void mouseReleased(int mouseX, int mouseY) {
this.mouseDragging = false;
}
}
@@ -26,17 +26,7 @@ public class BlockError extends Block {
public BlockError() {
super(Material.rock);
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onTextureStitch(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() == 0) {
for (int i = 0; i < icons.length; i++) {
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
@Override
@@ -45,4 +35,15 @@ public class BlockError extends Block {
return meta >= 0 && meta < icons.length ? icons[meta] : null;
}
public static class EventHandler{
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onTextureStitch(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() == 0) {
for (int i = 0; i < icons.length; i++) {
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
}
}
}
}
}
@@ -1,34 +1,20 @@
package com.gtnewhorizons.angelica.common;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadProvider;
import com.gtnewhorizon.gtnhlib.client.renderer.util.DirectionUtil;
import com.gtnewhorizons.angelica.mixins.interfaces.ModeledBlock;
import com.gtnewhorizons.angelica.utils.AssetLoader;
import lombok.Getter;
import lombok.Setter;
import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import org.jetbrains.annotations.NotNull;
import static com.gtnewhorizons.angelica.models.VanillaModels.LECTERN;
public class BlockTest extends Block implements ModeledBlock {
@Getter
@Setter
private QuadProvider model;
/// Unline {@link BlockError}, this *is* registered on both sides. It's intended to test functionality in a SP
/// development environment.
public class BlockTest extends Block {
public BlockTest() {
super(Material.rock);
this.setBlockTextureName("missingno");
this.setModel((world, pos, block, meta, dir, random, color, quadPool) -> {
if (meta < 2 || meta > 5) meta = 2;
return LECTERN.models[meta - 2].getQuads(world, pos, block, meta, dir, random, color, quadPool);
});
super(Material.wood);
setHardness(0.7f);
}
@Override
@@ -36,13 +22,6 @@ public class BlockTest extends Block implements ModeledBlock {
return false;
}
@Override
public void registerBlockIcons(IIconRegister reg) {
for (String s : AssetLoader.testTexs) {
reg.registerIcon(s);
}
}
/**
* Called when a block is placed using its ItemBlock. Args: World, X, Y, Z, side, hitX, hitY, hitZ, block metadata
*/
@@ -50,11 +29,16 @@ public class BlockTest extends Block implements ModeledBlock {
public int onBlockPlaced(@NotNull World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, int meta) {
// Face NORTH if placed up or down
final ForgeDirection s = DirectionUtil.ALL_DIRECTIONS[side];
final var s = ForgeDirection.getOrientation(side);
if (s == ForgeDirection.UP || s == ForgeDirection.DOWN)
return 2;
// Face the placed side
return side;
}
@Override
public int getRenderType() {
return JSON_ISBRH_ID;
}
}
@@ -1,8 +1,6 @@
package com.gtnewhorizons.angelica.compat;
import com.falsepattern.chunk.api.DataRegistry;
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedBlockStorageAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedNibbleArray;
import com.gtnewhorizons.neid.mixins.interfaces.IExtendedBlockStorageMixin;
import net.minecraft.world.chunk.Chunk;
@@ -19,7 +17,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
}
public ExtendedBlockStorageExt(Chunk chunk, ExtendedBlockStorage storage) {
super(((ExtendedBlockStorageAccessor) storage).getYBase(), storage.getSkylightArray() != null);
super(storage.yBase, storage.getSkylightArray() != null);
if (ModStatus.isChunkAPILoaded) {
if (storage.getSkylightArray() != null) {
@@ -33,7 +31,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
System.arraycopy(((IExtendedBlockStorageMixin)(Object)storage).getBlock16BArray(), 0, block16BArray, 0, block16BArray.length);
if(storage.getBlockMSBArray() != null) {
this.setBlockMSBArray(new NibbleArray(block16BArray.length, 4));
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
}
arrayLen = block16BArray.length;
if (ModStatus.isNEIDMetadataExtended) {
@@ -52,31 +50,31 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
System.arraycopy(storage.getBlockLSBArray(), 0, blockLSBArray, 0, blockLSBArray.length);
if(storage.getBlockMSBArray() != null) {
this.setBlockMSBArray(new NibbleArray(blockLSBArray.length, 4));
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
}
arrayLen = blockLSBArray.length;
}
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray((ExtendedNibbleArray) storage.getMetadataArray(), (ExtendedNibbleArray)this.getMetadataArray());
copyNibbleArray((ExtendedNibbleArray) storage.getBlocklightArray(), (ExtendedNibbleArray)this.getBlocklightArray());
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray(storage.getMetadataArray(), this.getMetadataArray());
copyNibbleArray(storage.getBlocklightArray(), this.getBlocklightArray());
if(storage.getSkylightArray() != null) {
hasSky = true;
if(this.getSkylightArray() == null) {
this.setSkylightArray(new NibbleArray(arrayLen, 4));
}
copyNibbleArray((ExtendedNibbleArray) storage.getSkylightArray(), (ExtendedNibbleArray) this.getSkylightArray());
copyNibbleArray(storage.getSkylightArray(), this.getSkylightArray());
}
}
((ExtendedBlockStorageAccessor) this).setBlockRefCount(((ExtendedBlockStorageAccessor) storage).getBlockRefCount());
this.blockRefCount = storage.blockRefCount;
}
private static void copyNibbleArray(ExtendedNibbleArray srcArray, ExtendedNibbleArray dstArray) {
private static void copyNibbleArray(NibbleArray srcArray, NibbleArray dstArray) {
if (srcArray == null || dstArray == null) {
throw new RuntimeException("NibbleArray is null src: " + (srcArray == null) + " dst: " + (dstArray == null));
}
final byte[] data = srcArray.getData();
System.arraycopy(data, 0, dstArray.getData(), 0, data.length);
final byte[] data = srcArray.data;
System.arraycopy(data, 0, dstArray.data, 0, data.length);
}
}
@@ -1,14 +1,17 @@
package com.gtnewhorizons.angelica.compat;
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import mods.battlegear2.Battlegear;
import net.dries007.holoInventory.HoloInventory;
import xonin.backhand.Backhand;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ModStatus {
public static final Logger LOGGER = LogManager.getLogger("ModCompat");
public static BackhandReflectionCompat backhandCompat;
/**
* Mixin Version
*/
@@ -17,7 +20,6 @@ public class ModStatus {
* ASM Version
*/
public static boolean isOldNEIDLoaded;
public static boolean isBetterCrashesLoaded;
public static boolean isNEIDMetadataExtended;
public static boolean isLotrLoaded;
@@ -30,8 +32,14 @@ public class ModStatus {
public static boolean isThaumcraftLoaded;
public static boolean isThaumicHorizonsLoaded;
public static boolean isBaublesLoaded;
public static boolean isFluidLoggedLoaded;
public static void preInit(){
isBackhandLoaded = Loader.isModLoaded("backhand");
if(isBackhandLoaded) {
isBackhandLoaded = BackhandReflectionCompat.isBackhandLoaded();
}
isBetterCrashesLoaded = Loader.isModLoaded("bettercrashes");
isNEIDLoaded = Loader.isModLoaded("neid");
isOldNEIDLoaded = Loader.isModLoaded("notenoughIDs");
@@ -41,17 +49,12 @@ public class ModStatus {
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
isBackhandLoaded = Loader.isModLoaded("backhand");
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
isBaublesLoaded = Loader.isModLoaded("Baubles");
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
if (isHoloInventoryLoaded){
isHoloInventoryLoaded = new DefaultArtifactVersion("2.4.4-GTNH")
.compareTo(
LoadControllerHelper.getOwningMod(HoloInventory.class).getProcessedVersion()
) <= 0;
}
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
// remove compat with original release of BG2
if (isBattlegearLoaded){
@@ -61,17 +64,9 @@ public class ModStatus {
) <= 0;
}
if (isBackhandLoaded){
isBackhandLoaded = new DefaultArtifactVersion("1.4.1")
.compareTo(
LoadControllerHelper.getOwningMod(Backhand.class).getProcessedVersion()
) <= 0;
}
isNEIDMetadataExtended = false;
if (isNEIDLoaded) {
int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
final int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
if (majorVersion >= 2) {
isNEIDMetadataExtended = true;
}
@@ -0,0 +1,76 @@
package com.gtnewhorizons.angelica.compat.backhand;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.relauncher.ReflectionHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
public class BackhandReflectionCompat {
private static final String BACKHAND_CLASS = "xonin.backhand.Backhand";
private static final String ITEM_RENDERER_HOOKS_CLASS = "xonin.backhand.client.hooks.ItemRendererHooks";
private static final String BACKHAND_UTILS_CLASS = "xonin.backhand.api.core.BackhandUtils";
private static boolean isLoaded;
private static final MethodHandle renderOffhandReturn;
private static final MethodHandle getOffhandItem;
static {
MethodHandle renderOffhandReturnTemp = null;
MethodHandle getOffhandItemTemp = null;
try {
final Class<?> backhandClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_CLASS);
final boolean versionCheck = new DefaultArtifactVersion("1.6.9").compareTo(LoadControllerHelper.getOwningMod(backhandClass).getProcessedVersion()) <= 0;
if (versionCheck) {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
final Class<?> itemRendererHooksClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), ITEM_RENDERER_HOOKS_CLASS);
final Class<?> backhandUtilsClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_UTILS_CLASS);
renderOffhandReturnTemp = lookup.findStatic(itemRendererHooksClass, "renderOffhandReturn", MethodType.methodType(void.class, float.class));
getOffhandItemTemp = lookup.findStatic(backhandUtilsClass, "getOffhandItem", MethodType.methodType(ItemStack.class, EntityPlayer.class));
isLoaded = true;
LOGGER.info("Backhand compat loaded");
}
} catch (Exception e) {
LOGGER.error("Failed to load Backhand compat", e);
isLoaded = false;
}
renderOffhandReturn = renderOffhandReturnTemp;
getOffhandItem = getOffhandItemTemp;
}
public static boolean isBackhandLoaded() {
return isLoaded;
}
public static void renderOffhand(float partialTicks) {
if (isLoaded && renderOffhandReturn != null) {
try {
renderOffhandReturn.invokeExact(partialTicks);
} catch (Throwable e) {
LOGGER.error("Failed to invoke Backhand renderOffhandReturn", e);
isLoaded = false;
}
}
}
public static ItemStack getOffhandItem(EntityPlayer player) {
if (isLoaded && getOffhandItem != null) {
try {
return (ItemStack) getOffhandItem.invokeExact(player);
} catch (Throwable e) {
LOGGER.error("Failed to invoke Backhand getOffhandItem", e);
isLoaded = false;
}
}
return null;
}
}
@@ -0,0 +1,15 @@
package com.gtnewhorizons.angelica.compat.battlegear2;
import mods.battlegear2.api.core.IInventoryPlayerBattle;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
/**
* This class exists to isolate classes that exist in Battlegear 2 but not
* in Battlegear 2 for Backhand in order to avoid class loading exceptions.
*/
public class Battlegear2Compat {
public static ItemStack getBattlegear2Offhand(EntityPlayer player) {
return ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
}
}
@@ -0,0 +1,93 @@
package com.gtnewhorizons.angelica.compat.holoinventory;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
public class HoloInventoryReflectionCompat {
private static final String RENDERER_CLASS = "net.dries007.holoInventory.client.Renderer";
private static boolean isLoaded;
private static final MethodHandle renderEventPostMethod;
private static final MethodHandle renderEventMethod;
private static final MethodHandle angelicaOverrideSetter;
static {
MethodHandle renderEventPostMethodTemp = null;
MethodHandle renderEventMethodTemp = null;
MethodHandle angelicaOverrideSetterTemp = null;
final Object rendererInstance;
final Class<?> rendererClass;
try {
rendererClass = Class.forName(RENDERER_CLASS);
// Get and cache the Renderer.INSTANCE
final Field instanceField = rendererClass.getDeclaredField("INSTANCE");
instanceField.setAccessible(true);
rendererInstance = instanceField.get(null);
final MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
renderEventPostMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.Post.class));
} catch (NoSuchMethodException e) {
// Fallback for 2.5.0-GTNH and earlier
renderEventMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.class));
}
final Field angelicaOverrideField = rendererClass.getDeclaredField("angelicaOverride");
angelicaOverrideField.setAccessible(true);
angelicaOverrideSetterTemp = lookup.unreflectSetter(angelicaOverrideField);
// Bind the method handles to the instance
if (rendererInstance != null) {
if (renderEventPostMethodTemp != null) {
renderEventPostMethodTemp = renderEventPostMethodTemp.bindTo(rendererInstance);
}
if (renderEventMethodTemp != null) {
renderEventMethodTemp = renderEventMethodTemp.bindTo(rendererInstance);
}
angelicaOverrideSetterTemp = angelicaOverrideSetterTemp.bindTo(rendererInstance);
}
isLoaded = true;
LOGGER.info("Successfully initialized HoloInventory compatibility layer");
} catch (Exception e) {
LOGGER.warn("Failed to initialize HoloInventory compatibility layer", e);
isLoaded = false;
}
renderEventPostMethod = renderEventPostMethodTemp;
renderEventMethod = renderEventMethodTemp;
angelicaOverrideSetter = angelicaOverrideSetterTemp;
}
public static void renderEvent(RenderGameOverlayEvent event) {
if (!isLoaded) return;
try {
if (event instanceof RenderGameOverlayEvent.Post && renderEventPostMethod != null) {
renderEventPostMethod.invokeExact((RenderGameOverlayEvent.Post) event);
} else {
renderEventMethod.invokeExact(event);
}
} catch (Throwable e) {
LOGGER.error("Failed to invoke HoloInventory renderEvent", e);
isLoaded = false;
}
}
public static void setAngelicaOverride(boolean value) {
if (!isLoaded) return;
try {
angelicaOverrideSetter.invokeExact(value);
} catch (Throwable e) {
LOGGER.error("Failed to set HoloInventory angelicaOverride", e);
isLoaded = false;
}
}
}
@@ -1,327 +0,0 @@
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.Pointer.BITS64;
import it.unimi.dsi.fastutil.longs.LongPredicate;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
/**
* Backported from LWJGL3 under the BSD 3-Clause "New" or "Revised" License license
*
* <p>This class provides functionality for managing native memory.
*
* <p>All methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback
* implementations make use of reflection and, in the worst-case, JNI.</p>
*
* <p>Method names in this class are prefixed with {@code mem} to avoid ambiguities when used with static imports.</p>
*/
public class CompatMemoryUtil {
public static final long NULL = 0;
static final sun.misc.Unsafe UNSAFE;
private static final Class<? extends ByteBuffer> BUFFER_BYTE;
private static final Class<? extends IntBuffer> BUFFER_INT;
private static final Class<? extends FloatBuffer> BUFFER_FLOAT;
private static final long MARK;
private static final long POSITION;
private static final long LIMIT;
private static final long CAPACITY;
private static final long ADDRESS;
static {
ByteBuffer bb = ByteBuffer.allocateDirect(0).order(ByteOrder.nativeOrder());
BUFFER_BYTE = bb.getClass();
BUFFER_INT = bb.asIntBuffer().getClass();
BUFFER_FLOAT = bb.asFloatBuffer().getClass();
UNSAFE = getUnsafeInstance();
MARK = getMarkOffset();
POSITION = getPositionOffset();
LIMIT = getLimitOffset();
CAPACITY = getCapacityOffset();
ADDRESS = getAddressOffset();
}
public static ByteBuffer memReallocDirect(ByteBuffer old, int capacity) {
ByteBuffer newBuf = BufferUtils.createByteBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
public static IntBuffer memReallocDirect(IntBuffer old, int capacity) {
IntBuffer newBuf = BufferUtils.createIntBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
public static FloatBuffer memReallocDirect(FloatBuffer old, int capacity) {
FloatBuffer newBuf = BufferUtils.createFloatBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
/* -------------------------------------
-------------------------------------
UNSAFE MEMORY ACCESS API
-------------------------------------
------------------------------------- */
private static final int FILL_PATTERN_32 = Integer.divideUnsigned(-1, 255);
private static final long FILL_PATTERN_64 = Long.divideUnsigned(-1L, 255L);
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
* @param bytes the number of bytes to set
*/
public static void memSet(long ptr, int value, long bytes) {
if (/*DEBUG*/ false && (ptr == NULL || bytes < 0)) {
throw new IllegalArgumentException();
}
/*
- Unsafe.setMemory is very slow.
- A custom Java loop is fastest at small sizes, approximately up to 256 bytes.
- The native memset becomes fastest at bigger sizes, when the JNI overhead becomes negligible.
TODO: verify this
*/
//UNSAFE.setMemory(ptr, bytes, (byte)(value & 0xFF));
//if (bytes < 256L) {
int p = (int)ptr;
if (BITS64) {
if ((p & 7) == 0) {
memSet64(ptr, value, (int)bytes & 0xFF);
return;
}
} else {
if ((p & 3) == 0) {
memSet32(p, value, (int)bytes & 0xFF);
return;
}
}
//}
//nmemset(ptr, value, bytes);
}
private static void memSet64(long ptr, int value, int bytes) {
int aligned = bytes & ~7;
// Aligned body
long valuel = (value & 0xFF) * FILL_PATTERN_64;
for (int i = 0; i < aligned; i += 8) {
UNSAFE.putLong(null, ptr + i, valuel);
}
// Unaligned tail
byte valueb = (byte)(value & 0xFF);
for (int i = aligned; i < bytes; i++) {
UNSAFE.putByte(null, ptr + i, valueb);
}
}
private static void memSet32(int ptr, int value, int bytes) {
int aligned = bytes & ~3;
// Aligned body
int vi = (value & 0xFF) * FILL_PATTERN_32;
for (int i = 0; i < aligned; i += 4) {
UNSAFE.putInt(null, (ptr + i) & 0xFFFF_FFFFL, vi);
}
// Unaligned tail
byte vb = (byte)(value & 0xFF);
for (int i = aligned; i < bytes; i++) {
UNSAFE.putByte(null, (ptr + i) & 0xFFFF_FFFFL, vb);
}
}
public static boolean memGetBoolean(long ptr) { return UNSAFE.getByte(null, ptr) != 0; }
public static byte memGetByte(long ptr) { return UNSAFE.getByte(null, ptr); }
public static short memGetShort(long ptr) { return UNSAFE.getShort(null, ptr); }
public static int memGetInt(long ptr) { return UNSAFE.getInt(null, ptr); }
public static long memGetLong(long ptr) { return UNSAFE.getLong(null, ptr); }
public static float memGetFloat(long ptr) { return UNSAFE.getFloat(null, ptr); }
public static double memGetDouble(long ptr) { return UNSAFE.getDouble(null, ptr); }
public static void memPutByte(long ptr, byte value) { UNSAFE.putByte(null, ptr, value); }
public static void memPutShort(long ptr, short value) { UNSAFE.putShort(null, ptr, value); }
public static void memPutInt(long ptr, int value) { UNSAFE.putInt(null, ptr, value); }
public static void memPutLong(long ptr, long value) { UNSAFE.putLong(null, ptr, value); }
public static void memPutFloat(long ptr, float value) { UNSAFE.putFloat(null, ptr, value); }
public static void memPutDouble(long ptr, double value) { UNSAFE.putDouble(null, ptr, value); }
// -------------------------------------------------
// -------------------------------------------------
// -------------------------------------------------
private static sun.misc.Unsafe getUnsafeInstance() {
java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields();
/*
Different runtimes use different names for the Unsafe singleton,
so we cannot use .getDeclaredField and we scan instead. For example:
Oracle: theUnsafe
PERC : m_unsafe_instance
Android: THE_ONE
*/
for (java.lang.reflect.Field field : fields) {
if (!field.getType().equals(sun.misc.Unsafe.class)) {
continue;
}
int modifiers = field.getModifiers();
if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) {
continue;
}
try {
field.setAccessible(true);
return (sun.misc.Unsafe)field.get(null);
} catch (Exception ignored) {
}
break;
}
throw new UnsupportedOperationException("LWJGL requires sun.misc.Unsafe to be available.");
}
/**
* Recursively searches an object and its superclasses for a field with the same name and type. If the field isn't
* public, it may not be stable across library/JVM versions, and these calls are likely not portable. You have been
* warned!
*/
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, String name) {
Class<?> c = containerType;
while (c != Object.class) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
continue;
}
long offset = UNSAFE.objectFieldOffset(field);
if (field.getName().equals(name)) {
return offset;
}
}
c = c.getSuperclass();
}
throw new UnsupportedOperationException("Failed to find field offset in class.");
}
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, LongPredicate predicate) {
Class<?> c = containerType;
while (c != Object.class) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
continue;
}
long offset = UNSAFE.objectFieldOffset(field);
if (predicate.test(offset)) {
return offset;
}
}
c = c.getSuperclass();
}
throw new UnsupportedOperationException("Failed to find field offset in class.");
}
private static long getFieldOffsetInt(Object container, int value) {
return getFieldOffset(container.getClass(), int.class, offset -> UNSAFE.getInt(container, offset) == value);
}
private static long getAddressOffset() {
final ByteBuffer bb = ByteBuffer.allocateDirect(0);
return getFieldOffset(bb.getClass(), long.class, "address");
// TODO: do this portably
// long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL & (BITS32 ? 0xFFFF_FFFFL : 0xFFFF_FFFF_FFFF_FFFFL);
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0));
//return getFieldOffset(bb.getClass(), long.class, offset -> UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS);
}
private static final int MAGIC_CAPACITY = 0x0D15EA5E;
private static final int MAGIC_POSITION = 0x00FACADE;
private static long getMarkOffset() {
// TODO: is this actually reliable without NewDirectByteBuffer?
ByteBuffer bb = ByteBuffer.allocateDirect(0);
return getFieldOffsetInt(bb, -1);
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(1L, 0));
// return getFieldOffsetInt(bb, -1);
}
private static long getPositionOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.position(MAGIC_POSITION);
return getFieldOffsetInt(bb, MAGIC_POSITION);
}
private static long getLimitOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.limit(MAGIC_POSITION);
return getFieldOffsetInt(bb, MAGIC_POSITION);
}
private static long getCapacityOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.limit(0);
return getFieldOffsetInt(bb, MAGIC_CAPACITY);
}
static IntBuffer wrapBufferInt(long address, int capacity) {
IntBuffer buffer;
try {
buffer = (IntBuffer)UNSAFE.allocateInstance(BUFFER_INT);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(buffer, ADDRESS, address);
UNSAFE.putInt(buffer, MARK, -1);
UNSAFE.putInt(buffer, LIMIT, capacity);
UNSAFE.putInt(buffer, CAPACITY, capacity);
return buffer;
}
static FloatBuffer wrapBufferFloat(long address, int capacity) {
FloatBuffer buffer;
try {
buffer = (FloatBuffer)UNSAFE.allocateInstance(BUFFER_FLOAT);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(buffer, ADDRESS, address);
UNSAFE.putInt(buffer, MARK, -1);
UNSAFE.putInt(buffer, LIMIT, capacity);
UNSAFE.putInt(buffer, CAPACITY, capacity);
return buffer;
}
}
@@ -1,382 +0,0 @@
/*
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
*/
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutDouble;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutFloat;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutInt;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutLong;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutShort;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memSet;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferFloat;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferInt;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.BufferUtils;
import org.lwjgl.MemoryUtil;
/**
* An off-heap memory stack.
*
* <p>This class should be used in a thread-local manner for stack allocations.</p>
*
* @ see Configuration#STACK_SIZE
* @ see Configuration#DEBUG_STACK
*/
@SuppressWarnings("LombokGetterMayBeUsed")
public class MemoryStack extends Pointer.Default implements AutoCloseable {
private static final int DEFAULT_STACK_SIZE = /*Configuration.STACK_SIZE.get(64)*/ 64 * 1024;
private static final int DEFAULT_STACK_FRAMES = 8;
private static final ThreadLocal<MemoryStack> TLS = ThreadLocal.withInitial(MemoryStack::create);
static {
if (DEFAULT_STACK_SIZE < 0) {
throw new IllegalStateException("Invalid stack size.");
}
}
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final @Nullable ByteBuffer container;
private final int size;
private int pointer;
private int[] frames;
protected int frameIndex;
/**
* Creates a new {@code MemoryStack} backed by the specified memory region.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param container the backing memory buffer, may be null
* @param address the backing memory address
* @param size the backing memory size
*/
protected MemoryStack(@Nullable ByteBuffer container, long address, int size) {
super(address);
this.container = container;
this.size = size;
this.pointer = size;
this.frames = new int[DEFAULT_STACK_FRAMES];
}
/**
* Creates a new {@code MemoryStack} with the default size.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*/
public static MemoryStack create() {
return create(DEFAULT_STACK_SIZE);
}
/**
* Creates a new {@code MemoryStack} with the specified size.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param capacity the maximum number of bytes that may be allocated on the stack
*/
public static MemoryStack create(int capacity) {
return create(BufferUtils.createByteBuffer(capacity));
}
/**
* Creates a new {@code MemoryStack} backed by the specified memory buffer.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param buffer the backing memory buffer
*/
public static MemoryStack create(ByteBuffer buffer) {
long address = MemoryUtil.getAddress(buffer);
int size = buffer.remaining();
return new MemoryStack(buffer, address, size);
}
/**
* Creates a new {@code MemoryStack} backed by the specified memory region.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param address the backing memory address
* @param size the backing memory size
*/
public static MemoryStack ncreate(long address, int size) {
return new MemoryStack(null, address, size);
}
/**
* Stores the current stack pointer and pushes a new frame to the stack.
*
* <p>This method should be called when entering a method, before doing any stack allocations. When exiting a method, call the {@link #pop} method to
* restore the previous stack frame.</p>
*
* <p>Pairs of push/pop calls may be nested. Care must be taken to:</p>
* <ul>
* <li>match every push with a pop</li>
* <li>not call pop before push has been called at least once</li>
* <li>not nest push calls to more than the maximum supported depth</li>
* </ul>
*
* @return this stack
*/
public MemoryStack push() {
if (frameIndex == frames.length) {
frameOverflow();
}
frames[frameIndex++] = pointer;
return this;
}
private void frameOverflow() {
if (/*DEBUG*/ false) {
AngelicaTweaker.LOGGER.warn("[WARNING] Out of frame stack space (" + frames.length + ") in thread: " + Thread.currentThread());
}
frames = Arrays.copyOf(frames, frames.length * 3 / 2);
}
/**
* Pops the current stack frame and moves the stack pointer to the end of the previous stack frame.
*
* @return this stack
*/
public MemoryStack pop() {
pointer = frames[--frameIndex];
return this;
}
/**
* Calls {@link #pop} on this {@code MemoryStack}.
*
* <p>This method should not be used directly. It is called automatically when the {@code MemoryStack} is used as a resource in a try-with-resources
* statement.</p>
*/
@Override
public void close() {
//noinspection resource
pop();
}
/**
* Returns the address of the backing off-heap memory.
*
* <p>The stack grows "downwards", so the bottom of the stack is at {@code address + size}, while the top is at {@code address}.</p>
*/
public long getAddress() {
return address;
}
/**
* Returns the size of the backing off-heap memory.
*
* <p>This is the maximum number of bytes that may be allocated on the stack.</p>
*/
public int getSize() {
return size;
}
/**
* Returns the current frame index.
*
* <p>This is the current number of nested {@link #push} calls.</p>
*/
public int getFrameIndex() {
return frameIndex;
}
/** Returns the memory address at the current stack pointer. */
public long getPointerAddress() {
return address + (pointer & 0xFFFF_FFFFL);
}
/**
* Returns the current stack pointer.
*
* <p>The stack grows "downwards", so when the stack is empty {@code pointer} is equal to {@code size}. On every allocation {@code pointer} is reduced by
* the allocated size (after alignment) and {@code address + pointer} points to the first byte of the last allocation.</p>
*
* <p>Effectively, this methods returns how many more bytes may be allocated on the stack.</p>
*/
public int getPointer() {
return pointer;
}
/**
* Sets the current stack pointer.
*
* <p>This method directly manipulates the stack pointer. Using it irresponsibly may break the internal state of the stack. It should only be used in rare
* cases or in auto-generated code.</p>
*/
public void setPointer(int pointer) {
if (/*CHECKS*/ false) {
checkPointer(pointer);
}
this.pointer = pointer;
}
private void checkPointer(int pointer) {
if (pointer < 0 || size < pointer) {
throw new IndexOutOfBoundsException("Invalid stack pointer");
}
}
private static void checkAlignment(int alignment) {
if (Integer.bitCount(alignment) != 1) {
throw new IllegalArgumentException("Alignment must be a power-of-two value.");
}
}
/**
* Calls {@link #nmalloc(int, int)} with {@code alignment} equal to {@link Pointer#POINTER_SIZE POINTER_SIZE}.
*
* @param size the allocation size
*
* @return the memory address on the stack for the requested allocation
*/
public long nmalloc(int size) {
return nmalloc(POINTER_SIZE, size);
}
/**
* Allocates a block of {@code size} bytes of memory on the stack. The content of the newly allocated block of memory is not initialized, remaining with
* indeterminate values.
*
* @param alignment the required alignment
* @param size the allocation size
*
* @return the memory address on the stack for the requested allocation
*/
public long nmalloc(int alignment, int size) {
// Align address to the specified alignment
long address = (this.address + pointer - size) & ~Integer.toUnsignedLong(alignment - 1);
pointer = (int)(address - this.address);
if (/*CHECKS*/ false && pointer < 0) {
throw new OutOfMemoryError("Out of stack space.");
}
return address;
}
/**
* Allocates a block of memory on the stack for an array of {@code num} elements, each of them {@code size} bytes long, and initializes all its bits to
* zero.
*
* @param alignment the required element alignment
* @param num num the number of elements to allocate
* @param size the size of each element
*
* @return the memory address on the stack for the requested allocation
*/
public long ncalloc(int alignment, int num, int size) {
int bytes = num * size;
long address = nmalloc(alignment, bytes);
memSet(address, 0, bytes);
return address;
}
// -------------------------------------------------
/** Unsafe version of {@link #shorts(short)}. */
public long nshort(short value) {
long a = nmalloc(2, 2);
memPutShort(a, value);
return a;
}
// -------------------------------------------------
/** Int version of {@link #malloc(int)}. */
public IntBuffer mallocInt(int size) { return wrapBufferInt(nmalloc(4, size << 2), size); }
/** Unsafe version of {@link #ints(int)}. */
public long nint(int value) {
long a = nmalloc(4, 4);
memPutInt(a, value);
return a;
}
// -------------------------------------------------
/** Unsafe version of {@link #longs(long)}. */
public long nlong(long value) {
long a = nmalloc(8, 8);
memPutLong(a, value);
return a;
}
// -------------------------------------------------
/** Float version of {@link #malloc(int)}. */
public FloatBuffer mallocFloat(int size) { return wrapBufferFloat(nmalloc(4, size << 2), size); }
/** Unsafe version of {@link #floats(float)}. */
public long nfloat(float value) {
long a = nmalloc(4, 4);
memPutFloat(a, value);
return a;
}
// -------------------------------------------------
/** Unsafe version of {@link #doubles(double)}. */
public long ndouble(double value) {
long a = nmalloc(8, 8);
memPutDouble(a, value);
return a;
}
// -------------------------------------------------
// -------------------------------------------------
// -------------------------------------------------
/** Returns the stack of the current thread. */
public static MemoryStack stackGet() {
return TLS.get();
}
/**
* Calls {@link #push} on the stack of the current thread.
*
* @return the stack of the current thread.
*/
public static MemoryStack stackPush() {
return stackGet().push();
}
/**
* Calls {@link #pop} on the stack of the current thread.
*
* @return the stack of the current thread.
*/
public static MemoryStack stackPop() {
return stackGet().pop();
}
/** Thread-local version of {@link #nmalloc(int)}. */
public static long nstackMalloc(int size) { return stackGet().nmalloc(size); }
/** Thread-local version of {@link #nmalloc(int, int)}. */
public static long nstackMalloc(int alignment, int size) { return stackGet().nmalloc(alignment, size); }
/** Thread-local version of {@link #ncalloc}. */
public static long nstackCalloc(int alignment, int num, int size) { return stackGet().ncalloc(alignment, num, size); }
// -------------------------------------------------
}
@@ -1,93 +0,0 @@
/*
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
*/
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.NULL;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.UNSAFE;
import static org.lwjgl.LWJGLUtil.CHECKS;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
/**
* Pointer interface.
*
* <p>LWJGL can run on both 32bit and 64bit architectures. Since LWJGL applications deal with native memory directly, this interface provides necessary
* information about the underlying architecture of the running JVM process.</p>
*
* <p>When interacting with native functions, pointer values are mapped to Java {@code long}. LWJGL automatically converts long values to the correct pointer
* addresses when used in native code. Native functions sometimes require arrays of pointer values; the {@link PointerBuffer} class may be used for that
* purpose. It has an API similar to a {@link java.nio.LongBuffer} but handles pointer casts automatically.</p>
*/
public interface Pointer {
/** The pointer size in bytes. Will be 4 on a 32bit JVM and 8 on a 64bit one. */
int POINTER_SIZE = UNSAFE.addressSize();
/** The pointer size power-of-two. Will be 2 on a 32bit JVM and 3 on a 64bit one. */
int POINTER_SHIFT = POINTER_SIZE == 8 ? 3 : 2;
/** The value of {@code sizeof(long)} for the current platform. */
int CLONG_SIZE = POINTER_SIZE == 8 && Util.getOSType() == Util.EnumOS.WINDOWS ? 4 : POINTER_SIZE;
/** The value of {@code sizeof(long)} as a power-of-two. */
int CLONG_SHIFT = CLONG_SIZE == 8 ? 3 : 2;
/** Will be true on a 32bit JVM. */
boolean BITS32 = POINTER_SIZE * 8 == 32;
/** Will be true on a 64bit JVM. */
boolean BITS64 = POINTER_SIZE * 8 == 64;
/**
* Returns the raw pointer address as a {@code long} value.
*
* @return the pointer address
*/
long address();
/** Default {@link Pointer} implementation. */
abstract class Default implements Pointer {
// Removed final due to JDK-8139758. TODO: Restore if the fix is backported to JDK 8.
protected long address;
protected Default(long address) {
if (CHECKS && address == NULL) {
throw new NullPointerException();
}
this.address = address;
}
@Override
public long address() {
return address;
}
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pointer)) {
return false;
}
Pointer that = (Pointer)o;
return address == that.address();
}
public int hashCode() {
return (int)(address ^ (address >>> 32));
}
@Override
public String toString() {
return String.format("%s pointer [0x%X]", getClass().getSimpleName(), address);
}
}
}
@@ -15,6 +15,7 @@ public class ByteBufferBackedInputStream extends InputStream {
this.buf = buf;
}
@Override
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
@@ -40,6 +40,7 @@ public class ChunkPos {
return (long)x & 4294967295L | ((long)z & 4294967295L) << 32;
}
@Override
public int hashCode()
{
final int i = 1664525 * this.x + 1013904223;
@@ -47,6 +48,7 @@ public class ChunkPos {
return i ^ j;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
@@ -1,10 +1,23 @@
package com.gtnewhorizons.angelica.compat.mojang;
import com.gtnewhorizons.angelica.compat.ModStatus;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
public enum InteractionHand {
MAIN_HAND,
OFF_HAND;
InteractionHand() {
}
public ItemStack getItemInHand(EntityPlayer player){
if (ModStatus.isBackhandLoaded && this == InteractionHand.OFF_HAND){ // off hand (requires backhand)
return ModStatus.backhandCompat.getOffhandItem(player);
}
else { // main hand
return player.getHeldItem();
}
}
}
@@ -1,6 +1,5 @@
package com.gtnewhorizons.angelica.compat.mojang;
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@@ -8,6 +7,8 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.imageio.ImageIO;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import lombok.Getter;
import net.coderbot.iris.Iris;
import org.lwjgl.opengl.GL11;
@@ -58,15 +59,20 @@ public class NativeImage extends BufferedImage {
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
// final int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_HEIGHT);
try (final MemoryStack stack = MemoryStack.stackPush()) {
final IntBuffer buffer = stack.mallocInt(size);
final IntBuffer buffer = MemoryUtilities.memAllocInt(size);
try {
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, level, format.glFormat, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
int[] data = new int[size];
buffer.get(data);
setRGB(0, 0, width, height, data, 0, width);
} finally {
MemoryUtilities.memFree(buffer);
}
}
public void writeToFile(File file) throws IOException{
try {
ImageIO.write(this, "png", file);
@@ -104,18 +110,15 @@ public class NativeImage extends BufferedImage {
public enum Format {
RGBA(4, GL11.GL_RGBA, BufferedImage.TYPE_INT_ARGB),
RGB(3, GL11.GL_RGB, BufferedImage.TYPE_INT_RGB);
RGBA(4, GL11.GL_RGBA),
RGB(3, GL11.GL_RGB);
private final int components;
private final int glFormat;
private final int imageType;
Format(int components, int glFormat, int imageType) {
Format(int components, int glFormat) {
this.components = components;
this.glFormat = glFormat;
this.imageType = imageType;
}
}
@@ -1,63 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
public class BufferWriter {
// Adapted from Neodymium
private final ByteBuffer buf;
private final FloatBuffer floatBuffer;
private final ShortBuffer shortBuffer;
private final IntBuffer intBuffer;
public BufferWriter(ByteBuffer buf) {
this.buf = buf;
this.floatBuffer = buf.asFloatBuffer();
this.shortBuffer = buf.asShortBuffer();
this.intBuffer = buf.asIntBuffer();
}
private void incrementPosition(int add) {
buf.position(buf.position() + add);
floatBuffer.position(buf.position() / 4);
shortBuffer.position(buf.position() / 2);
intBuffer.position(buf.position() / 4);
}
public void writeFloat(float x) {
try {
floatBuffer.put(x);
incrementPosition(4);
} catch(Exception e){
e.printStackTrace();
}
}
public void writeInt(int x) {
intBuffer.put(x);
incrementPosition(4);
}
public void writeByte(byte x) {
buf.put(x); // this increments the buffer position by 1
incrementPosition(0);
}
public int position() {
return buf.position();
}
public void writeShort(short s) {
shortBuffer.put(s);
incrementPosition(2);
}
}
@@ -1,47 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd.writers;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
import java.nio.ByteBuffer;
public class ItemVBOQuadWriter implements IWriteQuads {
private boolean direct;
public ItemVBOQuadWriter() {
init(false);
}
public void init(boolean direct) {
// This would need to be re-initialized if the direct vs indirect changes
this.direct = direct;
}
@Override
public void writeQuad(QuadView quad, ByteBuffer buf) {
if(direct) {
writeQuadDirect(quad, buf);
} else {
writeQuadIndirect(quad, buf);
}
}
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
throw new UnsupportedOperationException("Direct mode not supported yet");
}
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
for(int idx = 0; idx < 4; ++idx) {
// Position
buf.putFloat(quad.getX(idx));
buf.putFloat(quad.getY(idx));
buf.putFloat(quad.getZ(idx));
// Texture
buf.putFloat(quad.getTexU(idx));
buf.putFloat(quad.getTexV(idx));
// Normals + Padding
buf.putInt(quad.getNormal(idx));
}
}
}
@@ -1,47 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd.writers;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
import java.nio.ByteBuffer;
public class PositionColorTextureQuadWriter implements IWriteQuads {
private boolean direct;
public PositionColorTextureQuadWriter() {
init(false);
}
public void init(boolean direct) {
// This would need to be re-initialized if the direct vs indirect changes
this.direct = direct;
}
@Override
public void writeQuad(QuadView quad, ByteBuffer buf) {
if(direct) {
writeQuadDirect(quad, buf);
} else {
writeQuadIndirect(quad, buf);
}
}
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
throw new UnsupportedOperationException("Direct mode not supported yet");
}
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
for(int idx = 0; idx < 4; ++idx) {
// Position
buf.putFloat(quad.getX(idx));
buf.putFloat(quad.getY(idx));
buf.putFloat(quad.getZ(idx));
// Color
buf.putInt(quad.getColor(idx));
// Texture
buf.putFloat(quad.getTexU(idx));
buf.putFloat(quad.getTexV(idx));
}
}
}
@@ -1,40 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd.writers;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
import java.nio.ByteBuffer;
public class PositionQuadWriter implements IWriteQuads {
private boolean direct;
public PositionQuadWriter() {
init(false);
}
public void init(boolean direct) {
// This would need to be re-initialized if the direct vs indirect changes
this.direct = direct;
}
@Override
public void writeQuad(QuadView quad, ByteBuffer buf) {
if(direct) {
writeQuadDirect(quad, buf);
} else {
writeQuadIndirect(quad, buf);
}
}
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
throw new UnsupportedOperationException("Direct mode not supported yet");
}
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
for(int idx = 0; idx < 4; ++idx) {
// Position
buf.putFloat(quad.getX(idx));
buf.putFloat(quad.getY(idx));
buf.putFloat(quad.getZ(idx));
}
}
}
@@ -1,49 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd.writers;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
import java.nio.ByteBuffer;
public class PositionTextureColorQuadWriter implements IWriteQuads {
private boolean direct;
public PositionTextureColorQuadWriter() {
init(false);
}
public void init(boolean direct) {
// This would need to be re-initialized if the direct vs indirect changes
this.direct = direct;
}
@Override
public void writeQuad(QuadView quad, ByteBuffer buf) {
if(direct) {
writeQuadDirect(quad, buf);
} else {
writeQuadIndirect(quad, buf);
}
}
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
throw new UnsupportedOperationException("Direct mode not supported yet");
}
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
for(int idx = 0; idx < 4; ++idx) {
// Position
buf.putFloat(quad.getX(idx));
buf.putFloat(quad.getY(idx));
buf.putFloat(quad.getZ(idx));
// Texture
buf.putFloat(quad.getTexU(idx));
buf.putFloat(quad.getTexV(idx));
// Color
buf.putInt(quad.getColor(idx));
}
}
}
@@ -1,44 +0,0 @@
package com.gtnewhorizons.angelica.compat.nd.writers;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
import java.nio.ByteBuffer;
public class PositionTextureQuadWriter implements IWriteQuads {
private boolean direct;
public PositionTextureQuadWriter() {
init(false);
}
public void init(boolean direct) {
// This would need to be re-initialized if the direct vs indirect changes
this.direct = direct;
}
@Override
public void writeQuad(QuadView quad, ByteBuffer buf) {
if(direct) {
writeQuadDirect(quad, buf);
} else {
writeQuadIndirect(quad, buf);
}
}
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
throw new UnsupportedOperationException("Direct mode not supported yet");
}
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
for(int idx = 0; idx < 4; ++idx) {
// Position
buf.putFloat(quad.getX(idx));
buf.putFloat(quad.getY(idx));
buf.putFloat(quad.getZ(idx));
// Texture
buf.putFloat(quad.getTexU(idx));
buf.putFloat(quad.getTexV(idx));
}
}
}
@@ -72,7 +72,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
private final MultiPhaseParameters phases;
private final int hash;
private final Optional<RenderLayer> affectedOutline;
private final boolean outline;
private MultiPhase(String name, VertexFormat vertexFormat, int drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, MultiPhaseParameters phases) {
super(name, vertexFormat, drawMode, expectedBufferSize, () -> {
@@ -84,7 +83,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.affectedOutline = phases.outlineMode == RenderLayer.OutlineMode.AFFECTS_OUTLINE ? phases.texture.getId().map((arg2) -> {
return getOutline(arg2, phases.cull);
}) : Optional.empty();
this.outline = phases.outlineMode == RenderLayer.OutlineMode.IS_OUTLINE;
this.hash = Objects.hash(new Object[]{super.hashCode(), phases});
}
@@ -97,20 +95,23 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return this.affectedOutline;
}
@Override
public boolean equals(@Nullable Object object) {
return this == object;
}
@Override
public int hashCode() {
return this.hash;
}
@Override
public String toString() {
return "RenderType[" + this.phases + ']';
}
static {
CACHE = new ObjectOpenCustomHashSet(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
CACHE = new ObjectOpenCustomHashSet<>(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
}
static enum HashStrategy implements Hash.Strategy<MultiPhase> {
@@ -168,6 +169,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.phases = ImmutableList.of(this.texture, this.transparency, this.diffuseLighting, this.shadeModel, this.alpha, this.depthTest, this.cull, this.lightmap, this.fog, this.layering, this.target, this.texturing, this.writeMaskState);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
@@ -179,10 +181,12 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
}
}
@Override
public int hashCode() {
return Objects.hash(new Object[]{this.phases, this.outlineMode});
}
@Override
public String toString() {
return "CompositeState[" + this.phases + ", outlineProperty=" + this.outlineMode + ']';
}
@@ -293,6 +297,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.name = name;
}
@Override
public String toString() {
return this.name;
}
@@ -11,7 +11,6 @@ import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
public abstract class RenderPhase {
@@ -98,6 +97,7 @@ public abstract class RenderPhase {
this.endAction.run();
}
@Override
public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
@@ -109,10 +109,12 @@ public abstract class RenderPhase {
}
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public String toString() {
return this.name;
}
@@ -22,7 +22,7 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableSodiumFluidRendering;
@Config.Comment("Inject QuadProvider rendering into some vanilla blocks")
@Config.Comment("Inject BakedModel rendering into some vanilla blocks")
@Config.DefaultBoolean(false)
@Config.RequiresMcRestart
public static boolean injectQPRendering;
@@ -38,7 +38,7 @@ public class AngelicaConfig {
public static boolean enableIris;
@Config.Comment("Enable MCPatcherForge features, still in Alpha. Individual features are toggled in mcpatcher.json")
@Config.DefaultBoolean(false)
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean enableMCPatcherForgeFeatures;
@@ -133,6 +133,11 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableDebugLogging;
@Config.Comment("Enables PBR atlas dumping")
@Config.DefaultBoolean(false)
@Config.Name("Enable PBR Debug")
public static boolean enablePBRDebug;
@Config.Comment("Enable Zoom")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
@@ -143,9 +148,26 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean optimizeInWorldItemRendering;
@Config.Comment("Max amount of display lists to cache for optimized item rendering. Higher number will use more VRAM")
@Config.DefaultInt(256)
@Config.RangeInt(min = 64, max = 1024)
public static int itemRendererDisplayListCacheSize;
@Config.Comment("Upper limit for the amount of VBO's to cache for optimized item rendering. Higher number can potentially use more VRAM.")
@Config.DefaultInt(512)
@Config.RangeInt(min = 256, max = 1024)
public static int itemRendererCacheSize;
@Config.Comment("Render distance for the spinning mob inside mod spawners")
@Config.DefaultDouble(16D)
@Config.RangeDouble(min = 16D, max = 64D)
public static double mobSpawnerRenderDistance;
@Config.Comment("Switches to an alternate FPS limiter that gives more stable frametimes, in exchange for slightly " +
"more latency. Will never introduce more than one frame of latency, and has a lower impact at higher framerates.")
@Config.DefaultBoolean(false)
public static boolean sleepBeforeSwap;
@Config.Comment("Allows unicode languages to use an odd gui scale")
@Config.DefaultBoolean(true)
public static boolean removeUnicodeEvenScaling;
@Config.Comment("Register HardcodedCustomUniforms in Iris Shaders. May help with compatibility in certain shader packs")
@Config.DefaultBoolean(false)
public static boolean enableHardcodedCustomUniforms;
}
@@ -6,6 +6,12 @@ import net.minecraft.client.gui.GuiScreen;
public class AngelicaGuiConfig extends SimpleGuiConfig {
public AngelicaGuiConfig(GuiScreen parent) throws ConfigException {
super(parent, AngelicaConfig.class, "angelica", "Angelica");
super(parent,
"angelica",
"Angelica",
true,
AngelicaConfig.class,
FontConfig.class
);
}
}
@@ -39,4 +39,9 @@ public class CompatConfig {
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fixImmersiveEngineering;
@Config.Comment("Apply fixes to Security Craft")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fixSecurityCraft;
}
@@ -0,0 +1,58 @@
package com.gtnewhorizons.angelica.config;
import com.gtnewhorizon.gtnhlib.config.Config;
@Config(modid = "angelica", category = "customfont")
public class FontConfig {
@Config.Comment("Whether or not to use custom fonts when drawing text.")
@Config.DefaultBoolean(false)
public static boolean enableCustomFont;
@Config.Comment("Name of the primary custom font. Best not to set it from here.")
@Config.DefaultString("(none)")
public static String customFontNamePrimary;
@Config.Comment("Name of the fallback custom font. Best not to set it from here.")
@Config.DefaultString("(none)")
public static String customFontNameFallback;
@Config.Comment("The quality at which custom fonts are rendered, making them less pixelated but increasing memory usage.")
@Config.DefaultInt(30)
@Config.RangeInt(min = 6, max = 72)
public static int customFontQuality;
@Config.Comment("Controls the distance at which the font's shadow is drawn.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0F, max = 2F)
public static float fontShadowOffset;
@Config.Comment("Influences the aspect ratio of each glyph.")
@Config.DefaultFloat(0F)
@Config.RangeFloat(min = -1F, max = 1F)
public static float glyphAspect;
@Config.Comment("Scale of each glyph, whitespace excluded.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0.1F, max = 3F)
public static float glyphScale;
@Config.Comment("Whitespace scale.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0.1F, max = 3F)
public static float whitespaceScale;
@Config.Comment("Adds extra spacing between glyphs.")
@Config.DefaultFloat(0F)
@Config.RangeFloat(min = -2F, max = 2F)
public static float glyphSpacing;
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
@Config.DefaultInt(2)
@Config.RangeInt(min = 0, max = 2)
public static int fontAAMode;
@Config.Comment("Affects font antialiasing sample spacing. Higher values increase blur.")
@Config.DefaultInt(7)
@Config.RangeInt(min = 1, max = 24)
public static int fontAAStrength;
}
@@ -28,9 +28,10 @@ import static org.lwjgl.opengl.GL20.glUniform1;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.*;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram;
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
import java.nio.FloatBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
@@ -131,7 +132,7 @@ public abstract class F3Graph {
// Load vertex buffer
vertBuf = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
try (final MemoryStack stack = MemoryStack.stackPush()) {
try (final MemoryStack stack = stackPush()) {
final FloatBuffer vertices = stack.mallocFloat(VERT_COUNT * VERT_FLOATS);
// Since we use a triangle strip, we only need 4 vertices. The quad extends to the top of the screen so spikes
// don't get truncated. The max height is replaced in the vert shader, no need to be precise.
@@ -6,19 +6,18 @@ import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
import com.gtnewhorizons.angelica.api.IDynamicLightProducer;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.battlegear2.Battlegear2Compat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import mods.battlegear2.api.core.IBattlePlayer;
import mods.battlegear2.api.core.IInventoryPlayerBattle;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.monster.EntityCreeper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
@@ -28,7 +27,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xonin.backhand.api.core.BackhandUtils;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -378,13 +376,13 @@ public class DynamicLights {
player instanceof IBattlePlayer battlePlayer &&
battlePlayer.battlegear2$isBattlemode()
) {
ItemStack offhand = ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
ItemStack offhand = Battlegear2Compat.getBattlegear2Offhand(player);
if (offhand != null) {
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
}
}
else if (ModStatus.isBackhandLoaded && living instanceof EntityPlayer player){
ItemStack offhand = BackhandUtils.getOffhandItem(player);
ItemStack offhand = ModStatus.backhandCompat.getOffhandItem(player);
if (offhand != null) {
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
}
@@ -7,9 +7,11 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -19,24 +21,42 @@ public class Feature {
GL13.GL_MULTISAMPLE_BIT, GL11.GL_PIXEL_MODE_BIT, GL11.GL_POINT_BIT, GL11.GL_POLYGON_BIT, GL11.GL_POLYGON_STIPPLE_BIT, GL11.GL_SCISSOR_BIT,
GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_TEXTURE_BIT, GL11.GL_TRANSFORM_BIT, GL11.GL_VIEWPORT_BIT };
static final Int2ObjectMap<Set<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
static final Int2ObjectMap<List<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
static Set<IStateStack<?>> maskToFeatures(int mask) {
static List<IStateStack<?>> maskToFeatures(int mask) {
if(maskToFeaturesMap.containsKey(mask)) {
return maskToFeaturesMap.get(mask);
}
final Set<IStateStack<?>> features = new HashSet<>();
for(int attrib : Feature.supportedAttribs) {
if((mask & attrib) == attrib) {
features.addAll(getFeatures(attrib));
}
}
maskToFeaturesMap.put(mask, features);
return features;
final List<IStateStack<?>> asList = new ArrayList<>(features);
maskToFeaturesMap.put(mask, asList);
return asList;
}
private static final Map<Integer, Set<IStateStack<?>>> attribToFeatures = new HashMap<>();
/**
* Helper method to add all texture gen states (S, T, R, Q) for all texture units to a collection.
* Used by both GL_ENABLE_BIT and GL_TEXTURE_BIT.
*/
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
collection.add(GLStateManager.textures.getTexGenSStates(i));
collection.add(GLStateManager.textures.getTexGenTStates(i));
collection.add(GLStateManager.textures.getTexGenRStates(i));
collection.add(GLStateManager.textures.getTexGenQStates(i));
}
}
static {
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST enable bit
@@ -45,10 +65,10 @@ public class Feature {
, GLStateManager.blendState // Blending source and destination functions
// Constant blend color
// Blending equation
// GL_DITHER enable bit
// GL_DRAW_BUFFER setting
// GL_COLOR_LOGIC_OP enable bit
// GL_INDEX_LOGIC_OP enable bit
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP enable bit
, GLStateManager.ditherState // GL_DITHER enable bit
, GLStateManager.drawBuffer // GL_DRAW_BUFFER setting
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP enable bit
// Logic op function
, GLStateManager.colorMask // Color-mode and index-mode writemasks
, GLStateManager.clearColor // Color-mode and index-mode clear values
@@ -74,14 +94,15 @@ public class Feature {
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST flag
// GL_AUTO_NORMAL flag
, GLStateManager.autoNormalState // GL_AUTO_NORMAL flag
, GLStateManager.blendMode // GL_BLEND flag
// Enable bits for the user-definable clipping planes
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP flag
, GLStateManager.colorMaterial // GL_COLOR_MATERIAL
, GLStateManager.cullState // GL_CULL_FACE flag
, GLStateManager.depthTest // GL_DEPTH_TEST flag
// GL_DITHER flag
, GLStateManager.ditherState // GL_DITHER flag
, GLStateManager.fogMode // GL_FOG flag
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP flag
, GLStateManager.lightStates[0] // GL_LIGHT0
, GLStateManager.lightStates[1] // GL_LIGHT1
, GLStateManager.lightStates[2] // GL_LIGHT2
@@ -91,36 +112,57 @@ public class Feature {
, GLStateManager.lightStates[6] // GL_LIGHT6
, GLStateManager.lightStates[7] // GL_LIGHT7
, GLStateManager.lightingState // GL_LIGHTING flag
// GL_LINE_SMOOTH flag
// GL_LINE_STIPPLE flag
// GL_INDEX_LOGIC_OP flag
// GL_COLOR_LOGIC_OP flag
// GL_MAP1_x where x is a map type
// GL_MAP2_x where x is a map type
// GL_MULTISAMPLE flag
// GL_NORMALIZE flag
// GL_POINT_SMOOTH flag
// GL_POLYGON_OFFSET_LINE flag
// GL_POLYGON_OFFSET_FILL flag
// GL_POLYGON_OFFSET_POINT flag
// GL_POLYGON_SMOOTH flag
// GL_POLYGON_STIPPLE flag
// GL_SAMPLE_ALPHA_TO_COVERAGE flag
// GL_SAMPLE_ALPHA_TO_ONE flag
// GL_SAMPLE_COVERAGE flag
, GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
, GLStateManager.lineStippleState // GL_LINE_STIPPLE flag
, GLStateManager.map1Color4State // GL_MAP1_COLOR_4
, GLStateManager.map1IndexState // GL_MAP1_INDEX
, GLStateManager.map1NormalState // GL_MAP1_NORMAL
, GLStateManager.map1TextureCoord1State // GL_MAP1_TEXTURE_COORD_1
, GLStateManager.map1TextureCoord2State // GL_MAP1_TEXTURE_COORD_2
, GLStateManager.map1TextureCoord3State // GL_MAP1_TEXTURE_COORD_3
, GLStateManager.map1TextureCoord4State // GL_MAP1_TEXTURE_COORD_4
, GLStateManager.map1Vertex3State // GL_MAP1_VERTEX_3
, GLStateManager.map1Vertex4State // GL_MAP1_VERTEX_4
, GLStateManager.map2Color4State // GL_MAP2_COLOR_4
, GLStateManager.map2IndexState // GL_MAP2_INDEX
, GLStateManager.map2NormalState // GL_MAP2_NORMAL
, GLStateManager.map2TextureCoord1State // GL_MAP2_TEXTURE_COORD_1
, GLStateManager.map2TextureCoord2State // GL_MAP2_TEXTURE_COORD_2
, GLStateManager.map2TextureCoord3State // GL_MAP2_TEXTURE_COORD_3
, GLStateManager.map2TextureCoord4State // GL_MAP2_TEXTURE_COORD_4
, GLStateManager.map2Vertex3State // GL_MAP2_VERTEX_3
, GLStateManager.map2Vertex4State // GL_MAP2_VERTEX_4
, GLStateManager.multisampleState // GL_MULTISAMPLE flag
, GLStateManager.normalizeState // GL_NORMALIZE flag
, GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
, GLStateManager.sampleAlphaToCoverageState // GL_SAMPLE_ALPHA_TO_COVERAGE flag
, GLStateManager.sampleAlphaToOneState // GL_SAMPLE_ALPHA_TO_ONE flag
, GLStateManager.sampleCoverageState // GL_SAMPLE_COVERAGE flag
, GLStateManager.scissorTest // GL_SCISSOR_TEST flag
// GL_STENCIL_TEST flag
// GL_TEXTURE_1D flag
// GL_TEXTURE_2D flag - Below
// GL_TEXTURE_3D flag
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
, GLStateManager.stencilTest // GL_STENCIL_TEST flag
));
// GL_TEXTURE_2D flag
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
// Enable bits for the user-definable clipping planes
for(int i = 0; i < GLStateManager.clipPlaneStates.length; i++) {
enableBits.add(GLStateManager.clipPlaneStates[i]);
}
// GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D flags
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
enableBits.add(GLStateManager.textures.getTexture1DStates(i));
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
enableBits.add(GLStateManager.textures.getTexture3DStates(i));
}
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
addTextureGenStates(enableBits);
attribToFeatures.put(GL11.GL_ENABLE_BIT, enableBits);
attribToFeatures.put(GL11.GL_EVAL_BIT, ImmutableSet.of(
// GL_MAP1_x enable bits, where x is a map type
@@ -262,13 +304,16 @@ public class Feature {
// textureAttribs.add(GLStateManager.textures.getInfo(i))
}
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
addTextureGenStates(textureAttribs);
attribToFeatures.put(GL11.GL_TEXTURE_BIT, textureAttribs);
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
// Coefficients of the six clipping planes
// Enable bits for the user-definable clipping planes
GLStateManager.matrixMode
// GL_NORMALIZE flag
, GLStateManager.normalizeState // GL_NORMALIZE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
));
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
@@ -26,6 +26,7 @@ import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import com.gtnewhorizons.angelica.glsm.texture.TextureTracker;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import cpw.mods.fml.relauncher.ReflectionHelper;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -35,6 +36,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gbuffer_overrides.state.StateTracker;
import net.coderbot.iris.gl.blending.AlphaTestStorage;
@@ -65,17 +67,37 @@ import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.KHRDebug;
import java.lang.Math;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
import static com.gtnewhorizons.angelica.glsm.Vendor.INTEL;
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
import static com.gtnewhorizons.angelica.glsm.Vendor.NVIDIA;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
/**
* OpenGL State Manager - Provides cached state tracking and management for OpenGL operations.
*
* <p><b>IMPORTANT INITIALIZATION ORDER:</b></p>
* <ul>
* <li>This class performs GL queries in static initializers (MAX_CLIP_PLANES, MAX_TEXTURE_UNITS, etc.)</li>
* <li>The class MUST NOT be loaded until after the GL context has been created and made current</li>
* <li>Violating this requirement will cause crashes with "No current context" or return invalid values</li>
* <li>Call {@link #preInit()} after GL context creation to initialize runtime state</li>
* </ul>
*/
@SuppressWarnings("unused") // Used in ASM
public class GLStateManager {
public static ContextCapabilities capabilities;
@@ -87,13 +109,14 @@ public class GLStateManager {
public static final int MAX_PROJECTION_STACK_DEPTH = GL11.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH);
public static final int MAX_TEXTURE_STACK_DEPTH = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH);
public static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
public static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES);
public static final GLFeatureSet HAS_MULTIPLE_SET = new GLFeatureSet();
@Getter protected static boolean NVIDIA;
@Getter protected static boolean AMD;
@Getter protected static boolean INTEL;
@Getter protected static boolean MESA;
@Getter private static Vendor VENDOR;
// This setting varies depending on driver, so it gets queried at runtime
public static final int DEFAULT_DRAW_BUFFER = GL11.glGetInteger(GL11.GL_DRAW_BUFFER);
// GLStateManager State Trackers
private static final IntStack attribs = new IntArrayList(MAX_ATTRIB_STACK_DEPTH);
@@ -112,12 +135,58 @@ public class GLStateManager {
@Getter protected static final Color4Stack color = new Color4Stack();
@Getter protected static final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
@Getter protected static final ColorMaskStack colorMask = new ColorMaskStack();
@Getter protected static final IntegerStateStack drawBuffer = new IntegerStateStack(DEFAULT_DRAW_BUFFER);
@Getter protected static final BooleanStateStack cullState = new BooleanStateStack(GL11.GL_CULL_FACE);
@Getter protected static final AlphaStateStack alphaState = new AlphaStateStack();
@Getter protected static final BooleanStateStack alphaTest = new BooleanStateStack(GL11.GL_ALPHA_TEST);
@Getter protected static final BooleanStateStack lightingState = new BooleanStateStack(GL11.GL_LIGHTING);
@Getter protected static final BooleanStateStack rescaleNormalState = new BooleanStateStack(GL12.GL_RESCALE_NORMAL);
@Getter protected static final BooleanStateStack normalizeState = new BooleanStateStack(GL11.GL_NORMALIZE);
// Additional enable bit states tracked by GL_ENABLE_BIT
@Getter protected static final BooleanStateStack ditherState = new BooleanStateStack(GL11.GL_DITHER, true); // Defaults to true per OpenGL spec
@Getter protected static final BooleanStateStack stencilTest = new BooleanStateStack(GL11.GL_STENCIL_TEST);
@Getter protected static final BooleanStateStack lineSmoothState = new BooleanStateStack(GL11.GL_LINE_SMOOTH);
@Getter protected static final BooleanStateStack lineStippleState = new BooleanStateStack(GL11.GL_LINE_STIPPLE);
@Getter protected static final BooleanStateStack pointSmoothState = new BooleanStateStack(GL11.GL_POINT_SMOOTH);
@Getter protected static final BooleanStateStack polygonSmoothState = new BooleanStateStack(GL11.GL_POLYGON_SMOOTH);
@Getter protected static final BooleanStateStack polygonStippleState = new BooleanStateStack(GL11.GL_POLYGON_STIPPLE);
@Getter protected static final BooleanStateStack multisampleState = new BooleanStateStack(GL13.GL_MULTISAMPLE, true); // Defaults to true per OpenGL spec
@Getter protected static final BooleanStateStack sampleAlphaToCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE);
@Getter protected static final BooleanStateStack sampleAlphaToOneState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_ONE);
@Getter protected static final BooleanStateStack sampleCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_COVERAGE);
@Getter protected static final BooleanStateStack colorLogicOpState = new BooleanStateStack(GL11.GL_COLOR_LOGIC_OP);
@Getter protected static final BooleanStateStack indexLogicOpState = new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP);
// Polygon offset states
@Getter protected static final BooleanStateStack polygonOffsetPointState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_POINT);
@Getter protected static final BooleanStateStack polygonOffsetLineState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_LINE);
@Getter protected static final BooleanStateStack polygonOffsetFillState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL);
// Evaluator states
@Getter protected static final BooleanStateStack autoNormalState = new BooleanStateStack(GL11.GL_AUTO_NORMAL);
@Getter protected static final BooleanStateStack map1Color4State = new BooleanStateStack(GL11.GL_MAP1_COLOR_4);
@Getter protected static final BooleanStateStack map1IndexState = new BooleanStateStack(GL11.GL_MAP1_INDEX);
@Getter protected static final BooleanStateStack map1NormalState = new BooleanStateStack(GL11.GL_MAP1_NORMAL);
@Getter protected static final BooleanStateStack map1TextureCoord1State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1);
@Getter protected static final BooleanStateStack map1TextureCoord2State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2);
@Getter protected static final BooleanStateStack map1TextureCoord3State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3);
@Getter protected static final BooleanStateStack map1TextureCoord4State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4);
@Getter protected static final BooleanStateStack map1Vertex3State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_3);
@Getter protected static final BooleanStateStack map1Vertex4State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_4);
@Getter protected static final BooleanStateStack map2Color4State = new BooleanStateStack(GL11.GL_MAP2_COLOR_4);
@Getter protected static final BooleanStateStack map2IndexState = new BooleanStateStack(GL11.GL_MAP2_INDEX);
@Getter protected static final BooleanStateStack map2NormalState = new BooleanStateStack(GL11.GL_MAP2_NORMAL);
@Getter protected static final BooleanStateStack map2TextureCoord1State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1);
@Getter protected static final BooleanStateStack map2TextureCoord2State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2);
@Getter protected static final BooleanStateStack map2TextureCoord3State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3);
@Getter protected static final BooleanStateStack map2TextureCoord4State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4);
@Getter protected static final BooleanStateStack map2Vertex3State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_3);
@Getter protected static final BooleanStateStack map2Vertex4State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_4);
// Clip plane states
@Getter protected static final BooleanStateStack[] clipPlaneStates = new BooleanStateStack[MAX_CLIP_PLANES];
@Getter protected static final MatrixModeStack matrixMode = new MatrixModeStack();
@Getter protected static final Matrix4fStack modelViewMatrix = new Matrix4fStack(MAX_MODELVIEW_STACK_DEPTH);
@@ -133,11 +202,25 @@ public class GLStateManager {
@Getter protected static final MaterialStateStack frontMaterial = new MaterialStateStack(GL11.GL_FRONT);
@Getter protected static final MaterialStateStack backMaterial = new MaterialStateStack(GL11.GL_BACK);
private static final MethodHandle MAT4_STACK_CURR_DEPTH;
static {
for (int i = 0; i < lightStates.length; i ++) {
lightStates[i] = new BooleanStateStack(GL11.GL_LIGHT0 + i);
lightDataStates[i] = new LightStateStack(GL11.GL_LIGHT0 + i);
}
for (int i = 0; i < MAX_CLIP_PLANES; i++) {
clipPlaneStates[i] = new BooleanStateStack(GL11.GL_CLIP_PLANE0 + i);
}
try {
final Field curr = ReflectionHelper.findField(Matrix4fStack.class, "curr");
curr.setAccessible(true);
MAT4_STACK_CURR_DEPTH = MethodHandles.lookup().unreflectGetter(curr);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Getter protected static final ViewPortStateStack viewportState = new ViewPortStateStack();
@@ -149,11 +232,18 @@ public class GLStateManager {
while(!attribs.isEmpty()) {
attribs.popInt();
}
for(IStateStack<?> stack : Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS)) {
final List<IStateStack<?>> stacks = Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS);
final int size = stacks.size();
for(int i = 0; i < size; i++) {
final IStateStack<?> stack = stacks.get(i);
while(!stack.isEmpty()) {
stack.pop();
}
}
modelViewMatrix.clear();
projectionMatrix.clear();
}
@@ -180,6 +270,9 @@ public class GLStateManager {
public static class GLFeatureSet extends IntOpenHashSet {
private static final long serialVersionUID = 8558779940775721010L;
public GLFeatureSet addFeature(int feature) {
super.add(feature);
return this;
@@ -218,16 +311,12 @@ public class GLStateManager {
.addFeature(GL11.GL_VIEWPORT);
String glVendor = GL11.glGetString(GL11.GL_VENDOR);
NVIDIA = glVendor.toLowerCase().contains("nvidia");
AMD = glVendor.toLowerCase().contains("ati") || glVendor.toLowerCase().contains("amd");
INTEL = glVendor.toLowerCase().contains("intel");
MESA = glVendor.toLowerCase().contains("mesa");
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
if(AMD) {
if (vendorIsAMD()) {
// AMD Drivers seem to default to 0 for the matrix mode, so we need to set it to the default
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
}
public static void init() {
@@ -273,12 +362,23 @@ public class GLStateManager {
// LWJGL Overrides
public static void glEnable(int cap) {
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].enable();
return;
}
switch (cap) {
case GL11.GL_ALPHA_TEST -> enableAlphaTest();
case GL11.GL_AUTO_NORMAL -> autoNormalState.enable();
case GL11.GL_BLEND -> enableBlend();
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.enable();
case GL11.GL_CULL_FACE -> enableCull();
case GL11.GL_DEPTH_TEST -> enableDepthTest();
case GL11.GL_DITHER -> ditherState.enable();
case GL11.GL_FOG -> enableFog();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.enable();
case GL11.GL_LIGHTING -> enableLighting();
case GL11.GL_LIGHT0 -> enableLight(0);
case GL11.GL_LIGHT1 -> enableLight(1);
@@ -288,21 +388,69 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> enableLight(5);
case GL11.GL_LIGHT6 -> enableLight(6);
case GL11.GL_LIGHT7 -> enableLight(7);
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
case GL11.GL_SCISSOR_TEST -> enableScissorTest();
case GL11.GL_TEXTURE_2D -> enableTexture();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.enable();
case GL11.GL_LINE_STIPPLE -> lineStippleState.enable();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.enable();
case GL11.GL_MAP1_INDEX -> map1IndexState.enable();
case GL11.GL_MAP1_NORMAL -> map1NormalState.enable();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.enable();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.enable();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.enable();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.enable();
case GL11.GL_MAP2_INDEX -> map2IndexState.enable();
case GL11.GL_MAP2_NORMAL -> map2NormalState.enable();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.enable();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.enable();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.enable();
case GL13.GL_MULTISAMPLE -> multisampleState.enable();
case GL11.GL_NORMALIZE -> normalizeState.enable();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.enable();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.enable();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.enable();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.enable();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.enable();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.enable();
case GL12.GL_RESCALE_NORMAL -> enableRescaleNormal();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.enable();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.enable();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.enable();
case GL11.GL_SCISSOR_TEST -> enableScissorTest();
case GL11.GL_STENCIL_TEST -> stencilTest.enable();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_2D -> enableTexture();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).enable();
default -> GL11.glEnable(cap);
}
}
public static void glDisable(int cap) {
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].disable();
return;
}
switch (cap) {
case GL11.GL_ALPHA_TEST -> disableAlphaTest();
case GL11.GL_AUTO_NORMAL -> autoNormalState.disable();
case GL11.GL_BLEND -> disableBlend();
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.disable();
case GL11.GL_CULL_FACE -> disableCull();
case GL11.GL_DEPTH_TEST -> disableDepthTest();
case GL11.GL_DITHER -> ditherState.disable();
case GL11.GL_FOG -> disableFog();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.disable();
case GL11.GL_LIGHTING -> disableLighting();
case GL11.GL_LIGHT0 -> disableLight(0);
case GL11.GL_LIGHT1 -> disableLight(1);
@@ -312,10 +460,47 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> disableLight(5);
case GL11.GL_LIGHT6 -> disableLight(6);
case GL11.GL_LIGHT7 -> disableLight(7);
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
case GL11.GL_SCISSOR_TEST -> disableScissorTest();
case GL11.GL_TEXTURE_2D -> disableTexture();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.disable();
case GL11.GL_LINE_STIPPLE -> lineStippleState.disable();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.disable();
case GL11.GL_MAP1_INDEX -> map1IndexState.disable();
case GL11.GL_MAP1_NORMAL -> map1NormalState.disable();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.disable();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.disable();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.disable();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.disable();
case GL11.GL_MAP2_INDEX -> map2IndexState.disable();
case GL11.GL_MAP2_NORMAL -> map2NormalState.disable();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.disable();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.disable();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.disable();
case GL13.GL_MULTISAMPLE -> multisampleState.disable();
case GL11.GL_NORMALIZE -> normalizeState.disable();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.disable();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.disable();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.disable();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.disable();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.disable();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.disable();
case GL12.GL_RESCALE_NORMAL -> disableRescaleNormal();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.disable();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.disable();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.disable();
case GL11.GL_SCISSOR_TEST -> disableScissorTest();
case GL11.GL_STENCIL_TEST -> stencilTest.disable();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_2D -> disableTexture();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).disable();
default -> GL11.glDisable(cap);
}
}
@@ -324,12 +509,22 @@ public class GLStateManager {
if(shouldBypassCache()) {
return GL11.glIsEnabled(cap);
}
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
return clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].isEnabled();
}
return switch (cap) {
case GL11.GL_ALPHA_TEST -> alphaTest.isEnabled();
case GL11.GL_AUTO_NORMAL -> autoNormalState.isEnabled();
case GL11.GL_BLEND -> blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.isEnabled();
case GL11.GL_CULL_FACE -> cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> depthTest.isEnabled();
case GL11.GL_DITHER -> ditherState.isEnabled();
case GL11.GL_FOG -> fogMode.isEnabled();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.isEnabled();
case GL11.GL_LIGHTING -> lightingState.isEnabled();
case GL11.GL_LIGHT0 -> lightStates[0].isEnabled();
case GL11.GL_LIGHT1 -> lightStates[1].isEnabled();
@@ -339,10 +534,47 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> lightStates[5].isEnabled();
case GL11.GL_LIGHT6 -> lightStates[6].isEnabled();
case GL11.GL_LIGHT7 -> lightStates[7].isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.isEnabled();
case GL11.GL_LINE_STIPPLE -> lineStippleState.isEnabled();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.isEnabled();
case GL11.GL_MAP1_INDEX -> map1IndexState.isEnabled();
case GL11.GL_MAP1_NORMAL -> map1NormalState.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.isEnabled();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.isEnabled();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.isEnabled();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.isEnabled();
case GL11.GL_MAP2_INDEX -> map2IndexState.isEnabled();
case GL11.GL_MAP2_NORMAL -> map2NormalState.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.isEnabled();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.isEnabled();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.isEnabled();
case GL13.GL_MULTISAMPLE -> multisampleState.isEnabled();
case GL11.GL_NORMALIZE -> normalizeState.isEnabled();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.isEnabled();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.isEnabled();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.isEnabled();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.isEnabled();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.isEnabled();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.isEnabled();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_STENCIL_TEST -> stencilTest.isEnabled();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).isEnabled();
default -> GL11.glIsEnabled(cap);
};
}
@@ -351,13 +583,23 @@ public class GLStateManager {
if(shouldBypassCache()) {
return GL11.glGetBoolean(pname);
}
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (pname >= GL11.GL_CLIP_PLANE0 && pname < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
return clipPlaneStates[pname - GL11.GL_CLIP_PLANE0].isEnabled();
}
return switch (pname) {
case GL11.GL_ALPHA_TEST -> alphaTest.isEnabled();
case GL11.GL_AUTO_NORMAL -> autoNormalState.isEnabled();
case GL11.GL_BLEND -> blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.isEnabled();
case GL11.GL_CULL_FACE -> cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> depthTest.isEnabled();
case GL11.GL_DEPTH_WRITEMASK -> depthState.isEnabled();
case GL11.GL_DITHER -> ditherState.isEnabled();
case GL11.GL_FOG -> fogMode.isEnabled();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.isEnabled();
case GL11.GL_LIGHTING -> lightingState.isEnabled();
case GL11.GL_LIGHT0 -> lightStates[0].isEnabled();
case GL11.GL_LIGHT1 -> lightStates[1].isEnabled();
@@ -367,10 +609,47 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> lightStates[5].isEnabled();
case GL11.GL_LIGHT6 -> lightStates[6].isEnabled();
case GL11.GL_LIGHT7 -> lightStates[7].isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.isEnabled();
case GL11.GL_LINE_STIPPLE -> lineStippleState.isEnabled();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.isEnabled();
case GL11.GL_MAP1_INDEX -> map1IndexState.isEnabled();
case GL11.GL_MAP1_NORMAL -> map1NormalState.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.isEnabled();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.isEnabled();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.isEnabled();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.isEnabled();
case GL11.GL_MAP2_INDEX -> map2IndexState.isEnabled();
case GL11.GL_MAP2_NORMAL -> map2NormalState.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.isEnabled();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.isEnabled();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.isEnabled();
case GL13.GL_MULTISAMPLE -> multisampleState.isEnabled();
case GL11.GL_NORMALIZE -> normalizeState.isEnabled();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.isEnabled();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.isEnabled();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.isEnabled();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.isEnabled();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.isEnabled();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.isEnabled();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_STENCIL_TEST -> stencilTest.isEnabled();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).isEnabled();
default -> GL11.glGetBoolean(pname);
};
}
@@ -398,6 +677,11 @@ public class GLStateManager {
}
}
@SneakyThrows
public static int getMatrixStackDepth(Matrix4fStack stack) {
return (int) MAT4_STACK_CURR_DEPTH.invokeExact(stack);
}
public static int glGetInteger(int pname) {
if(shouldBypassCache()) {
return GL11.glGetInteger(pname);
@@ -417,6 +701,8 @@ public class GLStateManager {
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL20.GL_CURRENT_PROGRAM -> activeProgram;
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
case GL11.GL_PROJECTION_STACK_DEPTH -> getMatrixStackDepth(projectionMatrix);
default -> GL11.glGetInteger(pname);
};
@@ -829,6 +1115,16 @@ public class GLStateManager {
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : (IntBuffer) null);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
@@ -953,39 +1249,59 @@ public class GLStateManager {
i = mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_LINEAR;
j = GL11.GL_LINEAR;
} else {
i = mipmap ? GL11.GL_NEAREST_MIPMAP_LINEAR : GL11.GL_NEAREST;
i = mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_NEAREST;
j = GL11.GL_NEAREST;
}
glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, i);
glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, j);
}
public static void glDrawArrays(int mode, int first, int count) {
// Iris -- TODO: This doesn't seem to work and is related to matchPass()
if(AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
GL11.glDrawArrays(mode, first, count);
}
public static void glDrawBuffer(int mode) {
GL11.glDrawBuffer(mode);
}
public static void glDrawElements(int mode, IntBuffer indices) {
if(AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
GL11.glDrawElements(mode, indices);
}
public static void glBegin(int mode) {
public static void trySyncProgram() {
if (AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
}
public static void glBegin(int mode) {
trySyncProgram();
GL11.glBegin(mode);
}
public static void glDrawElements(int mode, ByteBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, IntBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, ShortBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
trySyncProgram();
GL11.glDrawElements(mode, indices_count, type, indices_buffer_offset);
}
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, count, type, indices);
}
public static void glDrawBuffer(int mode) {
trySyncProgram();
GL11.glDrawBuffer(mode);
}
public static void glDrawArrays(int mode, int first, int count) {
trySyncProgram();
GL11.glDrawArrays(mode, first, count);
}
public static void glLogicOp(int opcode) {
GL11.glLogicOp(opcode);
}
@@ -1191,13 +1507,21 @@ public class GLStateManager {
glListNesting += 1;
return;
}
glListId = list;
glListMode = mode;
GL11.glNewList(list, mode);
for(IStateStack<?> stack : Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS)) {
List<IStateStack<?>> stacks = Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS);
int size = stacks.size();
for(int i = 0; i < size; i++) {
IStateStack<?> stack = stacks.get(i);
// Feature Stack, copy of current feature state
glListStates.put(stack, (ISettableState<?>) ((ISettableState<?>)stack).copy());
}
if(glListMode == GL11.GL_COMPILE) {
pushState(GL11.GL_ALL_ATTRIB_BITS);
}
@@ -1240,6 +1564,7 @@ public class GLStateManager {
if(list < 0) {
VBOManager.get(list).render();
} else {
trySyncProgram();
GL11.glCallList(list);
if(glListChanges.containsKey(list)) {
for(Map.Entry<IStateStack<?>, ISettableState<?>> entry : glListChanges.get(list)) {
@@ -1252,15 +1577,23 @@ public class GLStateManager {
public static void pushState(int mask) {
attribs.push(mask);
for(IStateStack<?> stack : Feature.maskToFeatures(mask)) {
stack.push();
}
List<IStateStack<?>> stacks = Feature.maskToFeatures(mask);
int size = stacks.size();
for(int i = 0; i < size; i++) {
stacks.get(i).push();
}
}
public static void popState() {
final int mask = attribs.popInt();
for(IStateStack<?> stack : Feature.maskToFeatures(mask)) {
stack.pop();
List<IStateStack<?>> stacks = Feature.maskToFeatures(mask);
int size = stacks.size();
for(int i = 0; i < size; i++) {
stacks.get(i).pop();
}
}
@@ -1804,6 +2137,20 @@ public class GLStateManager {
}
}
public static boolean vendorIsAMD() {
return VENDOR == AMD;
}
public static boolean vendorIsIntel() {
return VENDOR == INTEL;
}
public static boolean vendorIsMesa() {
return VENDOR == MESA;
}
public static boolean vendorIsNVIDIA() {
return VENDOR == NVIDIA;
}
}
@@ -30,7 +30,6 @@ import java.nio.IntBuffer;
public class RenderSystem {
private static final Logger LOGGER = LogManager.getLogger("RenderSystem");
private static DSAAccess dsaState;
private static boolean hasMultibind;
private static boolean supportsCompute;
private RenderSystem() {}
@@ -41,7 +40,6 @@ public class RenderSystem {
dsaState = new DSACore();
LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
}
hasMultibind = GLStateManager.capabilities.OpenGL45;
} catch (NoSuchFieldError ignored) {}
try {
@@ -55,10 +53,6 @@ public class RenderSystem {
LOGGER.info("No DSA support detected, falling back to legacy OpenGL.");
}
try {
hasMultibind |= GLStateManager.capabilities.GL_ARB_multi_bind;
} catch (NoSuchFieldError ignored) {}
supportsCompute = supportsCompute();
}
@@ -103,6 +97,8 @@ public class RenderSystem {
GL20.glUniform3f(location, v0, v1, v2);
}
public static void uniform3i(int location, int v0, int v1, int v2) { GL20.glUniform3i(location, v0, v1, v2); }
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
GL20.glUniform4f(location, v0, v1, v2, v3);
}
@@ -0,0 +1,27 @@
package com.gtnewhorizons.angelica.glsm;
/// Matches
public enum Vendor {
AMD("amd"),
INTEL("intel"),
MESA("mesa"),
NVIDIA("nvidia"),
UNKNOWN("");
final String[] names;
Vendor(String... names) {
this.names = names;
}
public static Vendor getVendor(String vendorString) {
for (var v : values()) {
for (var name : v.names) {
if (vendorString.contains(name)) return v;
}
}
// Shouldn't be needed as "" matches everything, but sanity checks good.
return UNKNOWN;
}
}
@@ -12,7 +12,20 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
protected int pointer;
public BooleanStateStack(int glCap) {
// Most booleans default to false
this(glCap, false);
}
/**
* Create a BooleanStateStack with a custom initial state.
* Useful for GL states that default to true (e.g., GL_DITHER, GL_MULTISAMPLE).
*
* @param glCap GL capability constant
* @param initialState initial enabled state
*/
public BooleanStateStack(int glCap, boolean initialState) {
super(glCap);
this.enabled = initialState;
stack = new BooleanState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack[i] = new BooleanState(glCap);
@@ -34,7 +47,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
}
set(stack[--pointer]);
if (GLStateManager.isAMD() && glCap == GL11.GL_BLEND) {
if (GLStateManager.vendorIsAMD() && glCap == GL11.GL_BLEND) {
setEnabled(enabled);
}
return this;
@@ -22,7 +22,7 @@ public class BooleanState implements ISettableState<BooleanState> {
}
public void setEnabled(boolean enabled) {
if (GLStateManager.shouldBypassCache() || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.isAMD() && GLStateManager.isPoppingAttributes())) {
if (GLStateManager.shouldBypassCache() || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.vendorIsAMD() && GLStateManager.isPoppingAttributes())) {
this.enabled = enabled;
if (enabled) {
GL11.glEnable(this.glCap);
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.states;
public interface ISettableState<T> extends Cloneable {
T set(T state);
@SuppressWarnings("unchecked")
default void set(ISettableState<?> value) {
set((T) value);
}
@@ -18,7 +18,6 @@ public class LightState implements ISettableState<LightState> {
private static final Vector4i vector4i = new Vector4i();
private static final Vector4f vector4f = new Vector4f();
private static final Vector3i vector3i = new Vector3i();
private static final Vector3f vector3f = new Vector3f();
private static final Matrix3f matrix3f = new Matrix3f();
public int light;
@@ -124,7 +124,7 @@ public class MaterialState implements ISettableState<MaterialState> {
// glMaterial specifically for GL_COLOR_INDEXES. Other drivers do not do this.
// This probably breaks things but who knows, GLSM is setup to track to what the
// driver is doing for it right now.
if (GLStateManager.isNVIDIA()) {
if (GLStateManager.vendorIsNVIDIA()) {
vector3f.set(newBuffer.get(0), newBuffer.get(2), newBuffer.get(1));
} else {
vector3f.set(newBuffer);
@@ -6,21 +6,40 @@ import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
import lombok.Getter;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
public class TextureUnitArray {
private final TextureBindingStack[] bindings;
private final BooleanStateStack[] states;
private final BooleanStateStack[] texture1DStates;
private final BooleanStateStack[] texture3DStates;
private final BooleanStateStack[] texGenSStates;
private final BooleanStateStack[] texGenTStates;
private final BooleanStateStack[] texGenRStates;
private final BooleanStateStack[] texGenQStates;
@Getter
public final Matrix4fStack[] textureMatricies;
public TextureUnitArray() {
bindings = new TextureBindingStack[GLStateManager.MAX_TEXTURE_UNITS];
states = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture1DStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture3DStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenSStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenTStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenRStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenQStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
textureMatricies = new Matrix4fStack[GLStateManager.MAX_TEXTURE_UNITS];
for (int i = 0; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
bindings[i] = new TextureBindingStack();
states[i] = new BooleanStateStack(GL11.GL_TEXTURE_2D);
texture1DStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_1D);
texture3DStates[i] = new BooleanStateStack(GL12.GL_TEXTURE_3D);
texGenSStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_S);
texGenTStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_T);
texGenRStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_R);
texGenQStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_Q);
textureMatricies[i] = new Matrix4fStack(GLStateManager.MAX_TEXTURE_STACK_DEPTH);
}
}
@@ -33,6 +52,30 @@ public class TextureUnitArray {
return states[index];
}
public BooleanStateStack getTexture1DStates(int index) {
return texture1DStates[index];
}
public BooleanStateStack getTexture3DStates(int index) {
return texture3DStates[index];
}
public BooleanStateStack getTexGenSStates(int index) {
return texGenSStates[index];
}
public BooleanStateStack getTexGenTStates(int index) {
return texGenTStates[index];
}
public BooleanStateStack getTexGenRStates(int index) {
return texGenRStates[index];
}
public BooleanStateStack getTexGenQStates(int index) {
return texGenQStates[index];
}
public Matrix4fStack getTextureUnitMatrix(int index) {
return textureMatricies[index];
}
@@ -44,6 +87,12 @@ public class TextureUnitArray {
for (int i = 0; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
if (!bindings[i].sameAs(textureUnitArray.bindings[i])) return false;
if (!states[i].sameAs(textureUnitArray.states[i])) return false;
if (!texture1DStates[i].sameAs(textureUnitArray.texture1DStates[i])) return false;
if (!texture3DStates[i].sameAs(textureUnitArray.texture3DStates[i])) return false;
if (!texGenSStates[i].sameAs(textureUnitArray.texGenSStates[i])) return false;
if (!texGenTStates[i].sameAs(textureUnitArray.texGenTStates[i])) return false;
if (!texGenRStates[i].sameAs(textureUnitArray.texGenRStates[i])) return false;
if (!texGenQStates[i].sameAs(textureUnitArray.texGenQStates[i])) return false;
}
return true;
}
@@ -12,7 +12,7 @@ public class TextureInfo {
protected int width = -1;
protected int height = -1;
@Getter @Setter protected int minFilter = GL11.GL_NEAREST_MIPMAP_LINEAR;
@Getter @Setter protected int minFilter = GL11.GL_LINEAR_MIPMAP_LINEAR;
@Getter @Setter protected int magFilter = GL11.GL_LINEAR;
@Getter @Setter protected int wrapS = GL11.GL_REPEAT;
@Getter @Setter protected int wrapT = GL11.GL_REPEAT;
@@ -6,7 +6,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
import java.nio.IntBuffer;
import java.nio.Buffer;
public class TextureInfoCache {
/**
@@ -25,7 +25,7 @@ public class TextureInfoCache {
return cache.computeIfAbsent(id, TextureInfo::new);
}
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels) {
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable Buffer pixels) {
if (target == GL11.GL_TEXTURE_2D && level == 0) {
final TextureInfo info = getInfo(GLStateManager.getBoundTexture());
if(info == null) return;
@@ -1,17 +1,14 @@
package com.gtnewhorizons.angelica.hudcaching;
import java.util.ArrayList;
import java.util.List;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.holoinventory.HoloInventoryReflectionCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameForgeAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.RenderGameOverlayEventAccessor;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import cpw.mods.fml.common.eventhandler.EventPriority;
import net.dries007.holoInventory.client.Renderer;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.event.world.WorldEvent;
import org.lwjgl.opengl.GL11;
@@ -34,7 +31,7 @@ import xaero.common.core.XaeroMinimapCore;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
// See LICENSE+HUDCaching.md for license information.
// See LICENSE-HUDCaching.md for license information.
public class HUDCaching {
@@ -59,11 +56,10 @@ public class HUDCaching {
public static boolean renderCrosshairsCaptured;
private final static RenderGameOverlayEvent fakeTextEvent = new RenderGameOverlayEvent.Text(new RenderGameOverlayEvent(0, null, 0, 0), null, null);
private final static RenderGameOverlayEvent fakePreEvent = new RenderGameOverlayEvent.Pre(new RenderGameOverlayEvent(0, null, 0, 0), RenderGameOverlayEvent.ElementType.HELMET);
private final static RenderGameOverlayEvent.Post fakePostEvent = new RenderGameOverlayEvent.Post(new RenderGameOverlayEvent(0, null, 0, 0), RenderGameOverlayEvent.ElementType.HELMET);
public static final HUDCaching INSTANCE = new HUDCaching();
private HUDCaching() {}
// highest so it runs before the GLSM load event
@@ -102,7 +98,7 @@ public class HUDCaching {
renderingCacheOverride = false;
mc.getFramebuffer().bindFramebuffer(false);
} else {
renderer.setupOverlayRendering();
renderer.setupOverlayRendering();
}
ScaledResolution resolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
@@ -115,48 +111,50 @@ public class HUDCaching {
// render bits that were captured when rendering into cache
GuiIngameAccessor gui = (GuiIngameAccessor) ingame;
if (renderVignetteCaptured)
{
if (renderVignetteCaptured) {
gui.callRenderVignette(mc.thePlayer.getBrightness(partialTicks), width, height);
} else {
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
}
if (ingame instanceof GuiIngameForge) {
GuiIngameForgeAccessor guiForge = ((GuiIngameForgeAccessor) ingame);
if (renderHelmetCaptured) {
guiForge.callRenderHelmet(resolution, partialTicks, hasScreen, mouseX, mouseY);
if (ModStatus.isHoloInventoryLoaded){
Renderer.INSTANCE.angelicaOverride = false;
GuiIngameForgeAccessor guiForge = ((GuiIngameForgeAccessor) ingame);
if (renderHelmetCaptured) {
guiForge.callRenderHelmet(resolution, partialTicks, hasScreen, mouseX, mouseY);
if (ModStatus.isHoloInventoryLoaded) {
HoloInventoryReflectionCompat.setAngelicaOverride(false);
// only settings the partial ticks as mouseX and mouseY are not used in renderEvent
((RenderGameOverlayEventAccessor) fakePreEvent).setPartialTicks(partialTicks);
Renderer.INSTANCE.renderEvent(fakePreEvent);
((RenderGameOverlayEventAccessor) fakePostEvent).setPartialTicks(partialTicks);
HoloInventoryReflectionCompat.renderEvent(fakePostEvent);
}
}
if (renderPortalCapturedTicks > 0) {
guiForge.callRenderPortal(width, height, partialTicks);
}
if (renderCrosshairsCaptured) {
guiForge.callRenderCrosshairs(width, height);
}
if (ModStatus.isThaumcraftLoaded || ModStatus.isThaumicHorizonsLoaded){
}
if (renderPortalCapturedTicks > 0) {
guiForge.callRenderPortal(width, height, partialTicks);
}
if (renderCrosshairsCaptured) {
if (ModStatus.isXaerosMinimapLoaded) {
// this fixes the crosshair going invisible when no lines are being drawn under the minimap
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
}
guiForge.callRenderCrosshairs(width, height);
}
if (ModStatus.isThaumcraftLoaded || ModStatus.isThaumicHorizonsLoaded) {
((RenderGameOverlayEventAccessor) fakeTextEvent).setPartialTicks(partialTicks);
((RenderGameOverlayEventAccessor) fakeTextEvent).setResolution(resolution);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseX(mouseX);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseY(mouseY);
if (ModStatus.isThaumcraftLoaded){
if (ModStatus.isThaumcraftLoaded) {
Thaumcraft.instance.renderEventHandler.renderOverlay(fakeTextEvent);
}
if (ModStatus.isThaumicHorizonsLoaded){
if (ModStatus.isThaumicHorizonsLoaded) {
ThaumicHorizons.instance.renderEventHandler.renderOverlay(fakeTextEvent);
}
}
} else {
if (renderHelmetCaptured)
{
if (renderHelmetCaptured) {
gui.callRenderPumpkinBlur(width, height);
}
if (renderPortalCapturedTicks > 0)
{
if (renderPortalCapturedTicks > 0) {
gui.callRenderPortal(renderPortalCapturedTicks, width, height);
}
}
@@ -179,19 +177,19 @@ public class HUDCaching {
* the state before we start rendering
*/
public static void fixGLStateBeforeRenderingCache() {
GLStateManager.glDepthMask(true);
GLStateManager.enableDepthTest();
GLStateManager.enableAlphaTest();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.disableBlend();
GLStateManager.glDepthMask(true);
GLStateManager.enableDepthTest();
GLStateManager.enableAlphaTest();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.disableBlend();
}
private static void resetFramebuffer(int width, int height) {
if (framebuffer.framebufferWidth != width || framebuffer.framebufferHeight != height) {
framebuffer.createBindFramebuffer(width, height);
framebuffer.createBindFramebuffer(width, height);
framebuffer.setFramebufferFilter(GL11.GL_NEAREST);
} else {
framebuffer.framebufferClear();
framebuffer.framebufferClear();
}
// copy depth buffer from MC
OpenGlHelper.func_153171_g(GL30.GL_READ_FRAMEBUFFER, mc.getFramebuffer().framebufferObject);
@@ -213,10 +211,9 @@ public class HUDCaching {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
}
// moved to here due to the method being called from a mixin
public static void disableHoloInventory() {
if (ModStatus.isHoloInventoryLoaded) {
Renderer.INSTANCE.angelicaOverride = true;
HoloInventoryReflectionCompat.setAngelicaOverride(true);
}
}
@@ -1,21 +1,22 @@
package com.gtnewhorizons.angelica.loading;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
import com.gtnewhorizon.gtnhlib.config.ConfigException;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.ITransformers;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.mixins.Mixins;
import com.gtnewhorizons.angelica.mixins.TargetedMod;
import com.gtnewhorizons.angelica.transform.compat.GenericCompatTransformer;
import com.gtnewhorizons.angelica.transform.compat.handlers.CompatHandler;
import com.gtnewhorizons.angelica.transform.compat.handlers.CompatHandlers;
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import jss.notfine.asm.AsmTransformers;
import jss.notfine.asm.mappings.Namer;
import jss.notfine.config.MCPatcherForgeConfig;
import jss.notfine.config.NotFineConfig;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -26,7 +27,7 @@ import org.spongepowered.asm.launch.GlobalProperties;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -42,21 +43,22 @@ import java.util.Set;
//@IFMLLoadingPlugin.SortingIndex(Integer.MAX_VALUE - 5)
@IFMLLoadingPlugin.MCVersion("1.7.10")
@IFMLLoadingPlugin.TransformerExclusions({
"com.gtnewhorizons.angelica.transform.RedirectorTransformer",
"com.gtnewhorizons.angelica.glsm.GLStateManager"})
"jss.notfine.asm",
"com.gtnewhorizons.angelica.loading",
"com.gtnewhorizons.angelica.glsm.GLStateManager"})
public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
private static final boolean DUMP_CLASSES = Boolean.parseBoolean(System.getProperty("angelica.dumpClass", "false"));
private static boolean OBF_ENV;
private static Boolean OBF_ENV;
public static final Logger LOGGER = LogManager.getLogger("Angelica");
private String[] transformerClasses;
static {
public AngelicaTweaker() {
try {
// Angelica Config
ConfigurationManager.registerConfig(AngelicaConfig.class);
ConfigurationManager.registerConfig(CompatConfig.class);
ConfigurationManager.registerConfig(FontConfig.class);
MCPatcherForgeConfig.registerConfig();
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
@@ -67,39 +69,29 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
ctx.updateLoggers();
// Debug features
AngelicaConfig.enableTestBlocks = Boolean.parseBoolean(System.getProperty("angelica.enableTestBlocks", "false"));
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
} catch (ConfigException e) {
throw new RuntimeException(e);
}
verifyDependencies();
}
private static void verifyDependencies() {
if (AngelicaTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib 0.2.1 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
}
@Override
public String[] getASMTransformerClass() {
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
final List<String> mixinTweakClasses = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (mixinTweakClasses != null) {
mixinTweakClasses.add(MixinCompatHackTweaker.class.getName());
}
if (transformerClasses == null) {
final List<String> transformers = new ArrayList<>();
// Regsiter compat handlers, and add extra specific transformers, then build and register the generic transformer
for (CompatHandler handler : CompatHandlers.getHandlers()) {
GenericCompatTransformer.register(handler);
if (handler.extraTransformers() != null) {
transformers.addAll(handler.extraTransformers());
}
}
GenericCompatTransformer.build();
transformers.add(GenericCompatTransformer.class.getName());
transformers.addAll(CompatHandlers.getTransformers());
// Add NotFine transformers
final List<String> notFineTransformers = AsmTransformers.getTransformers();
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
if (!notFineTransformers.isEmpty()) Namer.initNames();
transformers.addAll(notFineTransformers);
transformerClasses = transformers.toArray(new String[0]);
}
return transformerClasses;
@@ -117,7 +109,28 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
@Override
public void injectData(Map<String, Object> data) {
OBF_ENV = (boolean) data.get("runtimeDeobfuscationEnabled");
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
final List<String> tweaks = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (tweaks != null) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
if (AngelicaConfig.enableHudCaching) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.XaerosTransformerDisablerTweaker");
}
if (FMLLaunchHandler.side().isClient()) {
// We register ITweakers that will run last in order to register
// specific IClassTransformers that will run last in the transformer chain.
// If we were to register them normally in getASMTransformerClass(),
// they would be sorted at index 0 which we do not want.
boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
}
if (AngelicaConfig.enableSodium) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.SodiumLateTweaker");
}
}
}
}
@Override
@@ -132,39 +145,34 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Mixins.getEarlyMixins(loadedCoreMods);
}
public static boolean DUMP_CLASSES() {
return DUMP_CLASSES || !OBF_ENV;
NotFineConfig.loadSettings();
//This may be possible to handle differently or fix.
if (loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
}
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
}
/**
* Returns true if we are in an obfuscated environment, returns false in dev environment.
*/
public static boolean isObfEnv() {
if (OBF_ENV == null) {
throw new IllegalStateException("Obfuscation state has been accessed too early!");
}
return OBF_ENV;
}
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
if (AngelicaTweaker.DUMP_CLASSES()) {
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
/**
* Returns the appropriate name according to current environment's obfuscation
*/
public static String obf(String deobf, String obf) {
if (OBF_ENV == null) {
throw new IllegalStateException("Obfuscation state has been accessed too early!");
}
if (OBF_ENV) {
return obf;
}
return deobf;
}
private static final ImmutableMap<String, TargetedMod> MODS_BY_CLASS = ImmutableMap.<String, TargetedMod>builder()
.put("optifine.OptiFineForgeTweaker", TargetedMod.OPTIFINE)
.put("fastcraft.Tweaker", TargetedMod.FASTCRAFT)
.put("cofh.asm.LoadingPlugin", TargetedMod.COFHCORE)
.build();
public static final Set<TargetedMod> coreMods = new HashSet<>();
private static void detectCoreMods(Set<String> loadedCoreMods) {
MODS_BY_CLASS.forEach((key, value) -> {
if (loadedCoreMods.contains(key))
coreMods.add(value);
});
}
}
@@ -0,0 +1,64 @@
package com.gtnewhorizons.angelica.loading.fml.compat;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ExtraUtilsCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ImmersiveEngineeringCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.StacksOnStacksCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumcraftCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumicHorizonsCompatHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
public enum CompatHandlers {
STACKS_ON_STACKS(() -> CompatConfig.fixStacksOnStacks, new StacksOnStacksCompatHandler()),
EXTRA_UTILS(() -> CompatConfig.fixExtraUtils, new ExtraUtilsCompatHandler()),
IMMERSIVE_ENGINEERING(() -> CompatConfig.fixImmersiveEngineering, new ImmersiveEngineeringCompatHandler()),
THAUMCRAFT(() -> CompatConfig.fixThaumcraft, new ThaumcraftCompatHandler()),
THAUMIC_HORIZONS(() -> CompatConfig.fixThaumicHorizons, new ThaumicHorizonsCompatHandler());
private final Supplier<Boolean> applyIf;
private final ICompatHandler handler;
CompatHandlers(Supplier<Boolean> applyIf, ICompatHandler handler) {
this.applyIf = applyIf;
this.handler = handler;
}
private static List<ICompatHandler> compatHandlers = null;
public static List<ICompatHandler> getHandlers() {
if (compatHandlers != null) {
return compatHandlers;
}
compatHandlers = new ArrayList<>();
for (CompatHandlers value : values()) {
if (value.applyIf.get()) {
compatHandlers.add(value.handler);
}
}
return compatHandlers;
}
/**
* Returns extra transformers as well as the main transformer.
* Returns an empty list if no handlers registered.
*/
public static List<String> getTransformers() {
final List<ICompatHandler> handlers = getHandlers();
if (handlers.isEmpty()) {
return Collections.emptyList();
}
List<String> transformers = new ArrayList<>();
for (ICompatHandler handler : handlers) {
if (handler.extraTransformers() != null) {
transformers.addAll(handler.extraTransformers());
}
}
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer");
return transformers;
}
}
@@ -1,9 +1,9 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat;
import java.util.List;
import java.util.Map;
public interface CompatHandler {
public interface ICompatHandler {
default Map<String, List<String>> getFieldLevelTessellator() {
return null;
@@ -1,10 +1,11 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.Map;
public class ExtraUtilsCompatHandler implements CompatHandler {
public class ExtraUtilsCompatHandler implements ICompatHandler {
@Override
public Map<String, Boolean> getThreadSafeISBRHAnnotations() {
@@ -1,13 +1,13 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.transform.compat.transformers.specific.ImmersiveEngineeringTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class ImmersiveEngineeringCompatHandler implements CompatHandler {
public class ImmersiveEngineeringCompatHandler implements ICompatHandler {
@Override
public Map<String, List<String>> getTileEntityNullGuard() {
@@ -38,7 +38,7 @@ public class ImmersiveEngineeringCompatHandler implements CompatHandler {
@Override
public List<String> extraTransformers() {
return ImmutableList.of(ImmersiveEngineeringTransformer.class.getName());
return ImmutableList.of("com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific.ImmersiveEngineeringTransformer");
}
}
@@ -1,13 +1,13 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.transform.compat.transformers.specific.StacksOnStacksTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class StacksOnStacksCompatHandler implements CompatHandler {
public class StacksOnStacksCompatHandler implements ICompatHandler {
@Override
public Map<String, List<String>> getFieldLevelTessellator() {
@@ -28,6 +28,6 @@ public class StacksOnStacksCompatHandler implements CompatHandler {
@Override
public List<String> extraTransformers() {
return ImmutableList.of(StacksOnStacksTransformer.class.getName());
return ImmutableList.of("com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific.StacksOnStacksTransformer");
}
}
@@ -1,12 +1,13 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class ThaumcraftCompatHandler implements CompatHandler {
public class ThaumcraftCompatHandler implements ICompatHandler {
@Override
public Map<String, List<String>> getHUDCachingEarlyReturn() {
@@ -1,12 +1,13 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class ThaumicHorizonsCompatHandler implements CompatHandler {
public class ThaumicHorizonsCompatHandler implements ICompatHandler {
@Override
public Map<String, List<String>> getHUDCachingEarlyReturn() {
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.generic;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.transform.AsmUtil;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
import org.spongepowered.asm.lib.tree.ClassNode;
@@ -62,7 +62,7 @@ public class FieldLevelTessellatorTransformer {
new FieldInsnNode(
Opcodes.GETSTATIC,
"net/minecraft/client/renderer/Tessellator",
AsmUtil.obf("instance", "field_78398_a"),
AngelicaTweaker.obf("instance", "field_78398_a"),
"Lnet/minecraft/client/renderer/Tessellator;"));
list.add(new VarInsnNode(Opcodes.ASTORE, localIndex));
mn.instructions.insert(list);
@@ -1,4 +1,4 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.generic;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import org.spongepowered.asm.lib.Opcodes;
@@ -1,4 +1,4 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.generic;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import org.spongepowered.asm.lib.tree.AnnotationNode;
import org.spongepowered.asm.lib.tree.ClassNode;
@@ -1,7 +1,6 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.generic;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.transform.AsmUtil;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
import org.spongepowered.asm.lib.tree.ClassNode;
@@ -60,7 +59,7 @@ public class TileEntityNullGuardTransformer {
AbstractInsnNode in = mn.instructions.get(i);
if (in instanceof MethodInsnNode min) {
if (min.getOpcode() == Opcodes.INVOKEINTERFACE
&& min.name.equals(AsmUtil.obf("getTileEntity", "func_147438_o"))
&& min.name.equals(AngelicaTweaker.obf("getTileEntity", "func_147438_o"))
&& min.owner.equals("net/minecraft/world/IBlockAccess")) {
AbstractInsnNode castNodeAbstract = min.getNext();
if (castNodeAbstract instanceof TypeInsnNode castNodeType) {
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.specific;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
import org.spongepowered.asm.lib.Opcodes;
@@ -60,7 +61,7 @@ public class ImmersiveEngineeringTransformer implements IClassTransformer {
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied ImmersiveEngineeringTransformer");
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
@@ -1,7 +1,8 @@
package com.gtnewhorizons.angelica.transform.compat.transformers.specific;
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
import org.spongepowered.asm.lib.ClassWriter;
@@ -47,7 +48,7 @@ public class StacksOnStacksTransformer implements IClassTransformer {
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied StacksOnStacksTransformer");
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
@@ -0,0 +1,49 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
/** IClassTransformer wrapper for {@link AngelicaRedirector} */
public class AngelicaRedirectorTransformer implements IClassTransformer {
private final AngelicaRedirector inner;
private final String[] exclusions;
public AngelicaRedirectorTransformer() {
inner = new AngelicaRedirector(AngelicaTweaker.isObfEnv());
exclusions = inner.getTransformerExclusions();
}
@Override
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
// Ignore classes that are excluded from transformation - Doesn't fully work without the
// TransformerExclusions due to some nested classes
for (String exclusion : exclusions) {
if (transformedName.startsWith(exclusion)) return basicClass;
}
if (!inner.shouldTransform(basicClass)) {
return basicClass;
}
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = inner.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
}
}
@@ -0,0 +1,91 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.FieldLevelTessellatorTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.HUDCachingEarlyReturnTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.ThreadSafeISBRHAnnotationTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.TileEntityNullGuardTransformer;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.transformers.MixinClassWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class GenericCompatTransformer implements IClassTransformer {
private final Map<String, List<String>> fieldLevelTessellator = new Object2ObjectOpenHashMap<>();
private final Map<String, List<String>> tileEntityNullGuard = new Object2ObjectOpenHashMap<>();
private final Map<String, Boolean> threadSafeIBSRH = new Object2BooleanOpenHashMap<>();
private final Map<String, List<String>> hudCachingEarlyReturn = new Object2ObjectOpenHashMap<>();
private final Set<String> targetedClasses = new ObjectOpenHashSet<>();
public GenericCompatTransformer() {
for (ICompatHandler handler : CompatHandlers.getHandlers()) {
registerHandler(handler);
}
buildTargetClassSet();
}
private void registerHandler(ICompatHandler handler) {
if (handler.getFieldLevelTessellator() != null) {
fieldLevelTessellator.putAll(handler.getFieldLevelTessellator());
}
if (handler.getTileEntityNullGuard() != null) {
tileEntityNullGuard.putAll(handler.getTileEntityNullGuard());
}
if (handler.getThreadSafeISBRHAnnotations() != null) {
threadSafeIBSRH.putAll(handler.getThreadSafeISBRHAnnotations());
}
if (handler.getHUDCachingEarlyReturn() != null) {
hudCachingEarlyReturn.putAll(handler.getHUDCachingEarlyReturn());
}
}
private void buildTargetClassSet() {
targetedClasses.addAll(fieldLevelTessellator.keySet());
targetedClasses.addAll(tileEntityNullGuard.keySet());
targetedClasses.addAll(threadSafeIBSRH.keySet());
targetedClasses.addAll(hudCachingEarlyReturn.keySet());
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
if (!targetedClasses.contains(transformedName)) return basicClass;
ClassReader cr = new ClassReader(basicClass);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
if (fieldLevelTessellator.containsKey(transformedName)) {
FieldLevelTessellatorTransformer.transform(cn, fieldLevelTessellator.get(transformedName));
}
if (tileEntityNullGuard.containsKey(transformedName)) {
TileEntityNullGuardTransformer.transform(cn, tileEntityNullGuard.get(transformedName));
}
if (threadSafeIBSRH.containsKey(transformedName)) {
ThreadSafeISBRHAnnotationTransformer.transform(cn, threadSafeIBSRH.get(transformedName));
}
if (hudCachingEarlyReturn.containsKey(transformedName)) {
HUDCachingEarlyReturnTransformer.transform(cn, hudCachingEarlyReturn.get(transformedName));
}
MixinClassWriter cw = new MixinClassWriter(MixinClassWriter.COMPUTE_MAXS | MixinClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
}
@@ -1,7 +1,8 @@
package com.gtnewhorizons.angelica.transform;
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.commons.lang3.tuple.Pair;
import org.objectweb.asm.ClassReader;
@@ -10,10 +11,9 @@ import org.objectweb.asm.tree.ClassNode;
import java.util.List;
public class BlockTransformer implements IClassTransformer {
public class SodiumBlockTransformer implements IClassTransformer {
private static final String BlockClassFriendly = "net.minecraft.block.Block";
public static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
private static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
Pair.of("minX", "field_149759_B"),
Pair.of("minY", "field_149760_C"),
Pair.of("minZ", "field_149754_D"),
@@ -23,12 +23,13 @@ public class BlockTransformer implements IClassTransformer {
);
/**
* Delete the global vanilla bounding box fields off the Block object. {@link RedirectorTransformer}
* Delete the global vanilla bounding box fields off the Block object. {@link AngelicaRedirector}
* replaces these with a thread-safe alternative.
*/
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass != null && BlockClassFriendly.equals(transformedName)) {
if (basicClass == null) return null;
if ("net.minecraft.block.Block".equals(transformedName)) {
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
@@ -36,7 +37,7 @@ public class BlockTransformer implements IClassTransformer {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
@@ -0,0 +1,36 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.util.List;
public class AngelicaLateTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
// Run after Mixins, but before LWJGl3ify
String transformer = "com.gtnewhorizons.angelica.loading.fml.transformers.AngelicaRedirectorTransformer";
FMLRelaunchLog.finer("Registering transformer %s", transformer);
Launch.classLoader.registerTransformer(transformer);
return new String[0];
}
}
@@ -1,11 +1,7 @@
package com.gtnewhorizons.angelica.loading;
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.transform.BlockTransformer;
import com.gtnewhorizons.angelica.transform.RedirectorTransformer;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
@@ -23,61 +19,44 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
public class IncompatibleModsDisablerTweaker implements ITweaker {
private static final boolean DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS = true;
public class MixinCompatHackTweaker implements ITweaker {
public static final boolean DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS = true;
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
verifyDependencies();
// no-op
}
if(DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS) {
LOGGER.info("Disabling Optifine, Fastcraft, BetterFPS, and other incompatible mods (if present)");
disableIncompatibleMods();
}
if (AngelicaConfig.enableHudCaching){
disableXaerosMinimapWaypointTransformer();
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
if (DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Disabling Optifine, Fastcraft, BetterFPS, and other incompatible mods (if present)");
disableIncompatibleMods(classLoader);
}
}
private void verifyDependencies() {
if(MixinCompatHackTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib 0.2.1 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
@Override
public String getLaunchTarget() {
return null;
}
private void disableXaerosMinimapWaypointTransformer(){
try {
final LaunchClassLoader lcl = Launch.classLoader;
final Field xformersField = lcl.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
@SuppressWarnings("unchecked")
List<IClassTransformer> xformers = (List<IClassTransformer>) xformersField.get(lcl);
for (int idx = xformers.size() - 1; idx >= 0; idx--) {
final String name = xformers.get(idx).getClass().getName();
if (name.startsWith("xaero.common.core.transformer.GuiIngameForgeTransformer")) {
LOGGER.info("Removing transformer " + name);
xformers.remove(idx);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
@Override
public String[] getLaunchArguments() {
return new String[0];
}
@SuppressWarnings("unchecked")
private void disableIncompatibleMods() {
private void disableIncompatibleMods(LaunchClassLoader lcl) {
// Remove transformers, Mod Containers, and mixins for Optifine, Fastcraft, BetterFPS and other incompatible mods
try {
final LaunchClassLoader lcl = Launch.classLoader;
final Field xformersField = lcl.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
final List<IClassTransformer> xformers = (List<IClassTransformer>) xformersField.get(lcl);
for (int idx = xformers.size() - 1; idx >= 0; idx--) {
final String name = xformers.get(idx).getClass().getName();
if (name.startsWith("optifine") || name.startsWith("fastcraft") || name.startsWith("me.guichaguri.betterfps")) {
LOGGER.info("Removing transformer " + name);
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing transformer " + name);
xformers.remove(idx);
}
}
@@ -85,13 +64,14 @@ public class MixinCompatHackTweaker implements ITweaker {
throw new RuntimeException(e);
}
try {
final Field injectedContainersField = Loader.class.getDeclaredField("injectedContainers");
final Class<?> loaderClass = Class.forName("cpw.mods.fml.common.Loader", true, lcl);
final Field injectedContainersField = loaderClass.getDeclaredField("injectedContainers");
injectedContainersField.setAccessible(true);
final List<String> containers = (List<String> ) injectedContainersField.get(Loader.class);
final List<String> containers = (List<String>) injectedContainersField.get(loaderClass);
for (int idx = containers.size() - 1; idx >= 0; idx--) {
final String name = containers.get(idx);
final String name = containers.get(idx);
if (name.startsWith("optifine") || name.startsWith("fastcraft")) {
LOGGER.info("Removing mod container " + name);
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mod container " + name);
containers.remove(idx);
}
}
@@ -105,7 +85,7 @@ public class MixinCompatHackTweaker implements ITweaker {
for (int idx = reparsedCoremods.size() - 1; idx >= 0; idx--) {
final String coreMod = reparsedCoremods.get(idx);
if (coreMod.startsWith("optimizationsandtweaks")) {
LOGGER.info("Removing reparsed coremod " + coreMod);
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing reparsed coremod " + coreMod);
// Fool the CoreModManager into not checking for a mod container again later
loadedCoremods.add(reparsedCoremods.remove(idx));
@@ -120,7 +100,7 @@ public class MixinCompatHackTweaker implements ITweaker {
for (int idx = mixinConfigsDefault.size() - 1; idx >= 0; idx--) {
final String name = mixinConfigsDefault.get(idx);
if (name != null && name.contains("optimizationsandtweaks")) {
LOGGER.info("Removing mixin config " + name);
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mixin config " + name);
mixinConfigsDefault.remove(idx);
}
}
@@ -131,7 +111,7 @@ public class MixinCompatHackTweaker implements ITweaker {
for (Config config : mixinConfigs) {
final String name = config.getName();
if (name != null && name.contains("optimizationsandtweaks")) {
LOGGER.info("Removing queued mixin config " + config.getName());
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing queued mixin config " + config.getName());
toRemove.add(config);
}
}
@@ -144,8 +124,8 @@ public class MixinCompatHackTweaker implements ITweaker {
final Map<IContainerHandle, MixinContainer> containers = (Map<IContainerHandle, MixinContainer>) containersField.get(platformManager);
for (Map.Entry<IContainerHandle, MixinContainer> entry : containers.entrySet()) {
final String attribute = entry.getKey().getAttribute("MixinConfigs");
if(attribute != null && attribute.contains("optimizationsandtweaks")) {
LOGGER.info("Removing mixin container " + entry.getKey().toString());
if (attribute != null && attribute.contains("optimizationsandtweaks")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mixin container " + entry.getKey().toString());
containers.remove(entry.getKey());
}
}
@@ -155,31 +135,4 @@ public class MixinCompatHackTweaker implements ITweaker {
throw new RuntimeException(e);
}
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
if (FMLLaunchHandler.side().isClient()) {
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
// Run after Mixins, but before LWJGl3ify
Launch.classLoader.registerTransformer(RedirectorTransformer.class.getName());
}
if(AngelicaConfig.enableSodium) {
Launch.classLoader.registerTransformer(BlockTransformer.class.getName());
}
}
return new String[0];
}
}
@@ -0,0 +1,35 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.util.List;
public class SodiumLateTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
String transformer = "com.gtnewhorizons.angelica.loading.fml.transformers.SodiumBlockTransformer";
FMLRelaunchLog.finer("Registering transformer %s", transformer);
Launch.classLoader.registerTransformer(transformer);
return new String[0];
}
}
@@ -0,0 +1,47 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
public class XaerosTransformerDisablerTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
try {
final Field xformersField = classLoader.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
@SuppressWarnings("unchecked")
List<IClassTransformer> transformers = (List<IClassTransformer>) xformersField.get(classLoader);
for (int idx = transformers.size() - 1; idx >= 0; idx--) {
final String name = transformers.get(idx).getClass().getName();
if (name.startsWith("xaero.common.core.transformer.GuiIngameForgeTransformer")) {
transformers.remove(idx);
FMLRelaunchLog.info("[Angelica/XaerosTransformerDisablerTweaker] Removed transformer " + name);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
return new String[0];
}
}
@@ -0,0 +1,11 @@
/**
* ITweaker needs to be in a standalone package
* because FML adds the whole package to the class
* loader exclusions and that causes really hard to
* find bugs. ITweakers also get class loaded
* by the parent class loader and not LaunchClassLoader,
* as a result the ITweakers should not reference any
* class otherwise it will load them (maybe a second time)
* on the parent class loader.
*/
package com.gtnewhorizons.angelica.loading.fml.tweakers;
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.loading.rfb;
import com.gtnewhorizons.angelica.loading.rfb.transformers.RFBAngelicaRedirector;
import com.gtnewhorizons.retrofuturabootstrap.api.PluginContext;
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
@@ -21,8 +22,10 @@ public class AngelicaRfbPlugin implements RfbPlugin {
if (isServer) {
return null;
}
final boolean isObf = RetroFuturaBootstrap.API.launchClassLoader().findClassMetadata("net.minecraft.world.World") == null;
return new RfbClassTransformer[] {
new RedirectorTransformerWrapper()
new RFBAngelicaRedirector(isObf)
};
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.loading.rfb;
package com.gtnewhorizons.angelica.loading.rfb.transformers;
import com.gtnewhorizons.angelica.transform.RedirectorTransformer;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
@@ -10,10 +11,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.jar.Manifest;
/** RFB wrapper for {@link com.gtnewhorizons.angelica.transform.RedirectorTransformer} */
public class RedirectorTransformerWrapper implements RfbClassTransformer {
/** RfbClassTransformer wrapper for {@link AngelicaRedirector} */
public class RFBAngelicaRedirector implements RfbClassTransformer {
public final RedirectorTransformer inner = new RedirectorTransformer();
private final AngelicaRedirector inner;
public RFBAngelicaRedirector(boolean isObf) {
inner = new AngelicaRedirector(isObf);
}
@Pattern("[a-z0-9-]+")
@Override
@@ -33,7 +38,7 @@ public class RedirectorTransformerWrapper implements RfbClassTransformer {
@Override
public @NotNull String @Nullable [] additionalExclusions() {
return RedirectorTransformer.getTransformerExclusions().toArray(new String[0]);
return inner.getTransformerExclusions();
}
@Override
@@ -47,7 +52,7 @@ public class RedirectorTransformerWrapper implements RfbClassTransformer {
// If a class is already a transformed ClassNode, conservatively continue processing.
return true;
}
return inner.shouldRfbTransform(classNode.getOriginalBytes());
return inner.shouldTransform(classNode.getOriginalBytes());
}
@Override
@@ -56,6 +61,7 @@ public class RedirectorTransformerWrapper implements RfbClassTransformer {
final boolean changed = inner.transformClassNode(className, classNode.getNode());
if (changed) {
classNode.computeMaxs();
AngelicaClassDump.dumpRFBClass(className, classNode, this);
}
}
}
@@ -0,0 +1,25 @@
package com.gtnewhorizons.angelica.loading.shared;
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
public class AngelicaClassDump {
private static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.dumpClass");
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
if (DUMP_CLASS) {
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
public static void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
if (DUMP_CLASS) {
final byte[] originalBytes = classNode.getOriginalBytes();
final byte[] transformedBytes = classNode.computeBytes();
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
}
@@ -0,0 +1,6 @@
/**
* Package that contains classes that may be loaded both by RFB and FML classloaders, the code contained in this package
* should not reference any code from outside of this package to avoid causing hard to debug classloading issues unless
* the code is known to be safe with different classloaders.
*/
package com.gtnewhorizons.angelica.loading.shared;
@@ -1,14 +1,12 @@
package com.gtnewhorizons.angelica.transform;
package com.gtnewhorizons.angelica.loading.shared.transformers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import net.coderbot.iris.IrisLogging;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.lang3.tuple.Pair;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@@ -22,7 +20,6 @@ import org.objectweb.asm.tree.MethodNode;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -33,10 +30,25 @@ import java.util.stream.Collectors;
/**
* This transformer redirects many GL calls to our custom GLStateManager
* <p>
* THIS CLASS MIGHT BE LOADED ON A DIFFERENT CLASS LOADER,
* IT SHOULD NOT CALL ANY CODE FROM THE MAIN MOD
*/
public class RedirectorTransformer implements IClassTransformer {
public final class AngelicaRedirector {
// TODO(Alexdoru) properly split this class in two because currently this class is doing
// two very different things :
// - redirect the GL calls to our GL manager class
// - replace the block fields with a thread safe replacement (only if Sodium is enabled)
private static final boolean ASSERT_MAIN_THREAD = Boolean.parseBoolean(System.getProperty("angelica.assertMainThread", "false"));
public AngelicaRedirector(boolean isObf) {
IS_OBF = isObf;
}
private final boolean IS_OBF;
private static final boolean ASSERT_MAIN_THREAD = Boolean.getBoolean("angelica.assertMainThread");
private static final boolean LOG_SPAM = Boolean.getBoolean("angelica.redirectorLogspam");
private static final Logger LOGGER = LogManager.getLogger("AngelicaRedirector");
private static final String Drawable = "org/lwjgl/opengl/Drawable";
private static final String GLStateManager = "com/gtnewhorizons/angelica/glsm/GLStateManager";
private static final String GL11 = "org/lwjgl/opengl/GL11";
@@ -58,25 +70,26 @@ public class RedirectorTransformer implements IClassTransformer {
"startGame", "func_71384_a",
"initializeTextures", "func_77474_a"
);
private static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
Pair.of("minX", "field_149759_B"),
Pair.of("minY", "field_149760_C"),
Pair.of("minZ", "field_149754_D"),
Pair.of("maxX", "field_149755_E"),
Pair.of("maxY", "field_149756_F"),
Pair.of("maxZ", "field_149757_G")
);
/** All classes in <tt>net.minecraft.block.*</tt> are the block subclasses save for these. */
private static final List<String> VanillaBlockExclusions = Arrays.asList(
private static final String[] VanillaBlockExclusions = {
"net/minecraft/block/IGrowable",
"net/minecraft/block/ITileEntityProvider",
"net/minecraft/block/BlockEventData",
"net/minecraft/block/BlockSourceImpl",
"net/minecraft/block/material/"
);
};
private static final ClassConstantPoolParser cstPoolParser = new ClassConstantPoolParser(GL11, GL13, GL14, OpenGlHelper, EXTBlendFunc, ARBMultiTexture, BlockPackage, Project);
private static final ClassConstantPoolParser cstPoolParser = new ClassConstantPoolParser(GL11, GL13, GL14, GL20, OpenGlHelper, EXTBlendFunc, ARBMultiTexture, BlockPackage, Project);
private static final Map<String, Map<String, String>> methodRedirects = new HashMap<>();
private static final Map<Integer, String> glCapRedirects = new HashMap<>();
private static final List<String> TransformerExclusions = Arrays.asList(
"org.lwjgl",
"com.gtnewhorizons.angelica.glsm.",
"com.gtnewhorizons.angelica.transform",
"me.eigenraven.lwjgl3ify"
);
private static int remaps = 0;
private static final Set<String> moddedBlockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
// Block owners we *shouldn't* redirect because they shadow one of our fields
@@ -210,8 +223,7 @@ public class RedirectorTransformer implements IClassTransformer {
try {
final Class<?> angelicaConfig = Class.forName("com.gtnewhorizons.angelica.config.AngelicaConfig", true, Launch.classLoader);
final MethodHandle sodiumGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableSodium", boolean.class);
angelicaConfigSodiumEnabledGetter = sodiumGetter;
angelicaConfigSodiumEnabledGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableSodium", boolean.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -241,45 +253,23 @@ public class RedirectorTransformer implements IClassTransformer {
return isVanillaBlockSubclass(className) || moddedBlockSubclasses.contains(className);
}
public static List<String> getTransformerExclusions() {
return Collections.unmodifiableList(TransformerExclusions);
private String getFieldName(Pair<String, String> fieldPair) {
return IS_OBF ? fieldPair.getRight() : fieldPair.getLeft();
}
public boolean shouldRfbTransform(byte[] basicClass) {
public String[] getTransformerExclusions() {
return new String[]{
"org.lwjgl",
"com.gtnewhorizons.angelica.glsm.",
"com.gtnewhorizons.angelica.transform",
"me.eigenraven.lwjgl3ify"
};
}
public boolean shouldTransform(byte[] basicClass) {
return cstPoolParser.find(basicClass, true);
}
@Override
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
// Ignore classes that are excluded from transformation - Doesn't fully work without the
// TransformerExclusions due to some nested classes
for (String exclusion : TransformerExclusions) {
if (className.startsWith(exclusion)) {
return basicClass;
}
}
if (!cstPoolParser.find(basicClass, true)) {
return basicClass;
}
// Keep in sync with com.gtnewhorizons.angelica.loading.rfb.RedirectorTransformerWrapper
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
}
/** @return Was the class changed? */
public boolean transformClassNode(String transformedName, ClassNode cn) {
if (cn == null) {
@@ -293,19 +283,19 @@ public class RedirectorTransformer implements IClassTransformer {
}
// Check if this class shadows any fields of the parent class
if(moddedBlockSubclasses.contains(cn.name)) {
if (moddedBlockSubclasses.contains(cn.name)) {
// If a superclass shadows, then so do we, because JVM will resolve a reference on our class to that
// superclass
boolean doWeShadow;
if(blockOwnerExclusions.contains(cn.superName)) {
if (blockOwnerExclusions.contains(cn.superName)) {
doWeShadow = true;
} else {
// Check if we declare any known field names
Set<String> fieldsDeclaredByClass = cn.fields.stream().map(f -> f.name).collect(Collectors.toSet());
doWeShadow = BlockTransformer.BlockBoundsFields.stream().anyMatch(pair -> fieldsDeclaredByClass.contains(pair.getLeft()) || fieldsDeclaredByClass.contains(pair.getRight()));
doWeShadow = BlockBoundsFields.stream().anyMatch(pair ->
cn.fields.stream().anyMatch(field -> field.name.equals(getFieldName(pair))));
}
if (doWeShadow) {
AngelicaTweaker.LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
blockOwnerExclusions.add(cn.name);
}
}
@@ -333,11 +323,11 @@ public class RedirectorTransformer implements IClassTransformer {
name = "disable" + name;
}
}
if (IrisLogging.ENABLE_SPAM) {
if (LOG_SPAM) {
if (name == null) {
AngelicaTweaker.LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}(I)V", transformedName, mNode.name, mNode.name);
LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}(I)V", transformedName, mNode.name, mNode.name);
} else {
AngelicaTweaker.LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}()V", transformedName, mNode.name, name);
LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}()V", transformedName, mNode.name, name);
}
}
mNode.owner = GLStateManager;
@@ -348,43 +338,37 @@ public class RedirectorTransformer implements IClassTransformer {
}
changed = true;
redirectInMethod = true;
remaps++;
} else if (mNode.owner.startsWith(Drawable) && mNode.name.equals("makeCurrent")) {
mNode.setOpcode(Opcodes.INVOKESTATIC);
mNode.owner = GLStateManager;
mNode.desc = "(L" + Drawable + ";)V";
mNode.itf = false;
changed = true;
if (IrisLogging.ENABLE_SPAM) {
AngelicaTweaker.LOGGER.info("Redirecting call in {} to GLStateManager.makeCurrent()", transformedName);
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.makeCurrent()", transformedName);
}
} else {
final Map<String, String> redirects = methodRedirects.get(mNode.owner);
if (redirects != null && redirects.containsKey(mNode.name)) {
if (IrisLogging.ENABLE_SPAM) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
AngelicaTweaker.LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, redirects.get(mNode.name), mNode.desc);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, redirects.get(mNode.name), mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = redirects.get(mNode.name);
changed = true;
redirectInMethod = true;
remaps++;
}
}
}
else if ((node.getOpcode() == Opcodes.GETFIELD || node.getOpcode() == Opcodes.PUTFIELD) && node instanceof FieldInsnNode fNode) {
if(!blockOwnerExclusions.contains(fNode.owner) && isBlockSubclass(fNode.owner) && isSodiumEnabled()) {
Pair<String, String> fieldToRedirect = null;
for(Pair<String, String> blockPairs : BlockTransformer.BlockBoundsFields) {
if(fNode.name.equals(blockPairs.getLeft()) || fNode.name.equals(blockPairs.getRight())) {
fieldToRedirect = blockPairs;
break;
}
}
} else if ((node.getOpcode() == Opcodes.GETFIELD || node.getOpcode() == Opcodes.PUTFIELD) && node instanceof FieldInsnNode fNode) {
if (!blockOwnerExclusions.contains(fNode.owner) && isBlockSubclass(fNode.owner) && isSodiumEnabled()) {
Pair<String, String> fieldToRedirect = BlockBoundsFields.stream()
.filter(pair -> fNode.name.equals(getFieldName(pair)))
.findFirst()
.orElse(null);
if (fieldToRedirect != null) {
if (IrisLogging.ENABLE_SPAM) {
AngelicaTweaker.LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
if (LOG_SPAM) {
LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
}
// Perform the redirect
fNode.name = fieldToRedirect.getLeft(); // use unobfuscated name
@@ -423,6 +407,7 @@ public class RedirectorTransformer implements IClassTransformer {
}
private static class RedirectMap<K> extends HashMap<K, K> {
public static RedirectMap<String> newMap() {
return new RedirectMap<>();
}
@@ -1,213 +1,243 @@
package com.gtnewhorizons.angelica.mixins;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import jss.notfine.config.MCPatcherForgeConfig;
import jss.notfine.config.NotFineConfig;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public enum Mixins {
ANGELICA_STARTUP(new Builder("Angelica Startup").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.startup.MixinInitGLStateManager"
@Getter
@RequiredArgsConstructor
public enum Mixins implements IMixins {
ANGELICA_STARTUP(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.startup.MixinInitGLStateManager"
)
),
ANGELICA(new Builder("Angelica").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.MixinActiveRenderInfo"
,"angelica.MixinEntityRenderer"
,"angelica.MixinGameSettings"
,"angelica.MixinMinecraft"
,"angelica.MixinMinecraftServer"
,"angelica.optimizations.MixinRendererLivingEntity"
,"angelica.MixinFMLClientHandler"
,"angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
ANGELICA(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.MixinActiveRenderInfo"
, "angelica.MixinEntityRenderer"
, "angelica.MixinGameSettings"
, "angelica.MixinMinecraft"
, "angelica.MixinMinecraftServer"
, "angelica.optimizations.MixinRendererLivingEntity"
, "angelica.MixinFMLClientHandler"
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
)
),
ANGELICA_VBO(
new Builder("Angelica VBO").addTargetedMod(TargetedMod.VANILLA).setApplyIf(() -> AngelicaConfig.enableVBO).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.vbo.MixinGLAllocation"
,"angelica.vbo.MixinModelRenderer"
,"angelica.vbo.MixinRenderGlobal"
,"angelica.vbo.MixinWavefrontObject"
)
new MixinBuilder()
.setApplyIf(() -> AngelicaConfig.enableVBO)
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.vbo.MixinGLAllocation"
, "angelica.vbo.MixinModelRenderer"
, "angelica.vbo.MixinRenderGlobal"
, "angelica.vbo.MixinWavefrontObject"
)
),
ANGELICA_FONT_RENDERER(new Builder("Angelica Font Renderer").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableFontRenderer).addMixinClasses(
"angelica.fontrenderer.MixinGuiIngameForge"
,"angelica.fontrenderer.MixinFontRenderer"
ANGELICA_FONT_RENDERER(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableFontRenderer)
.addClientMixins(
"angelica.fontrenderer.MixinGuiIngameForge"
, "angelica.fontrenderer.MixinFontRenderer"
, "angelica.fontrenderer.MixinMCResourceAccessor"
)
),
ANGELICA_ENABLE_DEBUG(new Builder("Angelica Debug").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaMod.lwjglDebug).addMixinClasses(
"angelica.debug.MixinProfiler"
,"angelica.debug.MixinSplashProgress"
,"angelica.debug.MixinTextureManager"
ANGELICA_ENABLE_DEBUG(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaMod.lwjglDebug)
.addClientMixins(
"angelica.debug.MixinProfiler"
, "angelica.debug.MixinSplashProgress"
, "angelica.debug.MixinTextureManager"
)
),
ANGELICA_DYNAMIC_LIGHTS(new Builder("Angelica Dynamic Lights").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableDynamicLights).addMixinClasses(
ANGELICA_DYNAMIC_LIGHTS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableDynamicLights)
.addClientMixins(
"angelica.dynamiclights.MixinEntityRenderer"
,"angelica.dynamiclights.MixinEntity"
,"angelica.dynamiclights.MixinWorld"
,"angelica.dynamiclights.MixinItemRenderer"
, "angelica.dynamiclights.MixinEntity"
, "angelica.dynamiclights.MixinWorld"
, "angelica.dynamiclights.MixinItemRenderer"
)
),
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
new Builder("Fix RenderBlockFluid reading the block type from the world access multiple times")
.setPhase(Phase.EARLY).addMixinClasses("angelica.bugfixes.MixinRenderBlockFluid").setSide(Side.BOTH)
.setApplyIf(() -> AngelicaConfig.fixFluidRendererCheckingBlockAgain)
.addTargetedMod(TargetedMod.VANILLA)),
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
.setPhase(Phase.EARLY)
.addClientMixins("angelica.bugfixes.MixinRenderBlockFluid")
.setApplyIf(() -> AngelicaConfig.fixFluidRendererCheckingBlockAgain)),
ANGELICA_LIMIT_DROPPED_ITEM_ENTITIES(new Builder("Dynamically modifies the render distance of dropped items entities to preserve performance")
.setPhase(Phase.EARLY).addMixinClasses("angelica.optimizations.MixinRenderGlobal_ItemRenderDist").setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.dynamicItemRenderDistance)
.addTargetedMod(TargetedMod.VANILLA)),
ANGELICA_LIMIT_DROPPED_ITEM_ENTITIES(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins("angelica.optimizations.MixinRenderGlobal_ItemRenderDist")
.setApplyIf(() -> AngelicaConfig.dynamicItemRenderDistance)),
ANGELICA_ITEM_DISPLAY_LIST_OPTIMIZATION(new Builder("Optimized item rendering by wrapping them with display lists")
.setPhase(Phase.EARLY).addMixinClasses("angelica.itemrenderer.MixinItemRenderer").setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)
.addTargetedMod(TargetedMod.VANILLA)),
ANGELICA_ITEM_RENDERER_OPTIMIZATION(new MixinBuilder("Optimizes in-world item rendering")
.setPhase(Phase.EARLY)
.addClientMixins("angelica.itemrenderer.MixinItemRenderer")
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)),
// Not compatible with the lwjgl debug callbacks, so disable if that's enabled
ARCHAIC_SPLASH(new Builder("ArchaicFix Splash").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug).addMixinClasses(
"angelica.archaic.MixinSplashProgress"
,"angelica.archaic.AccessorSplashProgress"
ARCHAIC_SPLASH(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug)
.addClientMixins(
"angelica.archaic.MixinSplashProgress$3",
"angelica.archaic.AccessorSplashProgress"
)
),
ARCHAIC_CORE(new Builder("Archaic Core").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.archaic.MixinBlockFence"
,"angelica.archaic.MixinFMLClientHandler"
,"angelica.archaic.MixinGuiIngameForge"
,"angelica.archaic.MixinNetHandlerPlayClient"
,"angelica.archaic.MixinThreadDownloadImageData"
ARCHAIC_CORE(new MixinBuilder()
.addExcludedMod(TargetedMod.ARCHAICFIX)
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.archaic.MixinBlockFence"
, "angelica.archaic.MixinFMLClientHandler"
, "angelica.archaic.MixinGuiIngameForge"
, "angelica.archaic.MixinNetHandlerPlayClient"
, "angelica.archaic.MixinThreadDownloadImageData"
)
),
IRIS_STARTUP(new Builder("Start Iris").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.startup.MixinGameSettings"
,"shaders.startup.MixinGuiMainMenu"
,"shaders.startup.MixinInitRenderer"
,"shaders.startup.MixinAbstractTexture"
,"shaders.startup.MixinTextureAtlasSprite"
,"shaders.startup.MixinTextureMap"
IRIS_STARTUP(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.startup.MixinGameSettings"
, "shaders.startup.MixinMinecraft"
, "shaders.startup.MixinInitRenderer"
, "shaders.startup.MixinAbstractTexture"
, "shaders.startup.MixinTextureAtlasSprite"
, "shaders.startup.MixinTextureMap"
)
),
SODIUM(new Builder("Sodium").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableSodium).addMixinClasses(
"sodium.MixinBlock"
,"sodium.MixinBlockFluidBase"
,"sodium.AccessorBiomeColorEvent"
,"sodium.MixinBiomeGenBase"
,"sodium.MixinChunk"
,"sodium.MixinChunkProviderServer"
,"sodium.MixinClientRegistry"
,"sodium.MixinEntity_RenderDist"
,"sodium.MixinEntityItem_RenderDist"
,"sodium.MixinRenderManager"
,"sodium.MixinExtendedBlockStorage"
,"sodium.MixinEntityRenderer"
,"sodium.MixinFMLClientHandler"
,"sodium.MixinForgeHooksClient"
,"sodium.MixinGameSettings"
,"sodium.MixinFrustrum"
,"sodium.MixinMaterial"
,"sodium.MixinMinecraft"
,"sodium.MixinNibbleArray"
,"sodium.MixinRenderBlocks"
,"sodium.MixinRenderGlobal"
,"sodium.MixinWorldClient"
,"sodium.MixinTileEntity"
,"sodium.MixinEffectRenderer"
,"sodium.MixinTileEntityRendererDispatcher"
,"sodium.MixinLongHashMap"
,"sodium.MixinRenderingRegistry"
,"sodium.MixinPlayerManager"
SODIUM(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableSodium)
.addClientMixins(
"sodium.MixinBlock"
, "sodium.MixinBlockFluidBase"
, "sodium.AccessorBiomeColorEvent"
, "sodium.MixinBiomeGenBase"
, "sodium.MixinChunk"
, "sodium.MixinChunkProviderServer"
, "sodium.MixinClientRegistry"
, "sodium.MixinEntity_RenderDist"
, "sodium.MixinEntityItem_RenderDist"
, "sodium.MixinRenderManager"
, "sodium.MixinEntityRenderer"
, "sodium.MixinFMLClientHandler"
, "sodium.MixinForgeHooksClient"
, "sodium.MixinGameSettings"
, "sodium.MixinFrustrum"
, "sodium.MixinMinecraft"
, "sodium.MixinRenderBlocks"
, "sodium.MixinRenderGlobal"
, "sodium.MixinWorldClient"
, "sodium.MixinTileEntity"
, "sodium.MixinTileEntityMobSpawner"
, "sodium.MixinEffectRenderer"
, "sodium.MixinTileEntityRendererDispatcher"
, "sodium.MixinLongHashMap"
, "sodium.MixinRenderingRegistry"
, "sodium.MixinPlayerManager"
)
),
IRIS_RENDERING(new Builder("Iris Shaders").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.MixinEntityRenderer"
,"shaders.MixinGuiIngameForge"
,"shaders.MixinFramebuffer"
,"shaders.MixinItem"
,"shaders.MixinItemRenderer"
,"shaders.MixinLocale"
,"shaders.MixinOpenGlHelper"
,"shaders.MixinRender"
,"shaders.MixinRendererLivingEntity"
,"shaders.MixinRenderGlobal"
,"shaders.MixinTileEntityBeaconRenderer"
IRIS_SHADERS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.MixinEntityRenderer"
, "shaders.MixinGuiIngameForge"
, "shaders.MixinFramebuffer"
, "shaders.MixinItem"
, "shaders.MixinLocale"
, "shaders.MixinOpenGlHelper"
, "shaders.MixinRender"
, "shaders.MixinRendererLivingEntity"
, "shaders.MixinRenderGlobal"
, "shaders.MixinTileEntityBeaconRenderer"
)
),
IRIS_ACCESSORS(new Builder("Iris Accessors").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.accessors.MixinMinecraft"
,"shaders.accessors.MixinEntityRenderer"
,"shaders.accessors.MixinSimpleTexture"
,"shaders.accessors.MixinTextureAtlasSprite"
,"shaders.accessors.MixinTextureMap"
,"shaders.accessors.MixinAnimationMetadataSection"
IRIS_RENDERING_NOBACKHAND(new MixinBuilder("Iris Hand Shaders")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addExcludedMod(TargetedMod.BACKHAND)
.addClientMixins(
"shaders.MixinItemRenderer"
)
),
ANGELICA_TEXTURE(new Builder("Textures").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableSodium).addMixinClasses(
"angelica.textures.MixinTextureAtlasSprite"
,"angelica.textures.MixinTextureUtil"
IRIS_RENDERING_BACKHAND(new MixinBuilder("Iris Hand Shaders (Backhand)")
.addRequiredMod(TargetedMod.BACKHAND)
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.MixinItemRendererBackhand"
)
),
ANGELICA_TEXTURE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableSodium)
.addClientMixins(
"angelica.textures.MixinTextureAtlasSprite"
, "angelica.textures.MixinTextureUtil"
)),
ANGELICA_ZOOM(new Builder("Zoom").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableZoom)
.addMixinClasses(
ANGELICA_ZOOM(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableZoom)
.addClientMixins(
"angelica.zoom.MixinEntityRenderer_Zoom",
"angelica.zoom.MixinMinecraft_Zoom",
"angelica.zoom.MixinMouseFilter"
)),
HUD_CACHING(new Builder("Renders the HUD elements 20 times per second maximum to improve performance")
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableHudCaching).addMixinClasses(
"angelica.hudcaching.MixinGuiIngame",
"angelica.hudcaching.MixinGuiIngameForge",
HUD_CACHING(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableHudCaching)
.addClientMixins(
"angelica.hudcaching.MixinGuiIngame",
"angelica.hudcaching.MixinGuiIngameForge",
"angelica.hudcaching.MixinRenderGameOverlayEvent",
"angelica.hudcaching.MixinEntityRenderer_HUDCaching",
"angelica.hudcaching.MixinFramebuffer_HUDCaching",
"angelica.hudcaching.MixinGuiIngame_HUDCaching",
"angelica.hudcaching.MixinGuiIngameForge_HUDCaching",
"angelica.hudcaching.MixinRenderItem")
),
OPTIMIZE_WORLD_UPDATE_LIGHT(new Builder("Optimize world updateLightByType method").setPhase(Phase.EARLY)
.setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA).addExcludedMod(TargetedMod.ARCHAICFIX).setApplyIf(() -> AngelicaConfig.optimizeWorldUpdateLight)
.addMixinClasses("angelica.lighting.MixinWorld_FixLightUpdateLag")),
OPTIMIZE_WORLD_UPDATE_LIGHT(new MixinBuilder()
.setPhase(Phase.EARLY)
.addExcludedMod(TargetedMod.ARCHAICFIX)
.setApplyIf(() -> AngelicaConfig.optimizeWorldUpdateLight)
.addCommonMixins("angelica.lighting.MixinWorld_FixLightUpdateLag")),
SPEEDUP_VANILLA_ANIMATIONS(new Builder("Speedup Vanilla Animations").setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.speedupAnimations).setSide(Side.CLIENT).addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(
SPEEDUP_VANILLA_ANIMATIONS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins(
"angelica.animation.MixinTextureAtlasSprite",
"angelica.animation.MixinTextureMap",
"angelica.animation.MixinBlockFire",
@@ -218,38 +248,57 @@ public enum Mixins {
"angelica.animation.MixinWorldRenderer",
"angelica.animation.MixinRenderItem")),
EXTRA_UTILITIES_THREAD_SAFETY(new Builder("Enable thread safety fixes in Extra Utilities").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.EXTRAUTILS).setSide(Side.CLIENT)
SCALED_RESOUTION_UNICODE_FIX(new MixinBuilder("Removes unicode languages gui scaling being forced to even values")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.removeUnicodeEvenScaling)
.addClientMixins("angelica.bugfixes.MixinScaledResolution_UnicodeFix")),
SECURITYCRAFT_COMPAT(new MixinBuilder("Fix reflection in SecurityCraft for compat with Angelica")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.SECURITYCRAFT)
.setApplyIf(() -> CompatConfig.fixSecurityCraft)
.addClientMixins(
"client.securitycraft.MixinBlockReinforcedFenceGate",
"client.securitycraft.MixinBlockReinforcedIronBars"
)),
EXTRA_UTILITIES_THREAD_SAFETY(new MixinBuilder("Enable thread safety fixes in Extra Utilities")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.EXTRAUTILS)
.setApplyIf(() -> CompatConfig.fixExtraUtils)
.addMixinClasses(
.addClientMixins(
"client.extrautils.MixinRenderBlockConnectedTextures",
"client.extrautils.MixinRenderBlockConnectedTexturesEthereal",
"client.extrautils.MixinIconConnectedTexture")),
MFR_THREAD_SAFETY(new Builder("Enable thread safety fixes for MineFactory Reloaded").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.MINEFACTORY_RELOADED).setSide(Side.CLIENT)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addMixinClasses("client.minefactoryreloaded.MixinRedNetCableRenderer")),
MFR_THREAD_SAFETY(new MixinBuilder("Enable thread safety fixes for MineFactory Reloaded")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.MINEFACTORY_RELOADED)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addClientMixins("client.minefactoryreloaded.MixinRedNetCableRenderer")),
SPEEDUP_CAMPFIRE_BACKPORT_ANIMATIONS(new Builder("Add animation speedup support to Campfire Backport").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.CAMPFIRE_BACKPORT).setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addMixinClasses("client.campfirebackport.MixinRenderBlockCampfire")),
SPEEDUP_CAMPFIRE_BACKPORT_ANIMATIONS(new MixinBuilder("Add animation speedup support to Campfire Backport")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.CAMPFIRE_BACKPORT)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins("client.campfirebackport.MixinRenderBlockCampfire")),
IC2_FLUID_RENDER_FIX(new Builder("IC2 Fluid Render Fix").setPhase(Phase.EARLY).setSide(Side.CLIENT)
.addTargetedMod(TargetedMod.IC2).setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addMixinClasses("angelica.textures.ic2.MixinRenderLiquidCell")),
IC2_FLUID_RENDER_FIX(new MixinBuilder()
.setPhase(Phase.EARLY)
.addRequiredMod(TargetedMod.IC2)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins("angelica.textures.ic2.MixinRenderLiquidCell")),
OPTIMIZE_TEXTURE_LOADING(new Builder("Optimize Texture Loading").setPhase(Phase.EARLY)
.addMixinClasses("angelica.textures.MixinTextureUtil_OptimizeMipmap").addTargetedMod(TargetedMod.VANILLA)
.setApplyIf(() -> AngelicaConfig.optimizeTextureLoading).setSide(Side.CLIENT)),
OPTIMIZE_TEXTURE_LOADING(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins("angelica.textures.MixinTextureUtil_OptimizeMipmap")
.setApplyIf(() -> AngelicaConfig.optimizeTextureLoading)),
//From NotFine
NOTFINE_BASE_MOD(new Builder("NotFine")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
NOTFINE_BASE_MOD(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.",
.addClientMixins(addPrefix("notfine.",
"clouds.MixinEntityRenderer",
"clouds.MixinGameSettings",
//"clouds.MixinRenderGlobal",
@@ -281,11 +330,10 @@ public enum Mixins {
"interpolatedtexturemap.MixinTextureMap"
))
),
BETTER_FACE_CULLING(new Builder("Better face culling")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
BETTER_FACE_CULLING(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> NotFineConfig.betterBlockFaceCulling)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.faceculling.",
.addClientMixins(addPrefix("notfine.faceculling.",
"MixinBlock",
"MixinBlockCactus",
"MixinBlockCarpet",
@@ -297,103 +345,94 @@ public enum Mixins {
"MixinRenderBlocks"
))
),
NOTFINE_NO_DYNAMIC_SURROUNDINGS(new Builder("NotFine no Dynamic Surroundings")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> true)
.addTargetedMod(TargetedMod.VANILLA)
NOTFINE_NO_DYNAMIC_SURROUNDINGS(new MixinBuilder()
.setPhase(Phase.EARLY)
.addExcludedMod(TargetedMod.DYNAMIC_SURROUNDINGS_MIST)
.addExcludedMod(TargetedMod.DYNAMIC_SURROUNDINGS_ORIGINAL)
.addMixinClasses("notfine.toggle.MixinEntityRenderer$RenderRainSnow")
.addClientMixins("notfine.toggle.MixinEntityRenderer$RenderRainSnow")
),
NOTFINE_NO_CUSTOM_ITEM_TEXTURES(new Builder("NotFine no Custom Item Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
NOTFINE_NO_CUSTOM_ITEM_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> !AngelicaConfig.enableMCPatcherForgeFeatures || !MCPatcherForgeConfig.CustomItemTextures.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.glint.",
.addClientMixins(addPrefix("notfine.glint.",
"MixinItemRenderer",
"MixinRenderItem"
))
),
NOTFINE_NATURA(new Builder("NotFine Natura compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
NOTFINE_NATURA_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.NATURA)
.addMixinClasses(addPrefix("notfine.leaves.natura.",
.addRequiredMod(TargetedMod.NATURA)
.addClientMixins(addPrefix("notfine.leaves.natura.",
"MixinBerryBush",
"MixinNetherBerryBush"
))
),
NOTFINE_THAUMCRAFT(new Builder("NotFine Thaumcraft compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
NOTFINE_THAUMCRAFT_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.THAUMCRAFT)
.addMixinClasses("notfine.leaves.thaumcraft.MixinBlockMagicalLeaves")
.addRequiredMod(TargetedMod.THAUMCRAFT)
.addClientMixins("notfine.leaves.thaumcraft.MixinBlockMagicalLeaves")
),
THAUMCRAFT_BETTER_FACE_CULLING(new Builder("Better face culling Thaumcraft compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
THAUMCRAFT_BETTER_FACE_CULLING(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> NotFineConfig.betterBlockFaceCulling)
.addTargetedMod(TargetedMod.THAUMCRAFT)
.addMixinClasses(addPrefix("notfine.faceculling.thaumcraft.",
.addRequiredMod(TargetedMod.THAUMCRAFT)
.addClientMixins(addPrefix("notfine.faceculling.thaumcraft.",
"MixinBlockWoodenDevice",
"MixinBlockStoneDevice",
"MixinBlockTable"
))
),
NOTFINE_TINKERS_CONSTRUCT(new Builder("NotFine Tinker's Construct compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
NOTFINE_TINKERS_CONSTRUCT_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.TINKERS_CONSTRUCT)
.addMixinClasses("notfine.leaves.tconstruct.MixinOreberryBush")
.addRequiredMod(TargetedMod.TINKERS_CONSTRUCT)
.addClientMixins("notfine.leaves.tconstruct.MixinOreberryBush")
),
NOTFINE_WITCHERY(new Builder("NotFine Witchery compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
NOTFINE_WITCHERY_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.WITCHERY)
.addMixinClasses("notfine.leaves.witchery.MixinBlockWitchLeaves")
.addRequiredMod(TargetedMod.WITCHERY)
.addClientMixins("notfine.leaves.witchery.MixinBlockWitchLeaves")
),
NOTFINE_TWILIGHT_FOREST(new Builder("NotFine Twilight Forest compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
NOTFINE_TWILIGHT_FOREST_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addTargetedMod(TargetedMod.TWILIGHT_FOREST)
.addMixinClasses(addPrefix("notfine.leaves.twilightforest.",
.addRequiredMod(TargetedMod.TWILIGHT_FOREST)
.addClientMixins(addPrefix("notfine.leaves.twilightforest.",
"MixinBlockTFLeaves",
"MixinBlockTFLeaves3",
// TODO: Verify 2.3.8.18 or later to support non NH builds?
"MixinBlockTFMagicLeaves"
))
),
MCPATCHER_FORGE(new Builder("MCPatcher Forge")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.",
.addClientMixins(addPrefix("mcpatcherforge.",
"base.MixinBlockGrass",
"base.MixinBlockMycelium",
"base.MixinAbstractTexture",
"base.MixinTextureAtlasSprite",
"base.MixinSimpleReloadableResourceManager",
"base.MixinMinecraft"
))
),
MCPATCHER_FORGE_RENDERPASS(new Builder("MCPatcher Forge Renderpass")
MCPATCHER_FORGE_RENDERPASS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> NotFineConfig.renderPass)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.",
.addClientMixins(addPrefix("mcpatcherforge.",
"renderpass.MixinEntityRenderer",
"renderpass.MixinRenderBlocks",
"renderpass.MixinRenderGlobal",
"renderpass.MixinWorldRenderer"
))
),
MCPATCHER_FORGE_CUSTOM_COLORS(new Builder("MCP:F Custom Colors")
.setSide(Mixins.Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CUSTOM_COLORS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.cc.",
.addClientMixins(addPrefix("mcpatcherforge.cc.",
"block.material.MixinMapColor",
"block.MixinBlock",
@@ -445,11 +484,10 @@ public enum Mixins {
"world.MixinWorldProviderHell"
))
),
MCPATCHER_FORGE_CUSTOM_ITEM_TEXTURES(new Builder("MCP:F Custom Item Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CUSTOM_ITEM_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.CustomItemTextures.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.cit.",
.addClientMixins(addPrefix("mcpatcherforge.cit.",
"client.renderer.entity.MixinRenderBiped",
"client.renderer.entity.MixinRenderEntityLiving",
"client.renderer.entity.MixinRenderItem",
@@ -464,34 +502,30 @@ public enum Mixins {
"world.MixinWorld"
))
),
MCPATCHER_FORGE_CONNECTED_TEXTURES(new Builder("MCP:F Connected Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CONNECTED_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ConnectedTextures.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm.MixinRenderBlocks")
.addClientMixins("mcpatcherforge.ctm.MixinRenderBlocks")
),
MCPATCHER_FORGE_EXTENDED_HD(new Builder("MCP:F Extended hd")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_EXTENDED_HD(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ExtendedHD.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.hd.",
.addClientMixins(addPrefix("mcpatcherforge.hd.",
"MixinTextureClock",
"MixinTextureCompass",
"MixinTextureManager"
))
),
MCPATCHER_FORGE_EXTENDED_HD_FONT(new Builder("MCP:F Extended HD Font")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_EXTENDED_HD_FONT(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> (AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ExtendedHD.enabled && MCPatcherForgeConfig.ExtendedHD.hdFont))
.addTargetedMod(TargetedMod.VANILLA)
.addExcludedMod(TargetedMod.COFHCORE)
.addMixinClasses("mcpatcherforge.hd.MixinFontRenderer")
.addClientMixins("mcpatcherforge.hd.MixinFontRenderer")
),
MCPATCHER_FORGE_RANDOM_MOBS(new Builder("MCP:F Random Mobs")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_RANDOM_MOBS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.RandomMobs.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.mob.",
.addClientMixins(addPrefix("mcpatcherforge.mob.",
"MixinRender",
"MixinRenderEnderman",
"MixinRenderFish",
@@ -504,220 +538,52 @@ public enum Mixins {
"MixinEntityLivingBase"
))
),
MCPATCHER_FORGE_SKY(new Builder("MCP:F Sky")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_SKY(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.BetterSkies.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.sky.",
.addClientMixins(addPrefix("mcpatcherforge.sky.",
"MixinEffectRenderer",
"MixinRenderGlobal"
))
),
MCPATCHER_FORGE_CC_NO_CTM(new Builder("MCP:F Custom Colors, no Connected Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CC_NO_CTM(new MixinBuilder("MCP:F Custom Colors, no Connected Textures")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& !MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.cc_ctm.MixinRenderBlocksNoCTM")
&& !MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCTM")
),
MCPATCHER_FORGE_CTM_NO_CC(new Builder("MCP:F Connected Textures, no Custom Colours")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_NO_CC(new MixinBuilder("MCP:F Connected Textures, no Custom Colours")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& !MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCC")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& !MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCC")
),
MCPATCHER_FORGE_CTM_AND_CC(new Builder("MCP:F Connected Textures and Custom Colors")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_AND_CC(new MixinBuilder("MCP:F Connected Textures and Custom Colors")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinRenderBlocks")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocks")
),
MCPATCHER_FORGE_CTM_OR_CC(new Builder("MCP:F Connected Textures or Custom Colors")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_OR_CC(new MixinBuilder("MCP:F Connected Textures or Custom Colors")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
|| MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinTextureMap")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
|| MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinTextureMap")
),
//End from NotFine
QPR(new Builder("Adds a QuadProvider field to blocks without populating it")
.setSide(Side.CLIENT)
.setPhase(Phase.EARLY)
.setApplyIf(() -> true)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(
"angelica.models.MixinBlock",
"angelica.models.MixinBlockOldLeaf")),
;
private final List<String> mixinClasses;
private final Supplier<Boolean> applyIf;
private final Phase phase;
private final Side side;
private final List<TargetedMod> targetedMods;
private final List<TargetedMod> excludedMods;
private final MixinBuilder builder;
Mixins(Builder builder) {
this.mixinClasses = builder.mixinClasses;
this.applyIf = builder.applyIf;
this.side = builder.side;
this.targetedMods = builder.targetedMods;
this.excludedMods = builder.excludedMods;
this.phase = builder.phase;
if (this.targetedMods.isEmpty()) {
throw new RuntimeException("No targeted mods specified for " + this.name());
}
if (this.applyIf == null) {
throw new RuntimeException("No ApplyIf function specified for " + this.name());
}
}
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
NotFineConfig.loadSettings();
//This may be possible to handle differently or fix.
if(loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
}
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Mixins.Phase.EARLY) {
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
AngelicaTweaker.LOGGER.info("Not loading the following EARLY mixins: {}", notLoading);
return mixins;
}
public static List<String> getLateMixins(Set<String> loadedMods) {
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Mixins.Phase.LATE) {
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
AngelicaTweaker.LOGGER.info("Not loading the following LATE mixins: {}", notLoading.toString());
return mixins;
}
private boolean shouldLoadSide() {
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
}
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return false;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& !loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return true;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
return (shouldLoadSide() && applyIf.get()
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
}
private static class Builder {
private final List<String> mixinClasses = new ArrayList<>();
private Supplier<Boolean> applyIf = () -> true;
private Side side = Side.BOTH;
private Phase phase = Phase.LATE;
private final List<TargetedMod> targetedMods = new ArrayList<>();
private final List<TargetedMod> excludedMods = new ArrayList<>();
public Builder(@SuppressWarnings("unused") String description) {}
public Builder addMixinClasses(String... mixinClasses) {
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
return this;
}
public Builder setPhase(Phase phase) {
this.phase = phase;
return this;
}
public Builder setSide(Side side) {
this.side = side;
return this;
}
public Builder setApplyIf(Supplier<Boolean> applyIf) {
this.applyIf = applyIf;
return this;
}
public Builder addTargetedMod(TargetedMod mod) {
this.targetedMods.add(mod);
return this;
}
public Builder addExcludedMod(TargetedMod mod) {
this.excludedMods.add(mod);
return this;
}
}
@SuppressWarnings("SimplifyStreamApiCallChains")
private static String[] addPrefix(String prefix, String... values) {
return Arrays.stream(values)
.map(s -> prefix + s)
.collect(Collectors.toList())
.toArray(new String[values.length]);
}
private enum Side {
BOTH,
CLIENT,
SERVER
}
private enum Phase {
EARLY,
LATE,
List<String> list = new ArrayList<>(values.length);
for (String s : values) {
list.add(prefix + s);
}
return list.toArray(new String[values.length]);
}
}
@@ -1,59 +1,31 @@
package com.gtnewhorizons.angelica.mixins;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder;
import lombok.Getter;
public enum TargetedMod {
@Getter
public enum TargetedMod implements ITargetMod {
VANILLA("Minecraft", null)
, FASTCRAFT("FastCraft", "fastcraft.Tweaker")
, OPTIFINE("Optifine", "optifine.OptiFineForgeTweaker", "Optifine")
, ARCHAICFIX("ArchaicFix", "org.embeddedt.archaicfix.ArchaicCore", "archaicfix")
, BOTANIA("Botania", null, "Botania")
, CAMPFIRE_BACKPORT("CampfireBackport", null, "campfirebackport")
, CHICKENCHUNKS("ChickenChunks", null, "ChickenChunks")
, COFHCORE("CoFHCore", "cofh.asm.LoadingPlugin", "CoFHCore")
, DYNAMIC_SURROUNDINGS_MIST("Dynamic Surroundings", "org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround")
, DYNAMIC_SURROUNDINGS_ORIGINAL("Dynamic Surroundings", "org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround")
, EXTRAUTILS("ExtraUtilities", null, "ExtraUtilities")
, MINEFACTORY_RELOADED("MineFactory Reloaded", null, "MineFactoryReloaded")
, GTNHLIB("GTNHLib", "com.gtnewhorizon.gtnhlib.core.GTNHLibCore", "gtnhlib")
, IC2("IC2", "ic2.core.coremod.IC2core", "IC2")
, JABBA("JABBA", null, "JABBA")
, JOURNEYMAP("JourneyMap", null, "journeymap")
, LWJGL3IFY("lwjgl3ify", "me.eigenraven.lwjgl3ify.core.Lwjgl3ifyCoremod", "lwjgl3ify")
, MRTJPCORE("MrTJPCore", null, "MrTJPCoreMod")
, NATURA("Natura", null, "Natura")
, PROJECTRED_ILLUMINATION("ProjectRed Illumination", null, "ProjRed|Illumination")
, SMART_RENDER("Smart Render", null, "SmartRender")
, THAUMCRAFT("Thaumcraft", null, "Thaumcraft")
, TINKERS_CONSTRUCT("Tinker's Construct", null, "TConstruct")
, TWILIGHT_FOREST("TwilightForest", null, "TwilightForest")
, WITCHERY("Witchery", null, "witchery")
;
ARCHAICFIX("org.embeddedt.archaicfix.ArchaicCore", "archaicfix"),
BACKHAND("xonin.backhand.coremod.BackhandLoadingPlugin", "backhand"),
CAMPFIRE_BACKPORT(null, "campfirebackport"),
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
EXTRAUTILS(null, "ExtraUtilities"),
MINEFACTORY_RELOADED(null, "MineFactoryReloaded"),
IC2("ic2.core.coremod.IC2core", "IC2"),
NATURA(null, "Natura"),
SECURITYCRAFT(null, "securitycraft"),
THAUMCRAFT(null, "Thaumcraft"),
TINKERS_CONSTRUCT(null, "TConstruct"),
TWILIGHT_FOREST(null, "TwilightForest"),
WITCHERY(null, "witchery");
private final TargetModBuilder builder;
/** The "name" in the @Mod annotation */
@Getter
public final String modName;
/** Class that implements the IFMLLoadingPlugin interface */
@Getter
public final String coreModClass;
/** The "modid" in the @Mod annotation */
@Getter
public final String modId;
TargetedMod(String modName, String coreModClass) {
this(modName, coreModClass, null);
}
TargetedMod(String modName, String coreModClass, String modId) {
this.modName = modName;
this.coreModClass = coreModClass;
this.modId = modId;
}
@Override
public String toString() {
return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}";
TargetedMod(String coreModClass, String modId) {
this.builder = new TargetModBuilder().setCoreModClass(coreModClass).setModId(modId);
}
}
@@ -1,14 +0,0 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
public interface AnimationMetadataSectionAccessor {
int getFrameWidth();
void setFrameWidth(int frameWidth);
int getFrameHeight();
void setFrameHeight(int frameHeight);
}
@@ -1,9 +0,0 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import org.spongepowered.asm.mixin.gen.Accessor;
public interface ExtendedBlockStorageAccessor {
int getYBase();
int getBlockRefCount();
void setBlockRefCount(int blockRefCount);
}
@@ -1,7 +0,0 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface ExtendedNibbleArray {
public byte[] getData();
public int getDepthBits();
public int getDepthBitsPlusFour();
}
@@ -8,4 +8,5 @@ public interface ITexturesCache {
Set<IIcon> getRenderedTextures();
void enableTextureTracking();
void track(IPatchedTextureAtlasSprite sprite);
}
@@ -1,7 +0,0 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.util.Timer;
public interface MinecraftAccessor {
Timer getTimer();
}
Loaded 100 of 456 files, more files were not shown because too many files have changed in this diff. Show more