Compare commits

...

352 Commits

Author SHA1 Message Date
embeddedt
7e9865411e
Update to Neo 21.4.47-beta 2024-12-29 19:57:00 -05:00
embeddedt
28664ddc3a
Fix faster_item_rendering on Neo 2024-12-27 18:14:35 -05:00
embeddedt
5493c736ee
Update Neo & Parchment 2024-12-27 18:14:29 -05:00
embeddedt
3d30de6398
Merge branch '1.21.4' of github.com:embeddedt/ModernFix into 1.21.4 2024-12-26 15:25:32 -05:00
embeddedt
06b1e8f5c3
Merge 1.21.1 into 1.21.4 2024-12-26 15:24:42 -05:00
embeddedt
fc96643a89
Merge 1.20 into 1.21.1 2024-12-26 15:24:41 -05:00
embeddedt
c66dd2382c
Remove LDLib integration as it will likely need changes for 1.21.4 2024-12-26 15:24:09 -05:00
embeddedt
2d263436f6
Merge remote-tracking branch 'origin/1.21.1' into 1.21.4 2024-12-26 15:23:35 -05:00
embeddedt
eeb842332b
Merge remote-tracking branch 'origin/1.20' into 1.21.1 2024-12-26 15:00:06 -05:00
embeddedt
e0f941cfe5
Port faster item rendering to 1.21.4 2024-12-25 20:01:05 -05:00
embeddedt
a5af6e5c1f
Guarantee deterministic behavior of values() iteration 2024-12-25 19:45:27 -05:00
embeddedt
80070aa201
Merge remote-tracking branch 'origin/1.21.1' into 1.21.4 2024-12-25 19:29:33 -05:00
embeddedt
6531b69fb9
Merge remote-tracking branch 'origin/1.20' into 1.21.1 2024-12-25 16:23:07 -05:00
embeddedt
2f977822df
Support the new Fabric model events 2024-12-24 15:38:33 -05:00
embeddedt
9ed71dcdb4
Provide emulated registries permanently 2024-12-24 15:14:41 -05:00
embeddedt
46913ac8b2
Spotless 2024-12-12 22:51:31 -05:00
embeddedt
a0cc8bfbd2
Add Fabric Model Loading API support 2024-12-12 22:49:12 -05:00
embeddedt
e7e065f809
Clean up some log messages 2024-12-12 21:43:21 -05:00
embeddedt
19d2d8cfc0
Get dynamic model loading working with ModifyBakingResult 2024-12-12 21:31:21 -05:00
embeddedt
0f16e159f9
Merge remote-tracking branch 'origin/1.20' into 1.21.1 2024-12-12 19:53:36 -05:00
embeddedt
1d4ddc302a
Merge commit '14a89f94a63d68e855ec38da039363d0147c8147' into 1.21.1 2024-12-12 19:53:23 -05:00
embeddedt
de5b79fe7c
Fix model parents not always being resolved
This would cause models to sometimes not appear at all (CC turtles were one reproduction case)
2024-12-07 21:44:41 -05:00
embeddedt
e82f316216
Remove old AW entry 2024-12-07 17:22:35 -05:00
embeddedt
2901b53424
Spotless 2024-12-07 17:19:39 -05:00
embeddedt
b822f5ce87
Dynamic model loading on Fabric 2024-12-07 17:13:30 -05:00
embeddedt
145896cc99
Initial port to 1.21.4 2024-12-07 15:52:34 -05:00
embeddedt
359d1e81d6
Merge 1.21.3 into 1.21.4 2024-11-29 16:43:01 -05:00
embeddedt
1bb730b2b0
Merge 1.21.1 into 1.21.3 2024-11-29 16:42:59 -05:00
embeddedt
86f06b2f36
Merge 1.20 into 1.21.1 2024-11-29 16:42:45 -05:00
pietro-lopes
520de2c12b
updated spark (#484) 2024-11-28 08:46:29 -05:00
embeddedt
a29bdb2f82
Fix standalone model variant not being loaded
Related: #475
2024-11-12 08:17:12 -05:00
embeddedt
c8ca6a5d53
24w45a
Dynamic resources and faster item rendering are gone for now
2024-11-06 14:59:26 -05:00
embeddedt
103dea589f
fix AW 2024-11-04 16:58:57 -05:00
embeddedt
7645af1bab
Merge 1.21.1 into 1.21.3 2024-11-04 16:55:12 -05:00
embeddedt
cdfe53589e
Merge 1.20 into 1.21.1 2024-11-04 16:55:10 -05:00
embeddedt
1e0d4d9d07
Remove useless test 2024-10-28 11:40:31 -04:00
embeddedt
5450a16aad
Rewrite dynamic resources once again 2024-10-28 11:37:03 -04:00
embeddedt
d8b86708e5
Fix another outdated mixin 2024-10-28 11:36:40 -04:00
Rhys
6012626112
Port cause_lag_by_disabling_threads to 1.21.3 2024-10-28 10:21:51 -04:00
embeddedt
3d785cedb4
Remove obsolete mixin 2024-10-27 21:48:59 -04:00
Rhys
b1f5f6ebbe
Fix faster item rendering mixin 2024-10-27 21:46:58 -04:00
embeddedt
91ffdb1acf
Spotless 2024-10-26 18:44:36 -04:00
embeddedt
f374bcbb3a
Update to 1.21.3, fix tests 2024-10-24 10:46:53 -04:00
embeddedt
e0d24d4cdb
Update to 1.21.2-rc1 2024-10-17 11:08:57 -04:00
embeddedt
545fb386a0
Start rewriting dynamic resources 2024-10-12 11:50:43 -04:00
embeddedt
09ea5e1dc9
Runs, dynamic resources still broken 2024-10-10 14:01:39 -04:00
embeddedt
a841d20f8a
Compiles on 1.21.2-pre2, does not yet run 2024-10-10 12:06:24 -04:00
embeddedt
5463ccc3e6
Merge 1.20 into 1.21.1 2024-09-22 13:49:32 -04:00
embeddedt
dee2627df9
Trim LRU maps after dropping entries 2024-09-21 14:21:18 -04:00
embeddedt
d8e937720f
Always tick model manager even if not in world 2024-09-21 14:18:34 -04:00
embeddedt
9df79d8c8c
Skip filtering states when not in a world 2024-09-21 14:17:03 -04:00
embeddedt
1572bd705d
Merge remote-tracking branch 'origin/1.20' into 1.21.1 2024-08-24 16:34:59 -04:00
embeddedt
2abc4fa56b
Add type checking to dynamic maps to avoid ClassCastException from badly behaved mods 2024-08-19 18:26:58 -04:00
embeddedt
489136d048
Update to 1.21.1 2024-08-19 17:23:30 -04:00
embeddedt
820169667a
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-08-10 19:19:06 -04:00
embeddedt
512a7e237c
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-08-10 19:17:42 -04:00
embeddedt
7db5d6a1da
Make CTM work on 1.21 2024-07-27 14:39:30 -04:00
embeddedt
631ad0528b
Fix registry progress bar on 1.21 2024-07-27 13:33:08 -04:00
embeddedt
1daea1f5e3
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-07-26 20:04:05 -04:00
embeddedt
b2eb14b766
Bump Neo version to fix config screen crash 2024-07-19 20:24:44 -04:00
embeddedt
70fba2e0af
Improve dynamic model loading efficiency during model bake event 2024-07-19 19:44:13 -04:00
embeddedt
30e91f2056
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-07-19 19:21:23 -04:00
embeddedt
beccbef151
Bump Neo requirement 2024-07-12 18:45:27 -04:00
embeddedt
c3b17b2927
Update NF, remove config hacks as config system is rewritten 2024-07-11 08:00:59 -04:00
embeddedt
783627f4c5
Always convert identity unbaked missing model to identity baked missing model
Related: #433
2024-07-08 19:21:30 -04:00
embeddedt
d7bfeedc62
Merge 1.20 into 1.21 2024-07-07 09:14:46 -04:00
embeddedt
212f139bf1
Remove nonexistent JEI plugin from fabric mod file
Related: #428
2024-07-03 17:11:25 -04:00
embeddedt
85740f83af
Remove blast_search_trees 2024-06-28 20:20:06 -04:00
embeddedt
d46d24542f
Update NeoForge, adjust dynamic resources for NF API 2024-06-18 21:49:23 -04:00
embeddedt
700ccc25b7
Freeze model bakery later to allow for mutation in event 2024-06-14 19:19:52 -04:00
embeddedt
1b6075562c
Fix wrong location type being passed to model registry 2024-06-14 19:15:16 -04:00
embeddedt
2697a8f358
Fix lambda shadow remapping issue 2024-06-14 19:08:54 -04:00
embeddedt
f056fe4d0c
Fix item frames not having models with dynamic resources on
Related: #422
2024-06-14 18:58:00 -04:00
embeddedt
ef07197345
Fix deduplicate_location 2024-06-14 18:57:54 -04:00
embeddedt
180606eea1
Remove upgraded structure caching
Performance improvement is minimal with rewritten DFU and it
breaks structure generation on 1.21 for some reason
2024-06-13 21:37:34 -04:00
embeddedt
a6e0785252
Merge 1.20 into 1.21 2024-06-13 18:24:45 -04:00
embeddedt
4673293039
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-06-13 17:27:31 -04:00
embeddedt
f2379dc9e3
Update to 1.21 full release 2024-06-13 17:27:20 -04:00
embeddedt
d4cc7664f6
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-06-10 21:59:04 -04:00
embeddedt
1dce8d4ccf
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-06-10 21:45:23 -04:00
embeddedt
6fd3dde1a2
Fix compile error 2024-06-10 21:45:20 -04:00
embeddedt
f586522dfe
Do not drop models during initial load 2024-06-10 20:23:37 -04:00
embeddedt
168ab8effa
Merge remote-tracking branch 'origin/1.20' into 1.21 2024-06-10 20:20:14 -04:00
embeddedt
6e9dfaf0c6
Enable NeoForge project 2024-06-08 21:01:23 -04:00
embeddedt
9d584d13d2
Make blockstate model loader more resilient 2024-06-08 16:41:45 -04:00
embeddedt
a0f391e258
Update FAPI 2024-06-08 16:39:44 -04:00
embeddedt
8c51ccc022
1.21-pre3 2024-06-05 20:28:43 -04:00
embeddedt
390404d1d5
Merge remote-tracking branch 'origin/1.20.6' into 1.21 2024-06-05 20:23:31 -04:00
embeddedt
57af3c6491
Merge remote-tracking branch 'origin/1.20' into 1.20.6 2024-06-05 20:23:17 -04:00
embeddedt
99da801c3f
Merge 1.20.6 into 1.21 2024-06-04 19:41:27 -04:00
embeddedt
c292f0ddce
Merge 1.20 into 1.20.6 2024-06-04 19:41:09 -04:00
embeddedt
aa2a3817ba
Update NeoForge, work around buggy hasErrors method 2024-06-03 18:52:12 -04:00
embeddedt
4473a4ffb4
Merge 1.20.6 into 1.21 2024-06-01 13:38:18 -04:00
embeddedt
b4398565a6
Merge 1.20 into 1.20.6 2024-06-01 13:38:17 -04:00
embeddedt
9cfccb58fa
Merge 1.20.6 into 1.21 2024-05-30 19:06:42 -04:00
embeddedt
a9f4ce72f1
Merge 1.20 into 1.20.6 2024-05-30 19:06:41 -04:00
embeddedt
bc3af0450e
Fix access widener 2024-05-29 20:52:22 -04:00
embeddedt
d0598055c0
Do not load models for all of a block's blockstates at once 2024-05-29 20:47:59 -04:00
embeddedt
ab8810b7fe
1.21-pre1 - rewrite dynamic resources 2024-05-29 20:37:45 -04:00
embeddedt
2ef7d5fc85
24w21a 2024-05-29 20:37:45 -04:00
embeddedt
d8b207ff10
Merge 1.20.6 into 1.21 2024-05-29 16:04:39 -04:00
embeddedt
4e8427545f
Merge 1.20 into 1.20.6 2024-05-29 16:04:38 -04:00
embeddedt
f85d31d24d
Remove redundant patch 2024-05-19 19:36:39 -04:00
embeddedt
6a7b6abb23
Fix faster_item_rendering mixin 2024-05-17 12:40:07 -04:00
embeddedt
a443972d28
24w20a 2024-05-16 19:53:11 -04:00
embeddedt
b6865eff6c
Merge 1.20.6 into 1.21 2024-05-12 16:59:14 -04:00
embeddedt
cc653249e4
Merge 1.20 into 1.20.6 2024-05-12 16:59:13 -04:00
embeddedt
e75810db5b
Merge 1.20.6 into 1.21 2024-05-12 15:21:27 -04:00
embeddedt
a4359e1ad5
Remove structure location optimization as Mojang fixed it in 1.20.5 2024-05-12 15:21:02 -04:00
embeddedt
95a5a1b7b1
Merge 1.20.6 into 1.21 2024-05-11 22:15:26 -04:00
embeddedt
d846a077ac
Merge 1.20 into 1.20.6 2024-05-11 22:15:25 -04:00
embeddedt
2f98fadc9e
Spotless 2024-05-10 11:58:01 -04:00
embeddedt
de70fbafad
Use chunk status method that more accurately matches 1.20 2024-05-10 11:54:45 -04:00
embeddedt
a66a1dab9d
Fix overwrite lowering method access level 2024-05-10 11:49:49 -04:00
embeddedt
4d251e61ca
Fix tests 2024-05-10 11:47:22 -04:00
embeddedt
57d5f08b1d
24w19a 2024-05-10 11:39:58 -04:00
embeddedt
f13910a6ed
Update NeoForge 2024-05-07 10:39:52 -04:00
embeddedt
bebf3ccec2
Update for NeoForge changes 2024-04-29 15:12:45 -04:00
embeddedt
a5b1cbdc13
1.20.6 2024-04-29 14:52:10 -04:00
embeddedt
89592a2c42
Fix dynamic resources mixin on NeoForge 2024-04-25 19:39:27 -04:00
embeddedt
3f688148d0
Merge 1.20.4 into 1.20.5 2024-04-24 12:14:40 -04:00
embeddedt
c8f9b1cd84
Merge 1.20 into 1.20.4 2024-04-24 12:14:39 -04:00
embeddedt
7ee968f719
Spotless 2024-04-24 09:26:10 -04:00
embeddedt
2cce200e4d
Update to 1.20.5 2024-04-24 09:17:55 -04:00
embeddedt
2e9a6f27e0
1.20.5-rc2 2024-04-21 20:10:26 -04:00
embeddedt
730932b018
Merge 1.20.4 into 1.20.5 2024-04-15 21:22:26 -04:00
embeddedt
c6338f9736
Merge 1.20 into 1.20.4 2024-04-15 21:22:25 -04:00
embeddedt
0277d80ce7
Merge 1.20.4 into 1.20.5 2024-04-13 13:11:22 -04:00
embeddedt
b1c0b0f813
Merge 1.20 into 1.20.4 2024-04-13 13:11:22 -04:00
embeddedt
0476cdc35a
Fix option info screen rendering description behind blur 2024-04-12 09:44:02 -04:00
embeddedt
23bbf7b092
24w14a 2024-04-09 20:00:35 -04:00
embeddedt
0061e1de8e
Remove irrelevant mixin 2024-04-09 19:25:41 -04:00
embeddedt
159f27a8bf
Merge 1.20.4 into 1.20.5 2024-04-09 19:23:04 -04:00
embeddedt
c5dd43f2a1
Merge 1.20 into 1.20.4 2024-04-09 19:23:03 -04:00
embeddedt
da065350d0
Merge 1.20.4 into 1.20.5 2024-03-31 16:06:05 -04:00
embeddedt
9bf4faa4a5
Merge 1.20 into 1.20.4 2024-03-31 16:06:04 -04:00
embeddedt
7bd88b4f04
Merge 1.20.4 into 1.20.5 2024-03-31 09:27:12 -04:00
embeddedt
3bedde4ebe
Merge 1.20 into 1.20.4 2024-03-31 09:27:11 -04:00
embeddedt
2e1bed7cfb
Merge 1.20.4 into 1.20.5 2024-03-30 18:12:30 -04:00
embeddedt
73f706164f
Merge 1.20 into 1.20.4 2024-03-30 18:12:30 -04:00
embeddedt
5b5cbf9380
Merge 1.20.4 into 1.20.5 2024-03-30 17:46:26 -04:00
embeddedt
50556ab005
Update recipe book search tree for 1.20.2 changes 2024-03-30 17:45:49 -04:00
embeddedt
c3d78c3d09
Merge 1.20.4 into 1.20.5 2024-03-30 17:42:56 -04:00
embeddedt
ad07fb3f0c
Merge 1.20 into 1.20.4 2024-03-30 17:42:55 -04:00
embeddedt
a9efc62867
Merge 1.20.4 into 1.20.5 2024-03-30 15:22:47 -04:00
embeddedt
4b741da54d
Merge 1.20 into 1.20.4 2024-03-30 15:22:46 -04:00
embeddedt
60c2b02e37
Merge 1.20.4 into 1.20.5 2024-03-29 10:49:44 -04:00
embeddedt
042a7caa9e
Merge 1.20 into 1.20.4 2024-03-29 10:49:43 -04:00
embeddedt
02e4cb6cf1
24w13a 2024-03-28 10:42:44 -04:00
embeddedt
b2e3ae82eb
Remove optimization that is obsolete in snapshots 2024-03-28 10:39:03 -04:00
embeddedt
180f4eaf8d
Merge 1.20.4 into 1.20.5 2024-03-27 12:15:15 -04:00
embeddedt
95cd576c68
Merge 1.20 into 1.20.4 2024-03-27 12:15:14 -04:00
embeddedt
0ea4284846
Merge 1.20.4 into 1.20.5 2024-03-22 14:11:06 -04:00
embeddedt
8fc9c0b3c1
Merge 1.20 into 1.20.4 2024-03-22 14:11:05 -04:00
embeddedt
60fa5d4afe
Merge 1.20.4 into 1.20.5 2024-03-22 14:08:00 -04:00
embeddedt
95a8e4fe21
Merge 1.20 into 1.20.4 2024-03-22 14:07:59 -04:00
embeddedt
add7dd4609
Deholderize item mesher mixin 2024-03-21 14:14:35 -04:00
embeddedt
827550e8af
Update CTM integration for 1.20.4
Requires https://github.com/Chisel-Team/ConnectedTexturesMod/pull/229 to be built from source
2024-03-21 14:10:56 -04:00
embeddedt
9d7ef772a0
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2024-03-21 13:53:42 -04:00
embeddedt
a47a61a923
24w12a 2024-03-21 11:02:37 -04:00
embeddedt
5e20e25c4d
Loom 1.5 2024-03-20 10:00:02 -04:00
embeddedt
e6b28de740
Merge 1.20.4 into 1.20.5 2024-03-17 16:09:55 -04:00
embeddedt
61c2116946
Merge 1.20 into 1.20.4 2024-03-17 16:09:54 -04:00
embeddedt
72845d8952
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2024-03-17 16:09:32 -04:00
embeddedt
70eaabe756
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2024-03-17 15:53:53 -04:00
embeddedt
e22f5caec7
Fix registry progress bar going off end of screen in NeoForge 2024-03-17 15:25:58 -04:00
embeddedt
8a872fed27
Merge 1.20.4 into 1.20.5 2024-03-17 15:08:26 -04:00
embeddedt
0a2299ee33
Merge 1.20 into 1.20.4 2024-03-17 15:08:25 -04:00
embeddedt
826e5a4c20
Merge 1.20.4 into 1.20.5 2024-03-14 22:13:59 -04:00
embeddedt
a62c0635a5
Merge 1.20 into 1.20.4 2024-03-14 22:13:58 -04:00
embeddedt
4584457a79
Remove blur disabling patch
Mojang fixed this issue in 24w11a, the blur shader is not run for
pointless radii
2024-03-14 20:11:56 -04:00
embeddedt
8f663a0b07
24w11a 2024-03-14 20:10:21 -04:00
embeddedt
b65a63896b
Merge remote-tracking branch 'origin/1.20.4' into 1.20.5 2024-03-14 20:01:31 -04:00
embeddedt
fdc98b2600
Merge 1.20 into 1.20.4 2024-03-10 21:59:06 -04:00
embeddedt
8806dffaea
Merge 1.20 into 1.20.4 2024-03-08 17:51:07 -05:00
embeddedt
761703b4ab
Cast to correct event type 2024-03-02 21:35:50 -05:00
embeddedt
967d39997f
Remove buffer builder leak fix since you can now close them properly 2024-03-02 13:50:35 -05:00
embeddedt
48b492b906
Bump NeoForge 2024-03-02 13:17:19 -05:00
embeddedt
96db279d58
Merge Forge mixins that didn't automerge 2024-03-02 13:14:41 -05:00
embeddedt
8d2d3d8e15
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2024-03-02 13:06:52 -05:00
embeddedt
b82942fe75
Merge 1.20 into 1.20.4 2024-03-01 11:33:04 -05:00
embeddedt
e6ca5f633c
Merge 1.20 into 1.20.4 2024-02-28 20:10:53 -05:00
embeddedt
611dc4a266
Make it possible to properly disable blur 2024-02-28 19:45:16 -05:00
embeddedt
eacab89181
24w09a 2024-02-28 14:05:14 -05:00
embeddedt
139b967ce6
Merge 1.20.4 into 1.20.5 2024-02-26 16:40:49 -05:00
embeddedt
d52725012a
Merge 1.20 into 1.20.4 2024-02-26 16:40:48 -05:00
embeddedt
23e6091747
Merge 1.20.4 into 1.20.5 2024-02-26 16:32:52 -05:00
embeddedt
3f7af1c2e0
Merge 1.20 into 1.20.4 2024-02-26 16:32:51 -05:00
embeddedt
f38a06ecc1
Merge 1.20.4 into 1.20.5 2024-02-23 16:45:24 -05:00
embeddedt
c8485eaef8
Merge 1.20 into 1.20.4 2024-02-23 16:45:23 -05:00
embeddedt
fc86d0eee0 24w07a 2024-02-14 11:40:34 -05:00
embeddedt
6143d8a783 spotless 2024-02-07 15:54:09 -05:00
embeddedt
90cf39e9d8 24w06a 2024-02-07 15:49:00 -05:00
embeddedt
739b1663cd Merge remote-tracking branch 'origin/1.20.4' into 1.20.5 2024-02-07 15:37:37 -05:00
embeddedt
4dfba0cab4 Fix EntityIDSyncPacket being broken in multiplayer 2024-02-05 15:37:44 -05:00
embeddedt
46fc1e8539
Merge 1.20.4 into 1.20.5 2024-02-02 20:38:33 -05:00
embeddedt
2699fe448e
Merge 1.20 into 1.20.4 2024-02-02 20:38:32 -05:00
embeddedt
5c72a527ad 24w05a 2024-01-31 12:29:35 -05:00
embeddedt
25f13a9701 Merge 1.20.4 into 1.20.5 2024-01-30 15:48:16 -05:00
embeddedt
16ef9253e6 Merge 1.20 into 1.20.4 2024-01-30 15:47:48 -05:00
embeddedt
f25ecf337d Update NeoForge version 2024-01-30 15:34:19 -05:00
embeddedt
675ce1cd43 Merge 1.20.4 into 1.20.5 2024-01-29 15:31:11 -05:00
embeddedt
c4855e0b70 Merge 1.20 into 1.20.4 2024-01-29 15:31:10 -05:00
embeddedt
6c206c47e9
Merge 1.20.4 into 1.20.5 2024-01-27 20:23:31 -05:00
embeddedt
daa84565a6
Merge 1.20 into 1.20.4 2024-01-27 20:23:30 -05:00
embeddedt
8658784906
24w04a 2024-01-24 20:39:42 -05:00
embeddedt
d3758eb4f6 Fix mixin target 2024-01-17 14:41:12 -05:00
embeddedt
e9bfd965cd 24w03a 2024-01-17 13:16:17 -05:00
embeddedt
c2a2fd876b Merge 1.20.4 into 1.20.5 2024-01-12 15:49:17 -05:00
embeddedt
649c25d0d2 Merge 1.20 into 1.20.4 2024-01-12 15:49:15 -05:00
embeddedt
ad4bfc79ab Merge 1.20.4 into 1.20.5 2024-01-12 11:36:22 -05:00
embeddedt
a4bb17d2af Merge 1.20 into 1.20.4 2024-01-12 11:36:20 -05:00
embeddedt
b55ed00748
Merge 1.20.4 into 1.20.5 2024-01-11 08:02:54 -05:00
embeddedt
806fb7dcfe
Merge 1.20 into 1.20.4 2024-01-11 08:02:10 -05:00
embeddedt
b5becf6ba3
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2024-01-04 20:33:31 -05:00
embeddedt
c65fdbccc0
Spotless 2023-12-31 21:14:39 -05:00
embeddedt
dcee8b4169
Update to NeoForge 20.4.70-beta 2023-12-31 21:07:32 -05:00
embeddedt
d6fc939f41
Workaround AP throwing exception if there is a compile error in the file 2023-12-31 20:30:04 -05:00
embeddedt
a012b60ae3
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2023-12-31 20:22:33 -05:00
embeddedt
97a2c0e0f5
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2023-12-28 14:50:51 -05:00
Fury_Phoenix
34bc295b3c
Mark some more mixins as ClientOnlyMixin 2023-12-28 09:42:07 -05:00
embeddedt
e08531c228
Merge 1.20.4 into 1.20.5 2023-12-27 15:20:18 -05:00
embeddedt
456bca47b6
Merge 1.20 into 1.20.4 2023-12-27 15:20:17 -05:00
embeddedt
b25bb14d3d
Merge 1.20.4 into 1.20.5 2023-12-23 21:28:43 -05:00
embeddedt
d4fcc80db0
Merge 1.20 into 1.20.4 2023-12-23 21:28:43 -05:00
embeddedt
64ea7eab19
Merge 1.20.4 into 1.20.5 2023-12-23 19:07:06 -05:00
embeddedt
432b137edb
Merge 1.20 into 1.20.4 2023-12-23 19:07:05 -05:00
embeddedt
991ec339b2
Merge 1.20.4 into 1.20.5 2023-12-23 17:15:39 -05:00
embeddedt
1bf98ced86
Merge 1.20 into 1.20.4 2023-12-23 17:15:38 -05:00
embeddedt
7646bfa153
Merge 1.20.4 into 1.20.5 2023-12-23 15:57:42 -05:00
embeddedt
7be97e61e8
Merge 1.20 into 1.20.4 2023-12-23 15:57:41 -05:00
embeddedt
b23502c32b
Fix incorrect merge 2023-12-23 15:56:28 -05:00
embeddedt
d03571cb05
Merge 1.20 into 1.20.4 2023-12-23 15:55:42 -05:00
embeddedt
e1c91f13ac
Merge 1.20.4 into 1.20.5 2023-12-21 16:51:13 -05:00
embeddedt
324fb3af97
Merge 1.20 into 1.20.4 2023-12-21 16:51:12 -05:00
embeddedt
585fdb9e99
Merge 1.20.4 into 1.20.5 2023-12-21 13:41:57 -05:00
embeddedt
fa9d39bd31
Merge 1.20 into 1.20.4 2023-12-21 13:41:56 -05:00
embeddedt
501db5b84a
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2023-12-21 13:33:49 -05:00
embeddedt
8c46b4629d
23w51a 2023-12-18 10:25:10 -05:00
embeddedt
9dcc87b227
Merge 1.20.2 into 1.20.4 2023-12-14 21:12:00 -05:00
embeddedt
0eb70468a1
Merge 1.20 into 1.20.2 2023-12-14 21:11:59 -05:00
embeddedt
ba2b740075
Merge remote-tracking branch 'origin/1.20' into 1.20.4 2023-12-14 20:46:02 -05:00
embeddedt
063289faac
Update NeoForge version target 2023-12-13 09:33:31 -05:00
embeddedt
d3ed56a1c1
Merge 1.20.3 into 1.20.4 2023-12-11 11:01:53 -05:00
embeddedt
45a7f1a63e
Merge 1.20.2 into 1.20.3 2023-12-11 11:01:52 -05:00
embeddedt
04aac43db0
Merge 1.20 into 1.20.2 2023-12-11 11:01:51 -05:00
embeddedt
0ea5139315
Merge remote-tracking branch 'origin/1.20.2' into 1.20.4 2023-12-11 10:16:48 -05:00
embeddedt
06bb9f9545
Update Minecraft & Fabric Loader; stop including Mixin Extras 2023-12-11 10:13:23 -05:00
embeddedt
adf169c3fa
Merge 1.20.2 into 1.20.3 2023-12-11 09:56:50 -05:00
embeddedt
8d5e66218e
Merge 1.20 into 1.20.2 2023-12-11 09:56:49 -05:00
embeddedt
45b734dead
Merge remote-tracking branch 'origin/1.20.2' into 1.20.3 2023-12-09 13:51:28 -05:00
embeddedt
2ec1000ae8
Merge remote-tracking branch 'origin/1.20' into 1.20.2 2023-12-09 13:51:22 -05:00
embeddedt
1f66c7becd
Merge 1.20.2 into 1.20.3 2023-12-06 20:44:42 -05:00
embeddedt
ccc21d76d8
Merge 1.20 into 1.20.2 2023-12-06 20:44:41 -05:00
embeddedt
4fec9f53c3 Fix publishing to CF 2023-12-05 18:12:48 -05:00
embeddedt
b8cd5adbb1 Update Fabric Loader to fix tests 2023-12-05 18:09:10 -05:00
embeddedt
bf69e58169 Merge remote-tracking branch 'origin/neoforge/1.20.2' into 1.20.3 2023-12-05 18:01:12 -05:00
embeddedt
6592ac5cac Update to 1.20.3 2023-12-05 18:00:07 -05:00
embeddedt
1f447b689f
Add back Forge item model shaper mixin 2023-12-03 20:41:07 -05:00
embeddedt
8df6fab0e7
Fix AT not being added to production jar 2023-12-03 20:37:53 -05:00
embeddedt
328507cea3
Fix blockstate ID reassignment being broken 2023-12-03 20:35:26 -05:00
embeddedt
c6b38f340a
Second rename pass, reaches main menu 2023-12-03 20:33:08 -05:00
embeddedt
69a9aa76da
Compiles, not yet running 2023-12-03 20:20:33 -05:00
embeddedt
87ee1017ae
Fix mistake in build script 2023-12-03 19:44:49 -05:00
embeddedt
fcf21283d8
Switch build target to NeoForge 2023-12-03 19:44:05 -05:00
embeddedt
bfdd1f913d
Merge 1.20 into 1.20.2 2023-12-03 19:28:56 -05:00
embeddedt
3187c80d48
Merge 1.20.2 into 1.20.3 2023-12-03 19:28:56 -05:00
embeddedt
f594ec6c5b Update test mod 2023-11-30 13:51:28 -05:00
embeddedt
efa46c3842 Merge 1.20.2 into 1.20.3 2023-11-30 13:42:36 -05:00
embeddedt
473597a915 Fix config screen background 2023-11-30 13:42:22 -05:00
embeddedt
84061b197f 1.20.3-rc1 2023-11-30 13:38:30 -05:00
embeddedt
dc86d3e137 23w45a 2023-11-30 13:18:08 -05:00
embeddedt
4d111cb381
Merge 1.20.2 into 1.20.3 2023-11-27 07:43:53 -05:00
embeddedt
29d1f88539
Merge 1.20 into 1.20.2 2023-11-27 07:43:52 -05:00
embeddedt
0ff032bd1a
Merge 1.20.2 into 1.20.3 2023-11-27 07:43:20 -05:00
embeddedt
e34e9fcf8b
Merge branch 'propagations/1.20' into propagations/1.20.2 2023-11-27 07:43:15 -05:00
embeddedt
9861926f1d Merge 1.20.2 into 1.20.3 2023-11-23 10:12:21 -05:00
embeddedt
acb6809459 Merge 1.20 into 1.20.2 2023-11-23 10:12:20 -05:00
embeddedt
fd975388ca Merge 1.20.2 into 1.20.3 2023-11-23 09:51:42 -05:00
embeddedt
13ee6b3523 Merge 1.20 into 1.20.2 2023-11-23 09:51:14 -05:00
embeddedt
af8e23f41a Fix mixin target 2023-11-21 12:38:02 -05:00
embeddedt
19ba30280c Update Loom 2023-11-21 12:37:30 -05:00
embeddedt
33609d234c Completely remove BitSet trimming 2023-11-21 12:27:02 -05:00
TonimatasDEV
6e3134161a
Forge 1.20.2 (#301) 2023-11-21 12:17:11 -05:00
embeddedt
27142d2c42
Merge 1.20.2 into 1.20.3 2023-11-11 17:26:28 -05:00
embeddedt
c672948d8c
Merge 1.20 into 1.20.2 2023-11-11 17:26:27 -05:00
embeddedt
0a418bc55d Merge 1.20.2 into 1.20.3 2023-11-07 17:27:30 -05:00
embeddedt
d8263c55f8 Merge 1.20 into 1.20.2 2023-11-07 17:27:28 -05:00
embeddedt
a212a3fc7e
Merge 1.20.2 into 1.20.3 2023-11-05 21:59:34 -05:00
embeddedt
7af609b56f
Merge 1.20 into 1.20.2 2023-11-05 21:59:33 -05:00
embeddedt
45c11ea7e0
Merge 1.20.2 into 1.20.3 2023-11-05 21:37:18 -05:00
embeddedt
894173cf1d
Remove unused import 2023-11-05 21:35:15 -05:00
embeddedt
fc52570951
Update cache_profile_texture_url for 1.20.2 2023-11-05 21:32:27 -05:00
embeddedt
207521f778
Merge 1.20.2 into 1.20.3 2023-11-05 21:25:23 -05:00
embeddedt
9efe912090
Merge 1.20 into 1.20.2 2023-11-05 21:25:22 -05:00
embeddedt
15d16ffa9a
Merge 1.20.2 into 1.20.3 2023-11-04 09:02:17 -04:00
embeddedt
95f2dc95d3
Merge 1.20 into 1.20.2 2023-11-04 09:02:16 -04:00
embeddedt
86c6e90436 Support closed flag on BufferBuilders
Thanks to Moulberry for noting this vanilla change
2023-11-02 13:35:25 -04:00
embeddedt
915de8187e 23w44a 2023-11-02 13:34:15 -04:00
embeddedt
181be3cf80 Merge 1.20.2 into 1.20.3 2023-10-30 14:54:27 -04:00
embeddedt
79751dac21 Merge 1.20 into 1.20.2 2023-10-30 14:54:21 -04:00
embeddedt
184dec0739
Merge 1.20.2 into 1.20.3 2023-10-29 21:17:26 -04:00
embeddedt
1339fc7af6
Merge 1.20 into 1.20.2 2023-10-29 21:17:26 -04:00
embeddedt
624a4483eb
Merge 1.20.2 into 1.20.3 2023-10-29 11:44:32 -04:00
embeddedt
3b7d2f1e2e
Merge 1.20 into 1.20.2 2023-10-29 11:44:31 -04:00
embeddedt
91a084f860
Merge 1.20.2 into 1.20.3 2023-10-29 11:41:44 -04:00
embeddedt
546a362d9c
Merge 1.20 into 1.20.2 2023-10-29 11:41:44 -04:00
embeddedt
87b984bfe9
Merge 1.20.2 into 1.20.3 2023-10-29 11:37:50 -04:00
embeddedt
7a2b71381c
Merge 1.20 into 1.20.2 2023-10-29 11:37:50 -04:00
embeddedt
850fdcbf1b
Merge 1.20.2 into 1.20.3 2023-10-28 20:06:04 -04:00
embeddedt
3add97202e
Merge 1.20 into 1.20.2 2023-10-28 20:06:03 -04:00
embeddedt
5d481334a8
Merge 1.20.2 into 1.20.3 2023-10-28 09:54:26 -04:00
embeddedt
6d096e8ae0
Merge 1.20 into 1.20.2 2023-10-28 09:54:26 -04:00
embeddedt
c09b530a8c Merge 1.20.2 into 1.20.3 2023-10-25 17:18:47 -04:00
embeddedt
fbacc85f86 Merge 1.20 into 1.20.2 2023-10-25 17:18:46 -04:00
embeddedt
0e9142e68f Merge 1.20.2 into 1.20.3 2023-10-25 15:00:21 -04:00
embeddedt
f6f7badde8 Merge 1.20 into 1.20.2 2023-10-25 15:00:20 -04:00
embeddedt
2c5b664c68 Merge 1.20.2 into 1.20.3 2023-10-25 14:24:19 -04:00
embeddedt
2a099d8537 Merge 1.20 into 1.20.2 2023-10-25 14:24:18 -04:00
embeddedt
472ed42f22 23w43a 2023-10-25 14:11:52 -04:00
embeddedt
9830d4d6f7 Merge 1.20.2 into 1.20.3 2023-10-25 14:04:07 -04:00
embeddedt
348a2bcf3d Merge 1.20 into 1.20.2 2023-10-25 14:04:06 -04:00
embeddedt
43da9ffcdc Merge 1.20.2 into 1.20.3 2023-10-24 12:45:44 -04:00
embeddedt
7f80f38862 Merge 1.20 into 1.20.2 2023-10-24 12:45:41 -04:00
embeddedt
231c3f41e5 23w42a 2023-10-18 14:45:34 -04:00
embeddedt
cc54b6d67d
Merge 1.20 into 1.20.2 2023-10-13 10:47:53 -04:00
embeddedt
e8fa483917
Merge 1.20 into 1.20.2 2023-10-10 17:40:31 -04:00
embeddedt
dc3cb998b0
Merge 1.20 into 1.20.2 2023-10-09 08:37:43 -04:00
embeddedt
972121fa50 Merge 1.20 into 1.20.2 2023-10-07 18:47:12 -04:00
embeddedt
93554d1854 Merge 1.20 into 1.20.2 2023-10-07 17:48:39 -04:00
embeddedt
a00efe62c7 Merge 1.20 into 1.20.2 2023-10-07 17:46:25 -04:00
embeddedt
1267a25db1 Merge 1.20 into 1.20.2 2023-10-06 17:07:49 -04:00
embeddedt
0149a113a8 Merge 1.20 into 1.20.2 2023-10-05 14:12:36 -04:00
embeddedt
97d4f6bbc2 Merge 1.20 into 1.20.2 2023-10-03 13:45:38 -04:00
embeddedt
dfb0c52fa3 Merge 1.20 into 1.20.2 2023-09-27 17:58:53 -04:00
embeddedt
02fa2be42b
Merge 1.20 into 1.20.2 2023-09-24 12:50:16 -04:00
embeddedt
45c29216a8
Spotless 2023-09-24 12:36:16 -04:00
embeddedt
5e8e7649e5
Merge 1.20 into 1.20.2 2023-09-24 12:34:00 -04:00
embeddedt
0a2cda0814
Disable recursive fluidstate test on 1.20.2 for now 2023-09-24 12:28:58 -04:00
embeddedt
5952fa2178
Merge 1.20 into 1.20.2 2023-09-24 12:19:14 -04:00
embeddedt
9bf0017aeb
1.20.2 2023-09-24 12:15:55 -04:00
embeddedt
cb286c0bca
1.20.2-pre3 2023-09-13 22:01:13 -04:00
embeddedt
b56a65c192
Merge remote-tracking branch 'origin/1.20' into dev/1.20.2 2023-09-05 11:21:20 -04:00
embeddedt
8d1058cc3f
23w33a 2023-08-17 11:55:44 -04:00
embeddedt
eb15718023
Merge remote-tracking branch 'origin/1.20' into dev/1.20.2 2023-08-17 11:30:19 -04:00
embeddedt
861474d635
23w32a 2023-08-09 12:07:35 -04:00
embeddedt
f3bda91ebf
Update Fabric API, fixes tests 2023-08-09 11:46:39 -04:00
embeddedt
84c72f8da8
Merge branch '1.20' into dev/1.20.2 2023-08-09 11:35:59 -04:00
embeddedt
3716912a53
Merge remote-tracking branch 'origin/1.20' into dev/1.20.2 2023-08-03 12:21:14 -04:00
embeddedt
42688757a7
Merge remote-tracking branch 'origin/1.20' into dev/1.20.2 2023-08-03 12:07:48 -04:00
embeddedt
23b473f85d
Move tags update mixin to ClientCommonPacketListenerImpl 2023-08-02 20:08:30 -04:00
embeddedt
7fcaf716d8
23w31a 2023-08-02 20:01:34 -04:00
215 changed files with 1882 additions and 7129 deletions

View File

@ -166,6 +166,7 @@ public class ClientMixinValidator {
clzsses = wrappedClzss.stream()
.map(AnnotationValue::getValue)
.filter(o -> o instanceof TypeMirror)
.map(TypeMirror.class::cast)
.collect(Collectors.toSet());

View File

@ -25,7 +25,7 @@ public record MixinConfig(
InjectorOptions injectors, OverwriteOptions overwrites
) {
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_17",
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_21",
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
}
public record InjectorOptions(int defaultRequire) {

View File

@ -35,7 +35,7 @@ def versionString = "${baseVersion}${preMarker}+mc${minecraft_version}${commitHa
version = versionString
archivesBaseName = rootProject.archives_base_name + '-' + project.name
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21
repositories {
exclusiveContent {

View File

@ -16,7 +16,7 @@ dependencies {
mappings loom.layered() {
officialMojangMappings()
if(rootProject.hasProperty("parchment_version")) {
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
parchment("org.parchmentmc.data:parchment-${parchment_mc_version}:${parchment_version}@zip")
}
}
implementation project(":annotations")

View File

@ -43,7 +43,7 @@ curseforge {
changelog = file("${rootDir}/CHANGELOG.md")
changelogType = "markdown"
releaseType = isBeta ? "beta" : "release"
addGameVersion project.name.capitalize()
addGameVersion (project.name.equals("neoforge") ? "NeoForge" : project.name.capitalize())
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
mainArtifact remapJar
}
@ -66,4 +66,4 @@ tasks.modrinth.dependsOn(rootProject.generateChangelog)
tasks.register('publishToModSites') {
publishToModSites.dependsOn(tasks.modrinth)
publishToModSites.dependsOn(tasks.curseforge)
}
}

View File

@ -6,8 +6,6 @@ architectury {
common(rootProject.enabled_platforms.split(","))
}
ext.jei_minecraft_version = "1.20.1" /* temporary, till 1.20 releases */
dependencies {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader
@ -20,18 +18,10 @@ dependencies {
modApi("dev.latvian.mods:rhino:${rhino_version}") {
transitive = false
}
modApi("me.shedaniel:RoughlyEnoughItems-api:${rei_version}") {
transitive = false
}
modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") {
transitive = false
}
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
// compile against the JEI API but do not include it at runtime
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-common:${jei_version}")
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-gui:${jei_version}")
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-lib:${jei_version}")
modCompileOnly fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
}

View File

@ -1,6 +1,7 @@
package org.embeddedt.modernfix;
import net.minecraft.SharedConstants;
import net.minecraft.TracingExecutor;
import net.minecraft.Util;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkMap;
@ -14,7 +15,6 @@ import org.embeddedt.modernfix.resources.ReloadExecutor;
import org.embeddedt.modernfix.util.ClassInfoManager;
import java.lang.management.ManagementFactory;
import java.util.concurrent.ExecutorService;
// The value here should match an entry in the META-INF/mods.toml file
public class ModernFix {
@ -31,7 +31,7 @@ public class ModernFix {
// Used to skip computing the blockstate caches twice
public static boolean runningFirstInjection = false;
private static ExecutorService resourceReloadService = null;
private static TracingExecutor resourceReloadService = null;
static {
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dedicated_reload_executor.ReloadExecutor")) {
@ -41,7 +41,7 @@ public class ModernFix {
}
}
public static ExecutorService resourceReloadExecutor() {
public static TracingExecutor resourceReloadExecutor() {
return resourceReloadService;
}

View File

@ -1,27 +1,19 @@
package org.embeddedt.modernfix;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.MemoryReserve;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
import org.embeddedt.modernfix.searchtree.REIBackedSearchTree;
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
import org.embeddedt.modernfix.util.ClassInfoManager;
import org.embeddedt.modernfix.world.IntegratedWatchdog;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ModernFixClient {
@ -47,8 +39,6 @@ public class ModernFixClient {
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
}
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
for(String className : ModernFixPlatformHooks.INSTANCE.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
try {
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
@ -123,85 +113,6 @@ public class ModernFixClient {
}
}
/**
* Horrendous hack to allow tracking every synced entity data manager.
*
* This is to ensure we can perform ID fixup on already constructed managers.
*/
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
private static final Field entriesArrayField;
static {
Field field;
try {
field = SynchedEntityData.class.getDeclaredField("entriesArray");
field.setAccessible(true);
} catch(ReflectiveOperationException e) {
field = null;
}
entriesArrayField = field;
}
/**
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
*
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
*/
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
boolean fixNeeded = false;
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
Class<? extends Entity> eClass = entry.getKey();
for(Pair<String, Integer> field : entry.getValue()) {
String fieldName = field.getFirst();
int newId = field.getSecond();
try {
Field f = eClass.getDeclaredField(fieldName);
f.setAccessible(true);
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
fixNeeded = true;
} catch(NoSuchFieldException e) {
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Unexpected exception", e);
}
}
}
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
List<SynchedEntityData> dataEntries;
synchronized (allEntityDatas) {
if(fixNeeded) {
dataEntries = new ArrayList<>(allEntityDatas);
for(SynchedEntityData manager : dataEntries) {
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
for(SynchedEntityData.DataItem<?> item : items) {
fixedMap.put(item.getAccessor().id, item);
}
manager.lock.writeLock().lock();
try {
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
if(entriesArrayField != null) {
try {
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
for(int i = 0; i < dataArray.length; i++) {
dataArray[i] = fixedMap.get(i);
}
entriesArrayField.set(manager, dataArray);
} catch(ReflectiveOperationException e) {
ModernFix.LOGGER.error(e);
}
}
} finally {
manager.lock.writeLock().unlock();
}
}
}
allEntityDatas.clear();
}
}
public void onServerStarted(MinecraftServer server) {
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
return;

View File

@ -1,11 +1,10 @@
package org.embeddedt.modernfix.api.entrypoint;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.UnbakedModel;
/**
* Implement this interface in a mod class and add it to "modernfix:integration_v1" in your mod metadata file
@ -21,49 +20,10 @@ public interface ModernFixClientIntegration {
default void onDynamicResourcesStatusChange(boolean enabled) {
}
/**
* Called to allow mods to observe the loading of an unbaked model and either make changes to it or wrap it with their
* own instance.
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
* @param originalModel the original model
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
* with dynamic resources on
* @return the model which should actually be loaded for this resource location
*/
default UnbakedModel onUnbakedModelLoad(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) {
return originalModel;
}
/**
* Called to allow mods to observe the use of an unbaked model at bake time and either make changes to it or wrap it with their
* own instance.
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
* @param originalModel the original model
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
* with dynamic resources on
* @return the model which should actually be loaded for this resource location
*/
default UnbakedModel onUnbakedModelPreBake(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) {
return originalModel;
}
/**
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
* own instance.
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
* @param originalModel the original model
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
* with dynamic resources on
* @return the model which should actually be loaded for this resource location
*/
@Deprecated
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
return originalModel;
}
/**
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
* own instance.
*
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
* @param originalModel the original model
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
@ -71,7 +31,7 @@ public interface ModernFixClientIntegration {
* @param textureGetter function to retrieve textures for this model
* @return the model which should actually be loaded for this resource location
*/
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery, Function<Material, TextureAtlasSprite> textureGetter) {
return onBakedModelLoad(location, baseModel, originalModel, state, bakery);
default BakedModel onBakedModelLoad(ModelResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery, ModelBakery.TextureGetter textureGetter) {
return originalModel;
}
}

View File

@ -7,10 +7,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
import org.embeddedt.modernfix.util.DynamicMap;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
@ -25,7 +23,7 @@ public final class ModelHelpers {
* @return a list of all blockstates related to the model
*/
public static ImmutableList<BlockState> getBlockStateForLocation(ModelResourceLocation location) {
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(location.getNamespace(), location.getPath()));
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(location.id());
if(blockOpt.isPresent())
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), location);
else
@ -49,7 +47,7 @@ public final class ModelHelpers {
* @return a fake map of the top-level models
*/
public static Map<ResourceLocation, BakedModel> createFakeTopLevelMap(BiFunction<ResourceLocation, ModelState, BakedModel> modelGetter) {
return new DynamicMap<>(location -> modelGetter.apply(location, BlockModelRotation.X0_Y0));
return new DynamicMap<>(ResourceLocation.class, location -> modelGetter.apply(location, BlockModelRotation.X0_Y0));
}
/**
@ -58,6 +56,8 @@ public final class ModelHelpers {
* @return an appropriate ModelBaker
*/
public static ModelBaker adaptBakery(ModelBakery bakery) {
throw new UnsupportedOperationException("TODO");
/*
return new ModelBaker() {
@Override
public UnbakedModel getModel(ResourceLocation resourceLocation) {
@ -70,5 +70,7 @@ public final class ModelHelpers {
return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState);
}
};
*/
}
}

View File

@ -7,7 +7,7 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.jetbrains.annotations.Nullable;
@ -32,24 +32,19 @@ public class SafeBlockGetter implements BlockGetter {
if(!(access instanceof ChunkAccess))
return null;
ChunkAccess chunk = (ChunkAccess)access;
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
if(!chunk.getPersistedStatus().isOrAfter(ChunkStatus.FULL))
return null;
return chunk;
}
@Override
public int getMaxBuildHeight() {
return this.wrapped.getMaxBuildHeight();
public int getMaxY() {
return this.wrapped.getMaxY();
}
@Override
public int getMaxLightLevel() {
return this.wrapped.getMaxLightLevel();
}
@Override
public int getMinBuildHeight() {
return this.wrapped.getMinBuildHeight();
public int getMinY() {
return this.wrapped.getMinY();
}
@Override

View File

@ -2,58 +2,9 @@ package org.embeddedt.modernfix.command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.structure.CachingStructureManager;
import java.io.InputStream;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static net.minecraft.commands.Commands.literal;
public class ModernFixCommands {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(literal("modernfix")
.then(literal("upgradeStructures")
.requires(source -> source.hasPermission(3))
.executes(context -> {
ServerLevel level = context.getSource().getLevel();
if(level == null) {
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
return 0;
}
ResourceManager manager = level.getServer().resources.resourceManager();
Map<ResourceLocation, Resource> structures = manager.listResources("structures", p -> p.getPath().endsWith(".nbt"));
int upgradedNum = 0;
Pattern pathPattern = Pattern.compile("^structures/(.*)\\.nbt$");
for(Map.Entry<ResourceLocation, Resource> entry : structures.entrySet()) {
upgradedNum++;
ResourceLocation found = entry.getKey();
Matcher matcher = pathPattern.matcher(found.getPath());
if(!matcher.matches())
continue;
ResourceLocation structureLocation = new ResourceLocation(found.getNamespace(), matcher.group(1));
try(InputStream resource = entry.getValue().open()) {
CachingStructureManager.readStructureTag(structureLocation, level.getServer().getFixerUpper(), resource);
Component msg = Component.literal("checked " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")");
context.getSource().sendSuccess(() -> msg, false);
} catch(Throwable e) {
ModernFix.LOGGER.error("Couldn't upgrade structure " + found, e);
context.getSource().sendFailure(Component.literal("error reading " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")"));
}
}
context.getSource().sendSuccess(() -> Component.literal("All structures upgraded"), false);
return 1;
}))
);
}
}

View File

@ -1,56 +0,0 @@
package org.embeddedt.modernfix.common.mixin.bugfix.buffer_builder_leak;
import com.mojang.blaze3d.vertex.BufferBuilder;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.UnsafeBufferHelper;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.nio.ByteBuffer;
@Mixin(value = BufferBuilder.class, priority = 1500)
@ClientOnlyMixin
public class BufferBuilderMixin {
@Shadow private ByteBuffer buffer;
private static boolean leakReported = false;
private boolean mfix$shouldFree = true;
@Dynamic
@Inject(method = "flywheel$injectForRender", at = @At("RETURN"), remap = false, require = 0)
private void preventFree(CallbackInfo ci) {
mfix$shouldFree = false;
}
/**
* Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log.
*/
@Inject(method = "<clinit>", at = @At(value = "RETURN"))
private static void initUnsafeBufferHelper(CallbackInfo ci) {
UnsafeBufferHelper.init();
}
@Override
protected void finalize() throws Throwable {
try {
ByteBuffer buf = buffer;
// can be null if a mod already tried to free the buffer
if(buf != null && mfix$shouldFree) {
if(!leakReported) {
leakReported = true;
ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this.");
}
UnsafeBufferHelper.free(buf);
buffer = null;
}
} finally {
super.finalize();
}
}
}

View File

@ -1,22 +0,0 @@
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
public class BlockStateBaseMixin {
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
private BlockGetter useSafeGetter(BlockGetter g) {
if(g instanceof ISafeBlockGetter) {
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
if(replacement.shouldUse())
return replacement;
}
return g;
}
}

View File

@ -1,24 +0,0 @@
package org.embeddedt.modernfix.common.mixin.bugfix.ender_dragon_leak;
import net.minecraft.client.renderer.entity.EnderDragonRenderer;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(EnderDragonRenderer.class)
@ClientOnlyMixin
public abstract class EnderDragonRendererMixin {
@Shadow @Final private EnderDragonRenderer.DragonModel model;
/**
* Prevent leaking the client world through the entity reference.
*/
@Inject(method = "render(Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At("RETURN"))
private void clearDragonEntityReference(CallbackInfo ci) {
this.model.entity = null;
}
}

View File

@ -1,75 +0,0 @@
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
import com.mojang.datafixers.util.Either;
import net.minecraft.server.level.*;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@Mixin(ChunkMap.class)
public abstract class ChunkMapMixin {
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
@Shadow @Final private ChunkMap.DistanceManager distanceManager;
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder arg);
@Shadow @Final private ServerLevel level;
@Shadow @Final private ThreadedLevelLightEngine lightEngine;
@Shadow @Final private ChunkProgressListener progressListener;
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
@Shadow @Final private StructureTemplateManager structureTemplateManager;
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
@ModifyArg(method = "prepareAccessibleChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
private Executor useMainThreadExecutor(Executor executor) {
return this.mainThreadExecutor;
}
/**
* @author embeddedt
* @reason revert 1.17 chunk system changes, significantly reduces time and RAM needed to load chunks
*/
@Inject(method = "schedule", at = @At("HEAD"), cancellable = true)
private void useLegacySchedulingLogic(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> cir) {
if(requiredStatus != ChunkStatus.EMPTY && !requiredStatus.hasLoadDependencies()) {
ChunkPos chunkpos = holder.getPos();
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = holder.getOrScheduleFuture(requiredStatus.getParent(), (ChunkMap)(Object)this);
cir.setReturnValue(future.thenComposeAsync((either) -> {
Optional<ChunkAccess> optional = either.left();
if (requiredStatus == ChunkStatus.LIGHT) {
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
}
// from original method
if (optional.isPresent() && optional.get().getStatus().isOrAfter(requiredStatus)) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureTemplateManager, this.lightEngine, (arg2) -> {
return this.protoChunkToFullChunk(holder);
}, (ChunkAccess)optional.get());
this.progressListener.onStatusChange(chunkpos, requiredStatus);
return completablefuture;
} else {
return this.scheduleChunkGeneration(holder, requiredStatus);
}
}, this.mainThreadExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainThreadExecutor));
}
}
}

View File

@ -24,7 +24,7 @@ public class MinecraftMixin {
/**
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
*/
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;Z)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
private void clearLevelDataForLeaks(CallbackInfo ci) {
if(this.level != null) {
try {

View File

@ -1,7 +1,7 @@
package org.embeddedt.modernfix.fabric.mixin.core;
package org.embeddedt.modernfix.common.mixin.core;
import net.minecraft.client.multiplayer.ClientPacketListener;
import org.embeddedt.modernfix.ModernFixClientFabric;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -13,11 +13,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class ClientPlayNetHandlerMixin {
@Inject(method = "handleUpdateRecipes", at = @At("RETURN"))
private void signalRecipes(CallbackInfo ci) {
ModernFixClientFabric.commonMod.onRecipesUpdated();
}
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
private void signalTags(CallbackInfo ci) {
ModernFixClientFabric.commonMod.onTagsUpdated();
ModernFixClient.INSTANCE.onRecipesUpdated();
}
}

View File

@ -1,26 +0,0 @@
package org.embeddedt.modernfix.common.mixin.core;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(SynchedEntityData.class)
@ClientOnlyMixin
public class SynchedEntityDataMixin {
/**
* Store this in our set of all entity data objects.
*
* Not an ideal solution, but it should guarantee compatibility with mods.
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void storeInSet(Entity arg, CallbackInfo ci) {
synchronized (ModernFixClient.allEntityDatas) {
ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this);
}
}
}

View File

@ -1,27 +1,27 @@
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.TracingExecutor;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Mixin(ChunkRenderDispatcher.class)
@Mixin(SectionRenderDispatcher.class)
@ClientOnlyMixin
public class ChunkRenderDispatcherMixin {
private static final Executor MFIX_CHUNK_BUILD_EXECUTOR = new ThreadPoolExecutor(1, computeNumThreads(), 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
private static final TracingExecutor MFIX_CHUNK_BUILD_EXECUTOR = new TracingExecutor(new ThreadPoolExecutor(1,computeNumThreads(), 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()));
private static int computeNumThreads() {
return Math.max(Math.min(Runtime.getRuntime().availableProcessors() / 4, 10), 1);
}
@ModifyVariable(method = "<init>*", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private static Executor replaceExecutor(Executor old) {
private static TracingExecutor replaceExecutor(TracingExecutor old) {
return MFIX_CHUNK_BUILD_EXECUTOR;
}
}

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
import net.minecraft.TracingExecutor;
import net.minecraft.Util;
import org.embeddedt.modernfix.util.DirectExecutorService;
import org.spongepowered.asm.mixin.Final;
@ -7,10 +8,8 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import java.util.concurrent.ExecutorService;
@Mixin(Util.class)
public class UtilMixin {
@Shadow @Final @Mutable
private static final ExecutorService BACKGROUND_EXECUTOR = new DirectExecutorService();
private static final TracingExecutor BACKGROUND_EXECUTOR = new TracingExecutor(new DirectExecutorService());
}

View File

@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class CrashReportMixin {
@Shadow @Final private Throwable exception;
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V"))
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
new Exception("ModernFix crash stacktrace").printStackTrace();
if(this.exception != null)

View File

@ -1,16 +1,15 @@
package org.embeddedt.modernfix.common.mixin.feature.stalled_chunk_load_detection;
import com.mojang.datafixers.util.Either;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.EmptyLevelChunk;
import org.embeddedt.modernfix.ModernFix;
import org.spongepowered.asm.mixin.Final;
@ -26,7 +25,7 @@ public abstract class ServerChunkCacheMixin {
@Shadow @Final private Thread mainThread;
@Shadow @Final public ServerLevel level;
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
@Shadow protected abstract CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
@Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor;
private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading");
@ -38,27 +37,27 @@ public abstract class ServerChunkCacheMixin {
if(debugDeadServerAccess) {
new Exception().printStackTrace();
}
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS);
Holder<Biome> plains = this.level.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), plains));
} else if(Thread.currentThread() != this.mainThread) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
CompletableFuture<ChunkResult<ChunkAccess>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
if(!future.isDone()) {
// Wait at least 500 milliseconds before printing anything
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> resultingChunk = null;
ChunkResult<ChunkAccess> resultingChunk = null;
try {
resultingChunk = future.get(500, TimeUnit.MILLISECONDS);
} catch(InterruptedException | ExecutionException | TimeoutException ignored) {
}
if(resultingChunk != null && resultingChunk.left().isPresent()) {
cir.setReturnValue(resultingChunk.left().get());
if(resultingChunk != null && resultingChunk.isSuccess()) {
cir.setReturnValue(resultingChunk.orElse(null));
return;
}
if(debugDeadServerAccess)
ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception());
try {
resultingChunk = future.get(10, TimeUnit.SECONDS);
if(resultingChunk.left().isPresent()) {
cir.setReturnValue(resultingChunk.left().get());
if(resultingChunk.isSuccess()) {
cir.setReturnValue(resultingChunk.orElse(null));
return;
}
} catch(InterruptedException | ExecutionException | TimeoutException e) {

View File

@ -1,52 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.blast_search_trees;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.searchtree.RecipeBookSearchTree;
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(Minecraft.class)
@ClientOnlyMixin
public abstract class MinecraftMixin {
@Shadow @Final private SearchRegistry searchRegistry;
@Shadow public abstract <T> void populateSearchTree(SearchRegistry.Key<T> key, List<T> list);
@Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true)
private void replaceSearchTrees(CallbackInfo ci) {
SearchTreeProviderRegistry.Provider provider = SearchTreeProviderRegistry.getSearchTreeProvider();
if(provider == null)
return;
ModernFix.LOGGER.info("Replacing search trees with '{}' provider", provider.getName());
SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier = list -> provider.getSearchTree(false);
SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier = list -> provider.getSearchTree(true);
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, nameSupplier);
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, tagSupplier);
this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new RecipeBookSearchTree(provider.getSearchTree(false), list));
ModernFixPlatformHooks.INSTANCE.registerCreativeSearchTrees(this.searchRegistry, nameSupplier, tagSupplier, this::populateSearchTree);
// grab components for all key mappings in order to prevent them from being loaded off-thread later
// this populates the LazyLoadedValues
// we also need to suppress GLFW errors to prevent crashes if a key is missing
GLFWErrorCallback oldCb = GLFW.glfwSetErrorCallback(null);
for(KeyMapping mapping : KeyMapping.ALL.values()) {
mapping.getTranslatedKeyMessage();
}
GLFW.glfwSetErrorCallback(oldCb);
ci.cancel();
}
}

View File

@ -1,28 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Collection;
@Mixin(MultiPart.class)
@ClientOnlyMixin
public class MultipartMixin {
private Collection<ResourceLocation> dependencyCache = null;
@Inject(method = "getDependencies", at = @At("HEAD"), cancellable = true)
private void useDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
if(dependencyCache != null)
cir.setReturnValue(dependencyCache);
}
@Inject(method = "getDependencies", at = @At("RETURN"))
private void storeDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
if(dependencyCache == null)
dependencyCache = cir.getReturnValue();
}
}

View File

@ -3,7 +3,6 @@ package org.embeddedt.modernfix.common.mixin.perf.cache_profile_texture_url;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import net.minecraft.client.resources.SkinManager;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@ -13,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@Mixin(SkinManager.class)
@Mixin(targets = {"net/minecraft/client/resources/SkinManager$TextureCache" })
@ClientOnlyMixin
public class SkinManagerMixin {
@Unique
@ -22,7 +21,7 @@ public class SkinManagerMixin {
.concurrencyLevel(1)
.build();
@Redirect(method = "registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)Lnet/minecraft/resources/ResourceLocation;",
@Redirect(method = { "getOrLoad", "registerTexture" },
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
private String useCachedHash(MinecraftProfileTexture texture) {
// avoid lambda allocation for common case

View File

@ -5,7 +5,6 @@ import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.dimension.DimensionType;
@ -22,12 +21,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Supplier;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin extends Level implements IServerLevel {
protected ServerLevelMixin(WritableLevelData arg, ResourceKey<Level> arg2, RegistryAccess arg3, Holder<DimensionType> arg4, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
super(arg, arg2, arg3, arg4, supplier, bl, bl2, l, i);
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, boolean bl, boolean bl2, long l, int i) {
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
}
@Shadow public abstract DimensionDataStorage getDataStorage();
@ -48,8 +46,8 @@ public abstract class ServerLevelMixin extends Level implements IServerLevel {
*/
@Inject(method = "<init>", at = @At("TAIL"))
private void ensureGeneration(CallbackInfo ci) {
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(StrongholdLocationCache::load,
StrongholdLocationCache::new,
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(
StrongholdLocationCache.factory((ServerLevel)(Object)this),
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
}

View File

@ -1,46 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.cache_upgraded_structures;
import com.mojang.datafixers.DataFixer;
import net.minecraft.core.HolderGetter;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.structure.CachingStructureManager;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
@Mixin(StructureTemplateManager.class)
public class StructureManagerMixin {
@Shadow @Final private DataFixer fixerUpper;
@Shadow private ResourceManager resourceManager;
@Shadow @Final private HolderGetter<Block> blockLookup;
/**
* @author embeddedt
* @reason use our own manager to avoid needless DFU updates
*/
@Overwrite
private Optional<StructureTemplate> loadFromResource(ResourceLocation id) {
ResourceLocation arg = new ResourceLocation(id.getNamespace(), "structures/" + id.getPath() + ".nbt");
try(InputStream stream = this.resourceManager.open(arg)) {
return Optional.of(CachingStructureManager.readStructure(id, this.fixerUpper, stream, this.blockLookup));
} catch(FileNotFoundException e) {
return Optional.empty();
} catch(IOException e) {
ModernFix.LOGGER.error("Can't read structure", e);
return Optional.empty();
}
}
}

View File

@ -1,9 +1,7 @@
package org.embeddedt.modernfix.common.mixin.perf.chunk_meshing;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.renderer.chunk.RenderChunkRegion;
import net.minecraft.client.renderer.chunk.SectionCompiler;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.annotation.RequiresMod;
import org.embeddedt.modernfix.util.blockpos.SectionBlockPosIterator;
@ -11,7 +9,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(targets = { "net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask"}, priority = 2000)
@Mixin(value = SectionCompiler.class, priority = 2000)
@ClientOnlyMixin
@RequiresMod("!fluidlogged")
public class RebuildTaskMixin {
@ -23,13 +21,4 @@ public class RebuildTaskMixin {
private Iterable<BlockPos> fastBetweenClosed(BlockPos firstPos, BlockPos secondPos) {
return () -> new SectionBlockPosIterator(firstPos);
}
/**
* @author embeddedt
* @reason RenderChunkRegion.getBlockState is expensive, avoid calling it multiple times for the same position
*/
@Redirect(method = "compile", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/RenderChunkRegion;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;", ordinal = 1), require = 0)
private BlockState useExistingBlockState(RenderChunkRegion instance, BlockPos pos, @Local(ordinal = 0) BlockState state) {
return state;
}
}

View File

@ -1,7 +1,8 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.resources.ResourceKey;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.registry.LifecycleMap;
import org.spongepowered.asm.mixin.Final;
@ -20,10 +21,10 @@ public abstract class MappedRegistryMixin<T> {
@Shadow
@Final
@Mutable
private Map<T, Lifecycle> lifecycles;
private Map<ResourceKey<T>, RegistrationInfo> registrationInfos;
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceStorage(CallbackInfo ci) {
this.lifecycles = new LifecycleMap<>();
this.registrationInfos = new LifecycleMap<>();
}
}

View File

@ -17,7 +17,7 @@ public class CreateWorldScreenMixin {
return ModernFix.resourceReloadExecutor();
}
@ModifyArg(method = "openFresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader;load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 3)
@ModifyArg(method = "openCreateWorldScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader;load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 3)
private static Executor getCreationExecutorService(Executor e) {
return ModernFix.resourceReloadExecutor();
}

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.common.mixin.perf.dedicated_reload_executor;
import net.minecraft.TracingExecutor;
import net.minecraft.client.Minecraft;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
@ -7,13 +8,11 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.concurrent.ExecutorService;
@Mixin(Minecraft.class)
@ClientOnlyMixin
public class MinecraftMixin {
@Redirect(method = { "<init>", "reloadResourcePacks(Z)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
private ExecutorService getResourceReloadExecutor() {
@Redirect(method = { "<init>", "reloadResourcePacks(ZLnet/minecraft/client/Minecraft$GameLoadCookie;)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Lnet/minecraft/TracingExecutor;", ordinal = 0))
private TracingExecutor getResourceReloadExecutor() {
return ModernFix.resourceReloadExecutor();
}
}

View File

@ -10,7 +10,7 @@ import java.util.concurrent.Executor;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin {
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/RegistryAccess$Frozen;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 5)
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/LayeredRegistryAccess;Ljava/util/List;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 6)
private Executor getReloadExecutor(Executor asyncExecutor) {
return ModernFix.resourceReloadExecutor();
}

View File

@ -15,15 +15,15 @@ public class MixinResourceLocation {
@Mutable
@Shadow
@Final
protected String namespace;
private String namespace;
@Mutable
@Shadow
@Final
protected String path;
private String path;
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
private void reinit(String[] id, CallbackInfo ci) {
@Inject(method = "<init>", at = @At("RETURN"))
private void reinit(String string, String string2, CallbackInfo ci) {
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
this.path = IdentifierCaches.PATH.deduplicate(this.path);
}

View File

@ -25,7 +25,7 @@ import java.util.Map;
*/
@Mixin(WallBlock.class)
public abstract class WallBlockMixin extends Block {
private static Map<ImmutableList<Float>, Pair<Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>>> CACHE_BY_SHAPE_VALS = new HashMap<>();
private static Map<ImmutableList<Float>, Pair<Map<Map<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>>> CACHE_BY_SHAPE_VALS = new HashMap<>();
public WallBlockMixin(Properties properties) {
super(properties);
@ -34,7 +34,7 @@ public abstract class WallBlockMixin extends Block {
@Inject(method = "makeShapes", at = @At("HEAD"), cancellable = true)
private synchronized void useCachedShapeMap(float f1, float f2, float f3, float f4, float f5, float f6, CallbackInfoReturnable<Map<BlockState, VoxelShape>> cir) {
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
Pair<Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>> cache = CACHE_BY_SHAPE_VALS.get(key);
Pair<Map<Map<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>> cache = CACHE_BY_SHAPE_VALS.get(key);
// require the properties to be identical
if(cache == null || !cache.getSecond().getProperties().equals(this.stateDefinition.getProperties()))
return;
@ -55,7 +55,7 @@ public abstract class WallBlockMixin extends Block {
return;
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
if(!CACHE_BY_SHAPE_VALS.containsKey(key)) {
Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape> cacheByProperties = new HashMap<>();
Map<Map<Property<?>, Comparable<?>>, VoxelShape> cacheByProperties = new HashMap<>();
Map<BlockState, VoxelShape> shapeMap = cir.getReturnValue();
for(Map.Entry<BlockState, VoxelShape> entry : shapeMap.entrySet()) {
cacheByProperties.put(entry.getKey().getValues(), entry.getValue());

View File

@ -1,19 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.types.Type;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
*/
@Mixin(BlockEntityType.class)
public class BlockEntityTypeMixin {
@Redirect(method = "register", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
private static Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
return null;
}
}

View File

@ -1,33 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import net.minecraft.util.datafix.DataFixers;
import org.embeddedt.modernfix.dfu.LazyDataFixer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Set;
@Mixin(DataFixers.class)
public abstract class DataFixersMixin {
@Shadow protected static DataFixer createFixerUpper(Set<DSL.TypeReference> set) {
throw new AssertionError();
}
private static LazyDataFixer lazyDataFixer;
/**
* Avoid classloading the DFU logic until we actually need it.
*/
@Inject(method = "createFixerUpper", at = @At("HEAD"), cancellable = true)
private static void createLazyFixerUpper(Set<DSL.TypeReference> set, CallbackInfoReturnable<DataFixer> cir) {
if(lazyDataFixer == null) {
lazyDataFixer = new LazyDataFixer(() -> createFixerUpper(set));
cir.setReturnValue(lazyDataFixer);
}
}
}

View File

@ -1,19 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.types.Type;
import net.minecraft.world.entity.EntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
*/
@Mixin(EntityType.Builder.class)
public class EntityTypeBuilderMixin {
@Redirect(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
private Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
return null;
}
}

View File

@ -19,19 +19,19 @@ import java.util.Map;
@Mixin(EntityRenderDispatcher.class)
@ClientOnlyMixin
public class EntityRenderDispatcherMixin {
@Shadow private Map<EntityType<?>, EntityRenderer<?>> renderers;
@Shadow private Map<EntityType<?>, EntityRenderer<?, ?>> renderers;
private EntityRendererMap mfix$dynamicRenderers;
@Inject(method = "getRenderer", at = @At("RETURN"), cancellable = true)
private <T extends Entity> void checkNullness(T entity, CallbackInfoReturnable<EntityRenderer<? super T>> cir) {
private <T extends Entity> void checkNullness(T entity, CallbackInfoReturnable<EntityRenderer<? super T, ?>> cir) {
// apparently some mods yeet the renderers map and cause issues
if(cir.getReturnValue() == null)
cir.setReturnValue((EntityRenderer<? super T>)mfix$dynamicRenderers.get(entity.getType()));
cir.setReturnValue((EntityRenderer<? super T, ?>)mfix$dynamicRenderers.get(entity.getType()));
}
@Redirect(method = "onResourceManagerReload", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/entity/EntityRenderDispatcher;renderers:Ljava/util/Map;"))
private void setRendererField(EntityRenderDispatcher instance, Map<EntityType<?>, EntityRenderer<?>> incomingMap) {
private void setRendererField(EntityRenderDispatcher instance, Map<EntityType<?>, EntityRenderer<?, ?>> incomingMap) {
this.renderers = incomingMap;
this.mfix$dynamicRenderers = (EntityRendererMap)incomingMap;
}

View File

@ -22,7 +22,7 @@ public class EntityRenderersMixin {
@Shadow @Final private static Map<EntityType<?>, EntityRendererProvider<?>> PROVIDERS;
@Inject(method = "createEntityRenderers", at = @At("HEAD"), cancellable = true)
private static void createDynamicRendererLoader(EntityRendererProvider.Context context, CallbackInfoReturnable<Map<EntityType<?>, EntityRenderer<?>>> cir) {
private static void createDynamicRendererLoader(EntityRendererProvider.Context context, CallbackInfoReturnable<Map<EntityType<?>, EntityRenderer<?, ?>>> cir) {
cir.setReturnValue(new EntityRendererMap(PROVIDERS, context));
ModernFix.LOGGER.info("Dynamic entity renderer hook setup");
}

View File

@ -1,23 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import net.minecraft.client.renderer.block.model.BlockElementFace;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.UVController;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.lang.reflect.Type;
@Mixin(BlockElementFace.Deserializer.class)
@ClientOnlyMixin
public class BlockElementFaceDeserializerMixin {
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false))
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
}
}

View File

@ -29,7 +29,7 @@ public class BlockModelShaperMixin {
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
private void replaceModelMap(CallbackInfo ci) {
// replace the backing map for mods which will access it
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
this.modelByStateCache = new DynamicOverridableMap<>(BlockState.class, state -> modelManager.getModel(ModelLocationCache.get(state)));
// Clear the cached models on blockstate objects
for(Block block : BuiltInRegistries.BLOCK) {
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
@ -72,3 +72,4 @@ public class BlockModelShaperMixin {
}
}
}

View File

@ -24,3 +24,4 @@ public class BlockStateBaseMixin implements IModelHoldingBlockState {
mfix$model = model != null ? new SoftReference<>(model) : null;
}
}

View File

@ -1,91 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
import java.util.Map;
@Mixin(ItemModelShaper.class)
@ClientOnlyMixin
public abstract class ItemModelShaperMixin {
@Shadow public abstract ModelManager getModelManager();
@Shadow @Final @Mutable private Int2ObjectMap<BakedModel> shapesCache;
private Map<Item, ModelResourceLocation> overrideLocationsVanilla;
public ItemModelShaperMixin() {
super();
}
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel");
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceLocationMap(CallbackInfo ci) {
overrideLocationsVanilla = new HashMap<>();
this.shapesCache = new DynamicInt2ObjectMap<>(index -> getModelManager().getModel(ModelLocationCache.get(Item.byId(index))));
}
@Unique
private ModelResourceLocation mfix$getLocation(Item item) {
ModelResourceLocation map = overrideLocationsVanilla.getOrDefault(item, SENTINEL_VANILLA);
if(map == SENTINEL_VANILLA) {
/* generate the appropriate location from our cache */
map = ModelLocationCache.get(item);
}
return map;
}
private BakedModel mfix$getModelForItem(Item item) {
ModelResourceLocation map = mfix$getLocation(item);
return map == null ? null : getModelManager().getModel(map);
}
/**
* @author embeddedt
* @reason Get the stored location for that item and meta, and get the model
* from that location from the model manager.
**/
@Overwrite
public BakedModel getItemModel(Item item) {
return this.mfix$itemModelCache.get(item);
}
/**
* @author embeddedt
* @reason Don't get all models during init (with dynamic loading, that would
* generate them all). Just store location instead.
**/
@Overwrite
public void register(Item item, ModelResourceLocation location) {
overrideLocationsVanilla.put(item, location);
}
/**
* @author embeddedt
* @reason Disable cache rebuilding (with dynamic loading, that would generate
* all models).
**/
@Overwrite
public void rebuildCache() {
this.mfix$itemModelCache.clear();
}
}

View File

@ -1,26 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(ItemOverrides.class)
@ClientOnlyMixin
public class ItemOverridesMixin {
@WrapOperation(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;getModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;"))
private UnbakedModel preventThrowForMissing(ModelBaker instance, ResourceLocation resourceLocation, Operation<UnbakedModel> original) {
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
try {
return original.call(instance, resourceLocation);
} finally {
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
}
}
}

View File

@ -1,22 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ItemRenderer.class)
@ClientOnlyMixin
public class ItemRendererMixin {
/**
* Don't waste space putting all these locations into the cache, compute them on demand later.
*/
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemModelShaper;register(Lnet/minecraft/world/item/Item;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
private void skipDefaultRegistration(ItemModelShaper shaper, Item item, ModelResourceLocation mrl) {
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.resources.model.ModelDiscovery;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ModelDiscovery.class)
@ClientOnlyMixin
public class ModelDiscoveryMixin {
/**
* @author embeddedt
* @reason We will show the warning ourselves later when loading the model dynamically, this is just spam since
* the models don't exist during early loading
*/
@Redirect(method = "loadBlockModel", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V"))
private void disableMissingModelWarning(Logger instance, String s, Object o) {
}
}

View File

@ -1,85 +1,98 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.resources.model.AtlasSet;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.ClientItemInfoLoader;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import org.embeddedt.modernfix.ModernFix;
import org.apache.commons.lang3.ArrayUtils;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.util.CacheUtil;
import org.embeddedt.modernfix.util.LambdaMap;
import org.embeddedt.modernfix.dynamicresources.DynamicModelProvider;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@Mixin(ModelManager.class)
@ClientOnlyMixin
public class ModelManagerMixin {
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
public class ModelManagerMixin implements DynamicModelProvider.ModelManagerExtension {
@Shadow private Map<ModelResourceLocation, BakedModel> bakedBlockStateModels;
@Shadow private Map<ResourceLocation, ItemModel> bakedItemStackModels;
@Shadow private Map<ResourceLocation, ClientItem.Properties> itemProperties;
@Inject(method = "<init>", at = @At("RETURN"))
private void injectDummyBakedRegistry(CallbackInfo ci) {
if(this.bakedRegistry == null) {
this.bakedRegistry = new HashMap<>();
}
}
@Unique
private DynamicModelProvider mfix$modelProvider;
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
var cache = CacheUtil.<ResourceLocation, BlockModel>simpleCacheForLambda(location -> loadSingleBlockModel(manager, location), 100L);
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
return CompletableFuture.completedFuture(Map.of());
}
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<Map<ResourceLocation, List<ModelBakery.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
var cache = CacheUtil.<ResourceLocation, List<ModelBakery.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadBlockStates(Lnet/minecraft/client/resources/model/UnbakedModel;Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<BlockStateModelLoader.LoadedModels> deferBlockStateLoad(UnbakedModel unbakedModel, ResourceManager resourceManager, Executor executor) {
return CompletableFuture.completedFuture(new BlockStateModelLoader.LoadedModels(Map.of()));
}
@Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
private ImmutableList<BlockState> skipCollection(StateDefinition<Block, BlockState> definition) {
return ImmutableList.of();
/**
* @author embeddedt
* @reason disable map creation
*/
@Overwrite
private static Map<BlockState, BakedModel> createBlockStateToModelDispatch(Map<ModelResourceLocation, BakedModel> map, BakedModel bakedModel) {
return Map.of();
}
private BlockModel loadSingleBlockModel(ResourceManager manager, ResourceLocation location) {
return manager.getResource(location).map(resource -> {
try (BufferedReader reader = resource.openAsReader()) {
return BlockModel.fromStream(reader);
} catch(IOException e) {
ModernFix.LOGGER.error("Couldn't load model", e);
return null;
}
}).orElse(null);
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ClientItemInfoLoader;scheduleLoad(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<ClientItemInfoLoader.LoadedClientInfos> disableClientItemEarlyLoad(ResourceManager resourceManager, Executor executor) {
return CompletableFuture.completedFuture(new ClientItemInfoLoader.LoadedClientInfos(Map.of()));
}
private List<ModelBakery.LoadedJson> loadSingleBlockState(ResourceManager manager, ResourceLocation location) {
return manager.getResourceStack(location).stream().map(resource -> {
try (BufferedReader reader = resource.openAsReader()) {
return new ModelBakery.LoadedJson(resource.sourcePackId(), GsonHelper.parse(reader));
} catch(IOException e) {
ModernFix.LOGGER.error("Couldn't load blockstate", e);
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
@ModifyArg(method = "reload", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;allOf([Ljava/util/concurrent/CompletableFuture;)Ljava/util/concurrent/CompletableFuture;", ordinal = 1))
private CompletableFuture<?>[] createModelProvider(CompletableFuture<?>[] cfs, @Local(ordinal = 0) CompletableFuture<EntityModelSet> entityModelFuture, @Local(ordinal = 0, argsOnly = true) Executor executor, @Local(ordinal = 0) Map<ResourceLocation, CompletableFuture<AtlasSet.StitchResult>> atlasPreparations) {
CompletableFuture<Void> makeModelProviderFuture = CompletableFuture.supplyAsync(() -> {
return Map.copyOf(Maps.transformValues(atlasPreparations, CompletableFuture::join));
}, executor).thenAcceptBoth(entityModelFuture, (stitchResults, entityModelSet) -> {
this.mfix$modelProvider = new DynamicModelProvider(
Minecraft.getInstance().getResourceManager(),
entityModelSet,
stitchResults
);
DynamicModelProvider.currentReloadingModelProvider = new WeakReference<>(this.mfix$modelProvider);
});
return ArrayUtils.add(cfs, makeModelProviderFuture);
}
@Inject(method = "apply", at = @At("RETURN"))
private void setModelRegistries(CallbackInfo ci) {
this.bakedBlockStateModels = this.mfix$modelProvider.getTopLevelEmulatedRegistry();
this.bakedItemStackModels = this.mfix$modelProvider.getItemModelEmulatedRegistry();
this.itemProperties = this.mfix$modelProvider.getItemPropertiesEmulatedRegistry();
}
@Override
public DynamicModelProvider mfix$getModelProvider() {
return this.mfix$modelProvider;
}
}

View File

@ -11,12 +11,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
@ClientOnlyMixin
public class GameRendererMixin {
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.BEFORE))
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(Lnet/minecraft/client/DeltaTracker;)V", shift = At.Shift.BEFORE))
private void markRenderingLevel(CallbackInfo ci) {
RenderState.IS_RENDERING_LEVEL = true;
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.AFTER))
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(Lnet/minecraft/client/DeltaTracker;)V", shift = At.Shift.AFTER))
private void markNotRenderingLevel(CallbackInfo ci) {
RenderState.IS_RENDERING_LEVEL = false;
}

View File

@ -0,0 +1,14 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.world.item.ItemDisplayContext;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ItemStackRenderState.class)
@ClientOnlyMixin
public interface ItemStackRenderStateAccessor {
@Accessor
ItemDisplayContext getDisplayContext();
}

View File

@ -1,36 +1,30 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.SimpleBakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.FastItemRenderType;
import org.embeddedt.modernfix.render.RenderState;
import org.embeddedt.modernfix.render.SimpleItemModelView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = ItemRenderer.class, priority = 600)
@Mixin(value = ItemStackRenderState.LayerRenderState.class, priority = 600)
@ClientOnlyMixin
public abstract class ItemRendererMixin {
private ItemDisplayContext transformType;
private final SimpleItemModelView modelView = new SimpleItemModelView();
public abstract class LayerRenderStateMixin {
@Shadow(aliases = {"this$0"}) @Final private ItemStackRenderState field_55345;
@Inject(method = "render", at = @At("HEAD"))
private void markRenderingType(ItemStack itemStack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) {
this.transformType = transformType;
}
@Shadow abstract ItemTransform transform();
@Unique
private final SimpleItemModelView modelView = new SimpleItemModelView();
/**
* If a model
@ -40,20 +34,24 @@ public abstract class ItemRendererMixin {
* we do not need to go through the process of rendering every quad. Just render the south ones (the ones facing the
* camera).
*/
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V"), index = 0)
private BakedModel useSimpleWrappedItemModel(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, @Local(ordinal = 0) BakedModel originalModel) {
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderItem(Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;II[ILnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/client/renderer/RenderType;Lnet/minecraft/client/renderer/item/ItemStackRenderState$FoilType;)V"), index = 6)
private BakedModel useSimpleWrappedItemModel(BakedModel model) {
var transformType = ((ItemStackRenderStateAccessor)this.field_55345).getDisplayContext();
// Forge composite models split themselves into a smaller simple model, we need to detect that the parent
// was not simple
// TODO 1.21.4 - I don't think that is needed anymore with the changes to item rendering
/*
if(originalModel != null && originalModel.getClass() != SimpleBakedModel.class) {
return model;
}
*/
if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
if(!RenderState.IS_RENDERING_LEVEL && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
FastItemRenderType type;
ItemTransform transform = model.getTransforms().gui;
ItemTransform transform = this.transform();
if(transform == ItemTransform.NO_TRANSFORM)
type = FastItemRenderType.SIMPLE_ITEM;
else if(stack.getItem() instanceof BlockItem && isBlockTransforms(transform))
else if(model.isGui3d() && isBlockTransforms(transform))
type = FastItemRenderType.SIMPLE_BLOCK;
else
return model;

View File

@ -1,22 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_structure_location;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.levelgen.structure.StructureCheck;
import org.embeddedt.modernfix.duck.IStructureCheck;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(ServerLevel.class)
public class ServerLevelMixin {
@Shadow @Final private ServerChunkCache chunkSource;
@ModifyExpressionValue(method = "<init>", at = @At(value = "NEW", target = "(Lnet/minecraft/world/level/chunk/storage/ChunkScanAccess;Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/LevelHeightAccessor;Lnet/minecraft/world/level/biome/BiomeSource;JLcom/mojang/datafixers/DataFixer;)Lnet/minecraft/world/level/levelgen/structure/StructureCheck;", ordinal = 0))
private StructureCheck attachGeneratorState(StructureCheck check) {
((IStructureCheck)check).mfix$setStructureState(this.chunkSource.getGeneratorState());
return check;
}
}

View File

@ -1,51 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_structure_location;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.core.Registry;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureCheck;
import net.minecraft.world.level.levelgen.structure.StructureCheckResult;
import org.embeddedt.modernfix.duck.IStructureCheck;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(StructureCheck.class)
public class StructureCheckMixin implements IStructureCheck {
@Shadow @Final private Registry<Structure> structureConfigs;
private ChunkGeneratorStructureState mfix$structureState;
@Override
public void mfix$setStructureState(ChunkGeneratorStructureState state) {
mfix$structureState = state;
}
/**
* @author embeddedt (inspired by 24w04a and Bytzo's comment on https://bugs.mojang.com/browse/MC-249136)
* @reason Avoid running the canCreateStructure method (which can be expensive) if the structure placement already
* forbids placing the structure in this chunk.
*/
@ModifyExpressionValue(method = "checkStart", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureCheck;tryLoadFromStorage(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/levelgen/structure/Structure;ZJ)Lnet/minecraft/world/level/levelgen/structure/StructureCheckResult;"))
private StructureCheckResult mfix$checkForValidPosition(StructureCheckResult storageResult, ChunkPos chunkPos, Structure structure, boolean skipKnownStructures) {
if (storageResult != null) {
return storageResult;
} else if(mfix$structureState != null) {
// Check if any of the placements allow for this structure to be in this chunk
var structureHolder = this.structureConfigs.wrapAsHolder(structure);
for (var placement : mfix$structureState.getPlacementsForStructure(structureHolder)) {
if (placement.isStructureChunk(mfix$structureState, chunkPos.x, chunkPos.z)) {
// Allowed - return null so regular check runs
return null;
}
}
// Not allowed - early exit by returning a non-null value
return StructureCheckResult.START_NOT_PRESENT;
} else {
return null;
}
}
}

View File

@ -17,7 +17,7 @@ public class BlockableEventLoopMixin {
* @reason yielding the thread is pretty pointless if we're about to park anyway
*/
@Overwrite
protected void waitForTasks() {
public void waitForTasks() {
LockSupport.parkNanos("waiting for tasks", MFIX$TICK_WAIT_TIME);
}
}

View File

@ -1,19 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.model_optimizations;
import com.google.common.collect.ImmutableSet;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BooleanProperty.class)
public class BooleanPropertyMixin {
/**
* There is no point comparing the immutable sets in any two instances of this class, as they will always be
* the same.
*/
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z", remap = false), remap = false)
private boolean skipEqualityCheck(ImmutableSet instance, Object object) {
return true;
}
}

View File

@ -1,36 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import net.minecraft.core.MappedRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(MappedRegistry.class)
public class MappedRegistryMixin {
/**
* Avoid copying the ID list to a slightly larger one every time an entry is added to the registry.
* The original behavior causes O(n) time complexity for registration.
*/
@Redirect(
method = "registerMapping(ILnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;)Lnet/minecraft/core/Holder$Reference;",
at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectList;size(I)V", remap = false)
)
private void setSizeSmart(ObjectList<?> list, int size) {
if(list instanceof ObjectArrayList && size > list.size()) {
int requestedSize = size;
/* choose next power of two, or this value if it is a power of two */
int p2Size = Integer.highestOneBit(size);
if(p2Size != size)
size = p2Size << 1;
// grow backing array to power-of-two size, this will return instantly in most cases
((ObjectArrayList<?>)list).ensureCapacity(size);
// write null entries to fill size, to match the behavior of list.size(int)
while(list.size() < requestedSize)
list.add(null);
} else {
list.size(size);
}
}
}

View File

@ -1,9 +1,6 @@
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
import com.google.common.collect.ArrayTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.embeddedt.modernfix.annotation.RequiresMod;
@ -13,6 +10,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
/**
* Minor mixin to avoid duplicate empty neighbor tables, used when FerriteCore is not present. Won't be enabled in 99% of
* modded environments but is useful for testing in dev without dragging in Fabric API.
@ -20,12 +19,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(StateHolder.class)
@RequiresMod("!ferritecore")
public class StateHolderMixin {
@Shadow private Table<Property<?>, Comparable<?>, ?> neighbours;
private static final Reference2ObjectArrayMap<Property<?>, ?> EMPTY_NEIGHBOURS = new Reference2ObjectArrayMap<>();
@Shadow private Map<Property<?>, ?> neighbours;
/* optimize the case where block has no properties */
@Inject(method = "populateNeighbours", at = @At("RETURN"), require = 0)
private void replaceEmptyTable(CallbackInfo ci) {
if((this.neighbours instanceof ArrayTable || this.neighbours instanceof HashBasedTable) && this.neighbours.isEmpty())
this.neighbours = ImmutableTable.of();
if (this.neighbours.isEmpty()) {
this.neighbours = EMPTY_NEIGHBOURS;
}
}
}

View File

@ -10,8 +10,8 @@ import java.util.Map;
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
public class CompoundTag1Mixin {
@ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
private Map<String, Tag> modifyMap(Map<String, Tag> map) {
@ModifyVariable(method = "loadCompound", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
private static Map<String, Tag> modifyMap(Map<String, Tag> map) {
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
if(map != null)
newMap.putAll(map);

View File

@ -1,129 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.MapCodec;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.embeddedt.modernfix.duck.IBlockState;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockBehaviour.BlockStateBase.class)
public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements IBlockState {
protected BlockStateBaseMixin(Block object, ImmutableMap<Property<?>, Comparable<?>> immutableMap, MapCodec<BlockState> mapCodec) {
super(object, immutableMap, mapCodec);
}
private static final FluidState MFIX$VANILLA_DEFAULT_FLUID = Fluids.EMPTY.defaultFluidState();
@Shadow public abstract void initCache();
@Shadow private BlockBehaviour.BlockStateBase.Cache cache;
@Shadow private FluidState fluidState;
@Shadow private boolean isRandomlyTicking;
@Shadow @Deprecated private boolean legacySolid;
@Shadow protected abstract BlockState asState();
private volatile boolean cacheInvalid = false;
private static boolean buildingCache = false;
@Override
public void clearCache() {
cacheInvalid = true;
}
@Override
public boolean isCacheInvalid() {
return cacheInvalid;
}
private void mfix$generateCache() {
if(cacheInvalid) {
// Ensure that only one block's cache is built at a time
synchronized (BlockBehaviour.BlockStateBase.class) {
if(cacheInvalid) {
// Ensure that if we end up in here recursively, we just use the original cache
if(!buildingCache) {
buildingCache = true;
try {
this.initCache();
cacheInvalid = false;
} finally {
buildingCache = false;
}
}
}
}
}
}
@Redirect(method = "*", at = @At(
value = "FIELD",
opcode = Opcodes.GETFIELD,
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;cache:Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache;",
ordinal = 0
))
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
mfix$generateCache();
return this.cache;
}
@Redirect(method = "*", at = @At(
value = "FIELD",
opcode = Opcodes.GETFIELD,
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;fluidState:Lnet/minecraft/world/level/material/FluidState;",
ordinal = 0
), require = 0)
private FluidState genCacheBeforeGettingFluid(BlockBehaviour.BlockStateBase base) {
// don't generate the full cache here as mods will iterate for the fluid state a lot
// assume blockstates will not change their contained fluidstate at runtime more than once
// this is how Lithium's implementation used to work, so it should be fine
if(this.cacheInvalid && this.fluidState == MFIX$VANILLA_DEFAULT_FLUID) {
synchronized (BlockBehaviour.BlockStateBase.class) {
if(!buildingCache) {
buildingCache = true;
try {
this.fluidState = this.owner.getFluidState(this.asState());
} finally {
buildingCache = false;
}
}
}
}
return this.fluidState;
}
@Redirect(method = "*", at = @At(
value = "FIELD",
opcode = Opcodes.GETFIELD,
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isRandomlyTicking:Z",
ordinal = 0
))
private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base) {
if(this.cacheInvalid)
return this.owner.isRandomlyTicking(this.asState());
return this.isRandomlyTicking;
}
@Redirect(method = "*", at = @At(
value = "FIELD",
opcode = Opcodes.GETFIELD,
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;legacySolid:Z",
ordinal = 0
))
private boolean genCacheBeforeCheckingSolid(BlockBehaviour.BlockStateBase base) {
mfix$generateCache();
return this.legacySolid;
}
}

View File

@ -1,28 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.blockstate.BlockStateCacheHandler;
import org.embeddedt.modernfix.duck.IBlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.function.Consumer;
@Mixin(value = Blocks.class, priority = 1100)
public class BlocksMixin {
@ModifyArg(method = "rebuildCache", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/IdMapper;forEach(Ljava/util/function/Consumer;)V"), index = 0)
private static Consumer getEmptyConsumer(Consumer original) {
BlockStateCacheHandler.rebuildParallel(true);
return o -> {};
}
// require = 0 due to Forge removing the BLOCK_STATE_REGISTRY init here
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;initCache()V"), require = 0)
private static void skipCacheInit(BlockState state) {
// Mark the cache as invalid
((IBlockState)state).clearCache();
}
}

View File

@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.Shadow;
/* Idea from Lithium for 1.19.3 */
@Mixin(Biome.class)
public abstract class BiomeMixin {
@Shadow protected abstract float getHeightAdjustedTemperature(BlockPos pos);
@Shadow protected abstract float getHeightAdjustedTemperature(BlockPos pos, int i);
/**
* @author 2No2Name
@ -18,7 +18,7 @@ public abstract class BiomeMixin {
* @return
*/
@Overwrite
private float getTemperature(BlockPos pos) {
return this.getHeightAdjustedTemperature(pos);
private float getTemperature(BlockPos pos, int i) {
return this.getHeightAdjustedTemperature(pos, i);
}
}

View File

@ -1,37 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(Entity.class)
public class EntityMixin {
/**
* @author embeddedt
* @reason If the spawn chunks are not loaded, end portals linking to the overworld will teleport entities into
* the void at the spawn position, which is not ideal. To solve this, we create a PORTAL ticket if the expected
* overworld chunk is missing.
*/
@ModifyExpressionValue(method = "findDimensionEntryPoint", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getSharedSpawnPos()Lnet/minecraft/core/BlockPos;"), require = 0)
private BlockPos mfix$triggerChunkloadAtSpawnPos(BlockPos spawnPos, ServerLevel destination) {
// Only apply this change if the overworld is the destination
if (destination.dimension() == ServerLevel.OVERWORLD) {
// No ticket is required if the chunk happens to already be loaded
if(!destination.hasChunk(spawnPos.getX() >> 4, spawnPos.getZ() >> 4)) {
// Create a portal ticket. While we could just load the chunk once, it would immediately unload on the
// next tick, causing churn. The ticket will keep it loaded for a few seconds which should give high
// performance for farms pumping things through portals frequently.
BlockPos key = spawnPos.immutable();
destination.getChunkSource().addRegionTicket(TicketType.PORTAL, new ChunkPos(key), 3, key);
// Wait for the chunk to be loaded, as adding the ticket is asynchronous
destination.getChunk(key);
}
}
return spawnPos;
}
}

View File

@ -1,24 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = MinecraftServer.class, priority = 1100)
public class MinecraftServerMixin {
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
private void addSpawnChunkTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
// load first chunk
cache.getChunk(pos.x, pos.z, ChunkStatus.FULL, true);
}
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"), require = 0)
private int getGenerated(ServerChunkCache cache) {
return 441;
}
}

View File

@ -1,12 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerChunkCache;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ServerChunkCache.class)
public interface ServerChunkCacheAccessor {
@Accessor("distanceManager")
DistanceManager getDistanceManager();
}

View File

@ -1,20 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ServerLevel.class)
public class ServerLevelMixin {
@Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;removeRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
private void removeTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
}
@Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
private void addTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
}
}

View File

@ -1,25 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.util.SortedArraySet;
import org.embeddedt.modernfix.ModernFix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(SortedArraySet.class)
public class SortedArraySetMixin<T> {
/**
* @author embeddedt
* @reason Make add() not crash with a null key, since some mods (Carpet) assume there will always be a spawn ticket,
* and then assume the reference they have is non-null (it can be null with this option enabled).
*/
@WrapOperation(method = "add", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/SortedArraySet;findIndex(Ljava/lang/Object;)I"), require = 0)
private int checkStatus(SortedArraySet<T> instance, T object, Operation<Integer> original) {
if(object == null) {
ModernFix.LOGGER.error("Attempted to insert a null key into SortedArraySet, ignoring");
return 0;
}
return original.call(instance, object);
}
}

View File

@ -1,40 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
import com.mojang.datafixers.util.Either;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.world.level.chunk.LevelChunk;
import org.embeddedt.modernfix.util.EitherUtil;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.concurrent.CompletableFuture;
@Mixin(value = ChunkHolder.class, priority = 500)
public abstract class ChunkHolderMixin {
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture();
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture();
/**
* @author embeddedt
* @reason avoid Optional allocation
*/
@Overwrite
public LevelChunk getTickingChunk() {
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getTickingChunkFuture();
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
return either == null ? null : EitherUtil.leftOrNull(either);
}
/**
* @author embeddedt
* @reason avoid Optional allocation
*/
@Overwrite
public LevelChunk getFullChunk() {
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getFullChunkFuture();
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
return either == null ? null : EitherUtil.leftOrNull(either);
}
}

View File

@ -1,34 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.worldgen_allocation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.material.MaterialRuleList;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
@Mixin(value = MaterialRuleList.class, priority = 100)
public class MaterialRuleListMixin {
@Shadow @Final private List<NoiseChunk.BlockStateFiller> materialRuleList;
/**
* @author embeddedt
* @reason Avoid iterator allocation
*/
@Overwrite
@Nullable
public BlockState calculate(DensityFunction.FunctionContext arg) {
BlockState state = null;
int s = this.materialRuleList.size();
for(int i = 0; state == null && i < s; i++) {
NoiseChunk.BlockStateFiller blockStateFiller = this.materialRuleList.get(i);
state = blockStateFiller.calculate(arg);
}
return state;
}
}

View File

@ -1,25 +0,0 @@
package org.embeddedt.modernfix.common.mixin.safety;
import net.minecraft.client.color.item.ItemColors;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Mixin(value = ItemColors.class, priority = 700)
@ClientOnlyMixin
public class ItemColorsMixin {
private Lock mapLock = new ReentrantLock();
@Inject(method = "register", at = @At("HEAD"))
private void lockMapBeforeAccess(CallbackInfo ci) {
mapLock.lock();
}
@Inject(method = "register", at = @At("TAIL"))
private void unlockMap(CallbackInfo ci) {
mapLock.unlock();
}
}

View File

@ -1,30 +0,0 @@
package org.embeddedt.modernfix.common.mixin.safety;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.client.renderer.item.ItemPropertyFunction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collections;
import java.util.Map;
@Mixin(value = ItemProperties.class, priority = 700)
@ClientOnlyMixin
public class ItemPropertiesMixin {
@Shadow @Final @Mutable private static Map<ResourceLocation, ItemPropertyFunction> GENERIC_PROPERTIES;
@Shadow @Final @Mutable private static Map<Item, Map<ResourceLocation, ItemPropertyFunction>> PROPERTIES;
@Inject(method = "<clinit>", at = @At("RETURN"))
private static void useConcurrentMaps(CallbackInfo ci) {
GENERIC_PROPERTIES = Collections.synchronizedMap(GENERIC_PROPERTIES);
PROPERTIES = Collections.synchronizedMap(PROPERTIES);
}
}

View File

@ -1,18 +1,14 @@
package org.embeddedt.modernfix.core;
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
import org.embeddedt.modernfix.core.config.Option;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.world.ThreadDumper;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
import java.io.File;
import java.util.*;
@ -150,138 +146,6 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
if(mixinClassName.equals("org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds.BlockStateBaseMixin")) {
try {
applyBlockStateCacheScan(targetClass);
} catch(RuntimeException e) {
ModernFixMixinPlugin.instance.logger.error("Applying blockstate cache ASM patch failed", e);
}
}
ModernFixPlatformHooks.INSTANCE.applyASMTransformers(mixinClassName, targetClass);
}
private void applyBlockStateCacheScan(ClassNode targetClass) {
Set<String> initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache");
Set<String> whitelistedInjections = ImmutableSet.of(
"getFluidState", "method_26227", "m_60819_", "func_204520_s"
);
Map<String, MethodNode> injectorMethodNames = new HashMap<>();
Map<String, MethodNode> allMethods = new HashMap<>();
Map<String, String> injectorMixinSource = new HashMap<>();
String descriptor = Type.getDescriptor(MixinMerged.class);
for(MethodNode m : targetClass.methods) {
if((m.access & Opcodes.ACC_STATIC) != 0)
continue;
allMethods.put(m.name, m);
Set<AnnotationNode> seenNodes = new HashSet<>();
if(m.invisibleAnnotations != null) {
for(AnnotationNode ann : m.invisibleAnnotations) {
if(ann.desc.equals(descriptor)) {
seenNodes.add(ann);
}
}
}
if(m.visibleAnnotations != null) {
for(AnnotationNode ann : m.visibleAnnotations) {
if(ann.desc.equals(descriptor)) {
seenNodes.add(ann);
}
}
}
if(seenNodes.size() > 0) {
injectorMethodNames.put(m.name, m);
for(AnnotationNode node : seenNodes) {
for(int i = 0; i < node.values.size(); i += 2) {
if(Objects.equals(node.values.get(i), "mixin")) {
injectorMixinSource.put(m.name, (String)node.values.get(i + 1));
break;
}
}
}
}
}
Set<String> cacheCalledInjectors = new HashSet<>();
// Search for initCache in the class
for(MethodNode m : targetClass.methods) {
if((m.access & Opcodes.ACC_STATIC) != 0)
continue;
if(initCacheMethodNames.contains(m.name)) {
// This is it. Check for any injectors it calls
for(AbstractInsnNode n : m.instructions) {
if(n instanceof MethodInsnNode) {
MethodInsnNode invoke = (MethodInsnNode)n;
if(((MethodInsnNode)n).owner.equals(targetClass.name) && injectorMethodNames.containsKey(((MethodInsnNode)n).name)) {
cacheCalledInjectors.add(invoke.name);
}
}
}
break;
}
}
Set<String> accessedFieldNames = new HashSet<>();
// Make a map of all injected methods called by initCache
Map<String, MethodNode> writingMethods = new HashMap<>(injectorMethodNames);
writingMethods.keySet().retainAll(cacheCalledInjectors);
// Recursively check the injected methods for any methods they may call
int previousSize = 0;
Set<String> checkedCalls = new HashSet<>();
while(writingMethods.size() > previousSize) {
previousSize = writingMethods.size();
List<String> keysToCheck = new ArrayList<>(writingMethods.keySet());
for(String name : keysToCheck) {
if(!checkedCalls.add(name))
continue;
for(AbstractInsnNode n : writingMethods.get(name).instructions) {
if(n instanceof MethodInsnNode) {
MethodInsnNode invokeNode = (MethodInsnNode)n;
if(invokeNode.owner.equals(targetClass.name)) {
MethodNode theMethod = allMethods.get(invokeNode.name);
if(theMethod != null)
writingMethods.put(invokeNode.name, theMethod);
}
}
}
}
}
// We now know all methods that have been injected into initCache, and their callers. See what fields they write to
writingMethods.forEach((name, method) -> {
for(AbstractInsnNode n : method.instructions) {
if(n instanceof FieldInsnNode) {
FieldInsnNode fieldAcc = (FieldInsnNode)n;
if(fieldAcc.getOpcode() == Opcodes.PUTFIELD && fieldAcc.owner.equals(targetClass.name)) {
accessedFieldNames.add(fieldAcc.name);
}
}
}
});
// Lastly, scan all injected methods and see if they retrieve from the field. If so, inject a generateCache
// call at the start.
injectorMethodNames.forEach((name, method) -> {
// skip whitelisted injectors, and injectors called by initCache itself (to prevent recursion)
if(whitelistedInjections.contains(name) || cacheCalledInjectors.contains(name))
return;
boolean needInjection = false;
for(AbstractInsnNode n : method.instructions) {
if(n instanceof FieldInsnNode) {
FieldInsnNode fieldAcc = (FieldInsnNode)n;
if(fieldAcc.getOpcode() == Opcodes.GETFIELD && accessedFieldNames.contains(fieldAcc.name)) {
needInjection = true;
break;
}
}
}
if(needInjection) {
ModernFixMixinPlugin.instance.logger.info("Injecting BlockStateBase cache population hook into {} from {}",
name, injectorMixinSource.getOrDefault(name, "[unknown mixin]"));
// inject this.mfix$generateCache() at method head
InsnList injection = new InsnList();
injection.add(new VarInsnNode(Opcodes.ALOAD, 0));
injection.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, targetClass.name, "mfix$generateCache", "()V"));
method.instructions.insert(injection);
}
});
}
}

View File

@ -62,7 +62,7 @@ public class ModernFixEarlyConfig {
private static final String MIXIN_REQUIRES_MOD_DESC = Type.getDescriptor(RequiresMod.class);
private static final String MIXIN_DEV_ONLY_DESC = Type.getDescriptor(IgnoreOutsideDev.class);
private static final Pattern PLATFORM_PREFIX = Pattern.compile("(forge|fabric|common)\\.");
private static final Pattern PLATFORM_PREFIX = Pattern.compile("(neoforge|fabric|common)\\.");
public static String sanitize(String mixinClassName) {
return PLATFORM_PREFIX.matcher(mixinClassName).replaceFirst("");
@ -78,7 +78,7 @@ public class ModernFixEarlyConfig {
}
private void scanForAndBuildMixinOptions() {
List<String> configFiles = ImmutableList.of("modernfix-common.mixins.json", "modernfix-fabric.mixins.json", "modernfix-forge.mixins.json");
List<String> configFiles = ImmutableList.of("modernfix-common.mixins.json", "modernfix-fabric.mixins.json", "modernfix-neoforge.mixins.json");
List<String> mixinPaths = new ArrayList<>();
for(String configFile : configFiles) {
InputStream stream = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream(configFile);
@ -163,7 +163,6 @@ public class ModernFixEarlyConfig {
.put("mixin.perf.dynamic_resources", false)
.put("mixin.feature.direct_stack_trace", false)
.put("mixin.feature.stalled_chunk_load_detection", false)
.put("mixin.perf.blast_search_trees.force", false)
.put("mixin.bugfix.restore_old_dragon_movement", false)
.put("mixin.perf.worldgen_allocation", false) // experimental
.put("mixin.feature.cause_lag_by_disabling_threads", false)
@ -182,9 +181,7 @@ public class ModernFixEarlyConfig {
.put("mixin.feature.warn_missing_perf_mods", true)
.put("mixin.feature.spark_profile_launch", false)
.put("mixin.devenv", isDevEnv)
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
.putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true)
.putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true)
.putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false)
.putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false)
.build();

View File

@ -1,45 +0,0 @@
package org.embeddedt.modernfix.dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.function.Supplier;
public class LazyDataFixer implements DataFixer {
private static final Logger LOGGER = LogManager.getLogger("ModernFix");
private DataFixer backingDataFixer;
private final Supplier<DataFixer> dfuSupplier;
public LazyDataFixer(Supplier<DataFixer> dfuSupplier) {
LOGGER.info("Bypassed Mojang DFU");
this.backingDataFixer = null;
this.dfuSupplier = dfuSupplier;
}
private DataFixer getDataFixer() {
synchronized (this) {
if(backingDataFixer == null) {
LOGGER.info("Instantiating Mojang DFU");
DFUBlaster.blastMaps();
backingDataFixer = dfuSupplier.get();
}
}
return backingDataFixer;
}
@Override
public <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> input, int version, int newVersion) {
if(version >= newVersion)
return input;
return getDataFixer().update(type, input, version, newVersion);
}
@Override
public Schema getSchema(int key) {
return getDataFixer().getSchema(key);
}
}

View File

@ -0,0 +1,7 @@
package org.embeddedt.modernfix.duck;
import net.minecraft.client.resources.model.ModelResourceLocation;
public interface IBlockStateModelLoader {
void loadSpecificBlock(ModelResourceLocation location);
}

View File

@ -1,9 +0,0 @@
package org.embeddedt.modernfix.duck;
public interface IExtendedModelBaker {
/**
* Causes the ModelBaker to throw when it finds a missing model instead of proceeding with the bake.
* @return the previous value of this flag
*/
boolean throwOnMissingModel(boolean flag);
}

View File

@ -1,17 +1,11 @@
package org.embeddedt.modernfix.duck;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
public interface IExtendedModelBakery {
ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location);
BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state);
UnbakedModel mfix$getUnbakedMissingModel();
void mfix$tick();
void mfix$finishLoading();
UnbakedModel mfix$loadUnbakedModelDynamic(ModelResourceLocation location);
UnbakedModel mfix$getMissingModel();
}

View File

@ -0,0 +1,5 @@
package org.embeddedt.modernfix.duck;
public interface IExtendedModelManager {
void mfix$tick();
}

View File

@ -1,243 +0,0 @@
package org.embeddedt.modernfix.dynamicresources;
import com.google.common.collect.ImmutableSet;
import com.mojang.math.Transformation;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.ModernFix;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedModel> {
/**
* The list of blacklisted resource locations that are never baked as top-level models.
*
* This is a hack to get around the fact that we don't really know exactly what models were supposed to end up
* in the baked registry ahead of time.
*/
private static final ImmutableSet<ResourceLocation> BAKE_SKIPPED_TOPLEVEL = ImmutableSet.<ResourceLocation>builder()
.add(new ResourceLocation("custommachinery", "block/custom_machine_block"))
.build();
public static DynamicBakedModelProvider currentInstance = null;
private final ModelBakery bakery;
private final Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
private final Map<ResourceLocation, BakedModel> permanentOverrides;
private BakedModel missingModel;
private static final BakedModel SENTINEL = new BakedModel() {
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) {
return null;
}
@Override
public boolean useAmbientOcclusion() {
return false;
}
@Override
public boolean isGui3d() {
return false;
}
@Override
public boolean usesBlockLight() {
return false;
}
@Override
public boolean isCustomRenderer() {
return false;
}
@Override
public TextureAtlasSprite getParticleIcon() {
return null;
}
@Override
public ItemTransforms getTransforms() {
return null;
}
@Override
public ItemOverrides getOverrides() {
return null;
}
};
public DynamicBakedModelProvider(ModelBakery bakery, Map<ModelBakery.BakedCacheKey, BakedModel> cache) {
this.bakery = bakery;
this.bakedCache = cache;
this.permanentOverrides = Collections.synchronizedMap(new Object2ObjectOpenHashMap<>());
if(currentInstance == null)
currentInstance = this;
}
private static ModelBakery.BakedCacheKey vanillaKey(Object o) {
return new ModelBakery.BakedCacheKey((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
}
@Override
public int size() {
return bakedCache.size();
}
@Override
public boolean isEmpty() {
return bakedCache.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return permanentOverrides.getOrDefault(o, SENTINEL) != null;
}
@Override
public boolean containsValue(Object o) {
return permanentOverrides.containsValue(o) || bakedCache.containsValue(o);
}
private static boolean isVanillaTopLevelModel(ResourceLocation location) {
if(location instanceof ModelResourceLocation) {
try {
ModelResourceLocation mrl = (ModelResourceLocation)location;
ResourceLocation registryKey = new ResourceLocation(mrl.getNamespace(), mrl.getPath());
// check for standard inventory model
if(mrl.getVariant().equals("inventory") && BuiltInRegistries.ITEM.containsKey(registryKey))
return true;
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(registryKey);
if(blockOpt.isPresent()) {
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), mrl).size() > 0;
}
} catch(RuntimeException ignored) {
// can occur if the MRL is not valid for that blockstate, ignore
}
}
if(location.getNamespace().equals("minecraft") && location.getPath().equals("builtin/missing"))
return true;
return false;
}
private BakedModel getMissingModel() {
BakedModel m = missingModel;
if(m == null) {
m = missingModel = ((IExtendedModelBakery)bakery).bakeDefault(ModelBakery.MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0);
}
return m;
}
@Override
public BakedModel get(Object o) {
if (o == null) {
return null;
}
BakedModel model = permanentOverrides.getOrDefault(o, SENTINEL);
if(model != SENTINEL)
return model;
else {
try {
if(BAKE_SKIPPED_TOPLEVEL.contains((ResourceLocation)o))
model = getMissingModel();
else
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o, BlockModelRotation.X0_Y0);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception baking {}: {}", o, e);
model = getMissingModel();
}
if(model == getMissingModel()) {
// to correctly emulate the original map, we return null for missing models, unless they are top-level
model = isVanillaTopLevelModel((ResourceLocation)o) ? model : null;
permanentOverrides.put((ResourceLocation) o, model);
}
return model;
}
}
@Override
public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) {
BakedModel m = permanentOverrides.put(resourceLocation, bakedModel);
if(m != null)
return m;
else
return bakedCache.get(vanillaKey(resourceLocation));
}
@Override
public BakedModel remove(Object o) {
BakedModel m = permanentOverrides.remove(o);
if(m != null)
return m;
return bakedCache.remove(vanillaKey(o));
}
@Override
public void putAll(@NotNull Map<? extends ResourceLocation, ? extends BakedModel> map) {
permanentOverrides.putAll(map);
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<ResourceLocation> keySet() {
return bakedCache.keySet().stream().map(ModelBakery.BakedCacheKey::id).collect(Collectors.toSet());
}
@NotNull
@Override
public Collection<BakedModel> values() {
return bakedCache.values();
}
@NotNull
@Override
public Set<Entry<ResourceLocation, BakedModel>> entrySet() {
return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().id(), entry.getValue())).collect(Collectors.toSet());
}
@Nullable
@Override
public BakedModel replace(ResourceLocation key, BakedModel value) {
BakedModel existingOverride = permanentOverrides.get(key);
// as long as no valid override was put in (null can mean unable to load model, so we treat as invalid), replace
// the model
if(existingOverride == null)
return this.put(key, value);
else
return existingOverride;
}
@Override
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
Set<ResourceLocation> overridenLocations = permanentOverrides.keySet();
permanentOverrides.replaceAll(function);
boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked();
Transformation rotation = BlockModelRotation.X0_Y0.getRotation();
bakedCache.replaceAll((loc, oldModel) -> {
if(loc.transformation() != rotation || loc.isUvLocked() != uvLock || overridenLocations.contains(loc.id()))
return oldModel;
else
return function.apply(loc.id(), oldModel);
});
}
}

View File

@ -1,79 +0,0 @@
package org.embeddedt.modernfix.dynamicresources;
import it.unimi.dsi.fastutil.Function;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
import net.minecraft.client.resources.model.BakedModel;
import java.util.concurrent.locks.StampedLock;
/**
* The Mojang Triple-based baked cache system is too slow to be hitting on every model retrieval, so
* we need a fast, concurrency-safe wrapper on top.
*/
public class DynamicModelCache<K> {
private final Reference2ReferenceLinkedOpenHashMap<K, BakedModel> cache = new Reference2ReferenceLinkedOpenHashMap<>();
private final StampedLock lock = new StampedLock();
private final Function<K, BakedModel> modelRetriever;
private final boolean allowNulls;
public DynamicModelCache(Function<K, BakedModel> modelRetriever, boolean allowNulls) {
this.modelRetriever = modelRetriever;
this.allowNulls = allowNulls;
}
public void clear() {
long stamp = lock.writeLock();
try {
cache.clear();
} finally {
lock.unlock(stamp);
}
}
private boolean needToPopulate(K state) {
long stamp = lock.readLock();
try {
return !cache.containsKey(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel getModelFromCache(K state) {
long stamp = lock.readLock();
try {
return cache.get(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel cacheModel(K state) {
BakedModel model = modelRetriever.apply(state);
// Lock and modify our local, faster cache
long stamp = lock.writeLock();
try {
cache.putAndMoveToFirst(state, model);
// TODO: choose less arbitrary number
if(cache.size() >= 1000) {
cache.removeLast();
}
} finally {
lock.unlock(stamp);
}
return model;
}
public BakedModel get(K key) {
BakedModel model = getModelFromCache(key);
if(model == null && (!allowNulls || needToPopulate(key))) {
model = cacheModel(key);
}
return model;
}
}

View File

@ -1,41 +1,618 @@
package org.embeddedt.modernfix.dynamicresources;
import com.google.common.cache.Cache;
import com.google.common.base.Suppliers;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
import net.minecraft.client.renderer.block.model.ItemModelGenerator;
import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.MissingItemModel;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.AtlasSet;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.MissingBlockModel;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelDebugName;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.SpriteGetter;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import org.embeddedt.modernfix.ModernFix;
import org.jetbrains.annotations.NotNull;
import java.io.Reader;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Handles loading models dynamically, rather than at startup time.
*/
public class DynamicModelProvider {
private final Map<ResourceLocation, UnbakedModel> internalModels;
private final Cache<ResourceLocation, Optional<UnbakedModel>> loadedModels =
CacheBuilder.newBuilder()
.expireAfterAccess(3, TimeUnit.MINUTES)
.maximumSize(1000)
.concurrencyLevel(8)
.softValues()
.build();
private final LoadingCache<ResourceLocation, Optional<BlockStateModelLoader.LoadedModels>> loadedStateDefinitions =
this.makeLoadingCache(this::loadBlockStateDefinition);
public DynamicModelProvider(Map<ResourceLocation, UnbakedModel> initialModels) {
this.internalModels = initialModels;
}
private final LoadingCache<ResourceLocation, Optional<UnbakedModel>> loadedBlockModels =
this.makeLoadingCache(this::loadBlockModel);
public UnbakedModel getModel(ResourceLocation location) {
private final LoadingCache<ModelResourceLocation, Optional<BakedModel>> loadedBakedModels =
this.makeLoadingCache(this::loadBakedModel);
private final LoadingCache<ResourceLocation, Optional<ClientItem>> loadedClientItemProperties =
this.makeLoadingCache(this::loadClientItemProperties);
private final LoadingCache<ResourceLocation, Optional<ItemModel>> loadedItemModels =
this.makeLoadingCache(this::loadItemModel);
private final LoadingCache<ResourceLocation, Optional<BakedModel>> loadedStandaloneModels =
this.makeLoadingCache(this::loadStandaloneModel);
private final BakedModel missingModel;
private final ItemModel missingItemModel;
private final UnbakedModel unbakedMissingModel;
private final Function<ResourceLocation, StateDefinition<Block, BlockState>> stateMapper;
private final ResourceManager resourceManager;
private final ModelBakery.TextureGetter textureGetter;
private final DynamicResolver resolver;
private final EntityModelSet entityModelSet;
private final ItemModelGenerator itemModelGenerator;
private final Map<ModelResourceLocation, BakedModel> mrlModelOverrides = new ConcurrentHashMap<>();
private final Map<ResourceLocation, ItemModel> itemStackModelOverrides = new ConcurrentHashMap<>();
private final Map<ResourceLocation, BakedModel> standaloneModelOverrides = new ConcurrentHashMap<>();
private final Map<ModelResourceLocation, UnbakedBlockStateModel> unbakedBlockStateModelOverrides = new ConcurrentHashMap<>();
private final List<DynamicModelProvider.DynamicModelPlugin> pluginList = new ArrayList<>();
private static final boolean DEBUG_DYNAMIC_MODEL_LOADING = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
public DynamicModelProvider(ResourceManager resourceManager, EntityModelSet entityModelSet,
Map<ResourceLocation, AtlasSet.StitchResult> atlasMap) {
this.unbakedMissingModel = MissingBlockModel.missingModel();
this.entityModelSet = entityModelSet;
var missing = atlasMap.get(TextureAtlas.LOCATION_BLOCKS).missing();
this.textureGetter = new ModelBakery.TextureGetter() {
@Override
public TextureAtlasSprite get(ModelDebugName modelDebugName, Material material) {
var atlas = atlasMap.get(material.atlasLocation());
var sprite = atlas.getSprite(material.texture());
if (sprite != null) {
return sprite;
} else {
ModernFix.LOGGER.warn("Unable to find sprite '{}' referenced by model '{}'", material.texture(), modelDebugName.get());
return missing;
}
}
@Override
public TextureAtlasSprite reportMissingReference(ModelDebugName modelDebugName, String string) {
return missing;
}
};
this.stateMapper = BlockStateModelLoader.definitionLocationToBlockMapper();
this.resourceManager = resourceManager;
this.resolver = new DynamicResolver();
this.itemModelGenerator = new ItemModelGenerator();
this.missingModel = this.bakeMissingModel();
this.missingItemModel = new MissingItemModel(this.missingModel);
try {
return loadedModels.get(location, () -> Optional.ofNullable(loadModel(location))).orElse(null);
} catch(ExecutionException e) {
throw new RuntimeException(e.getCause());
Class.forName("net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin");
pluginList.add(new FabricDynamicModelHandler(this));
} catch(Exception ignored) {
// Fabric API likely not present
}
}
private UnbakedModel loadModel(ResourceLocation location) {
return null; /* TODO :) */
public BakedModel getMissingBakedModel() {
return this.missingModel;
}
public ItemModel getMissingItemModel() {
return this.missingItemModel;
}
private static final Supplier<Set<ModelResourceLocation>> TOP_LEVEL_LOCATIONS_SUPPLIER = Suppliers.memoizeWithExpiration(() -> {
Set<ModelResourceLocation> set = new HashSet<>();
// Skip going through ModelLocationCache because most of the accesses will be misses
BuiltInRegistries.BLOCK.entrySet().forEach(entry -> {
var location = entry.getKey().location();
for(BlockState state : entry.getValue().getStateDefinition().getPossibleStates()) {
set.add(BlockModelShaper.stateToModelLocation(location, state));
}
});
return Collections.unmodifiableSet(set);
}, 2, TimeUnit.MINUTES);
public Map<ModelResourceLocation, BakedModel> getTopLevelEmulatedRegistry() {
return new EmulatedRegistry<>(ModelResourceLocation.class, this.loadedBakedModels, TOP_LEVEL_LOCATIONS_SUPPLIER, this.mrlModelOverrides);
}
public Map<ResourceLocation, BakedModel> getStandaloneEmulatedRegistry() {
return new EmulatedRegistry<>(ResourceLocation.class, this.loadedStandaloneModels, Set::of, this.standaloneModelOverrides);
}
public Map<ResourceLocation, ItemModel> getItemModelEmulatedRegistry() {
return new EmulatedRegistry<>(ResourceLocation.class, this.loadedItemModels, BuiltInRegistries.ITEM::keySet, this.itemStackModelOverrides);
}
public Map<ResourceLocation, ClientItem.Properties> getItemPropertiesEmulatedRegistry() {
return Maps.transformValues(new EmulatedRegistry<>(ResourceLocation.class, this.loadedClientItemProperties, BuiltInRegistries.ITEM::keySet, Map.of()), ClientItem::properties);
}
private <K, V> LoadingCache<K, Optional<V>> makeLoadingCache(Function<K, Optional<V>> loadingFunction) {
return CacheBuilder.newBuilder()
.expireAfterAccess(3, TimeUnit.MINUTES)
.maximumSize(1000)
.concurrencyLevel(8)
.softValues()
.build(new CacheLoader<>() {
@Override
public Optional<V> load(K key) {
return loadingFunction.apply(key);
}
});
}
private static class EmulatedRegistry<K, V> implements Map<K, V> {
private final LoadingCache<K, Optional<V>> realCache;
private final Supplier<Set<K>> keys;
private final Map<K, V> overrides;
private final Class<K> keyClass;
public EmulatedRegistry(Class<K> keyClass, LoadingCache<K, Optional<V>> realCache, Supplier<Set<K>> keys, Map<K, V> overrides) {
this.keyClass = keyClass;
this.realCache = realCache;
this.keys = keys;
this.overrides = overrides;
}
@Override
public V get(Object key) {
if (this.keyClass.isAssignableFrom(key.getClass())) {
return this.realCache.getUnchecked((K)key).orElse(null);
} else {
return null;
}
}
@Override
public V getOrDefault(Object key, V defaultValue) {
if (this.keyClass.isAssignableFrom(key.getClass())) {
return this.realCache.getUnchecked((K)key).orElse(defaultValue);
} else {
return defaultValue;
}
}
@Override
public V put(K key, V value) {
V oldValue = this.realCache.getUnchecked(key).orElse(null);
this.overrides.put(key, value);
this.realCache.invalidate(key);
return oldValue;
}
@Override
public V remove(Object key) {
this.overrides.remove(key);
this.realCache.invalidate(key);
return null;
}
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public void clear() {
this.overrides.clear();
this.realCache.invalidateAll();
}
@Override
public @NotNull Set<K> keySet() {
return keys.get();
}
@Override
public @NotNull Collection<V> values() {
return Collections.emptyList();
}
@Override
public int size() {
return keys.get().size();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsKey(Object key) {
return keys.get().contains(key);
}
@Override
public boolean containsValue(Object value) {
return false;
}
@Override
public @NotNull Set<Entry<K, V>> entrySet() {
return new AbstractSet<>() {
@Override
public Iterator<Entry<K, V>> iterator() {
return Iterators.transform(keys.get().iterator(), key -> new Entry<>() {
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return get(key);
}
@Override
public V setValue(V value) {
return put(key, value);
}
});
}
@Override
public int size() {
return keys.get().size();
}
};
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
for(K location : keys.get()) {
/*
* Fetching every model is insanely slow. So we call the function with a null object first, since it
* probably isn't expecting that. If we get an exception thrown, or it returns nonnull, then we know
* it actually cares about the given model.
*/
boolean needsReplacement;
try {
needsReplacement = function.apply(location, null) != null;
} catch(Throwable e) {
needsReplacement = true;
}
if(needsReplacement) {
V existing = get(location);
V replacement = function.apply(location, existing);
if(replacement != existing) {
put(location, replacement);
}
}
}
}
}
private Optional<BlockStateModelLoader.LoadedModels> loadBlockStateDefinition(ResourceLocation location) {
StateDefinition<Block, BlockState> stateDefinition = this.stateMapper.apply(location);
if(stateDefinition == null) {
return Optional.empty();
}
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Loading blockstate definition '{}'", location);
}
List<Resource> resources = resourceManager.getResourceStack(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "blockstates/" + location.getPath() + ".json"));
List<BlockStateModelLoader.LoadedBlockModelDefinition> loadedDefinitions = new ArrayList<>(resources.size());
for(Resource resource : resources) {
try(Reader reader = resource.openAsReader()) {
JsonObject jsonObject = GsonHelper.parse(reader);
BlockModelDefinition blockModelDefinition = BlockModelDefinition.fromJsonElement(jsonObject);
loadedDefinitions.add(new BlockStateModelLoader.LoadedBlockModelDefinition(resource.sourcePackId(), blockModelDefinition));
} catch(Exception e) {
ModernFix.LOGGER.error("Failed to load blockstate definition {} from pack '{}'", location, resource.sourcePackId(), e);
}
}
var loadedModels = new HashMap<>(BlockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions, this.unbakedMissingModel).models());
if (!pluginList.isEmpty()) {
loadedModels.replaceAll((mrl, oldModel) -> {
UnbakedBlockStateModel ubm = oldModel.model();
for (var plugin : pluginList) {
ubm = plugin.modifyBlockModelOnLoad(ubm, mrl, oldModel.state());
}
if (ubm == oldModel.model()) {
return oldModel;
} else {
return new BlockStateModelLoader.LoadedModel(oldModel.state(), ubm);
}
});
}
return Optional.of(new BlockStateModelLoader.LoadedModels(loadedModels));
}
private BakedModel bakeMissingModel() {
this.resolver.clearResolver();
this.unbakedMissingModel.resolveDependencies(this.resolver);
var modelBaker = new DynamicBaker(() -> "missing");
return UnbakedModel.bakeWithTopModelValues(this.unbakedMissingModel, modelBaker, BlockModelRotation.X0_Y0);
}
private BakedModel bakeModel(UnbakedModel model, ResourceLocation location) {
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Baking model '{}'", location);
}
synchronized (this) {
this.resolver.clearResolver();
model.resolveDependencies(this.resolver);
var modelBaker = new DynamicBaker(location::toString);
for (var plugin : pluginList) {
model = plugin.modifyModelBeforeBake(model, location, BlockModelRotation.X0_Y0, modelBaker);
}
var bakedModel = UnbakedModel.bakeWithTopModelValues(model, modelBaker, BlockModelRotation.X0_Y0);
for (var plugin : pluginList) {
bakedModel = plugin.modifyModelAfterBake(bakedModel, model, location, BlockModelRotation.X0_Y0, modelBaker);
}
return bakedModel;
}
}
private BakedModel bakeModel(UnbakedBlockStateModel model, ModelResourceLocation mrl) {
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Baking model '{}'", mrl);
}
synchronized (this) {
this.resolver.clearResolver();
model.resolveDependencies(this.resolver);
var modelBaker = new DynamicBaker(mrl::toString);
for (var plugin : pluginList) {
model = plugin.modifyBlockModelBeforeBake(model, mrl, modelBaker);
}
var bakedModel = model.bake(modelBaker);
for (var plugin : pluginList) {
bakedModel = plugin.modifyBlockModelAfterBake(bakedModel, model, mrl, modelBaker);
}
return bakedModel;
}
}
private Optional<BakedModel> loadBakedModel(ModelResourceLocation location) {
var override = this.mrlModelOverrides.get(location);
if (override != null) {
return Optional.of(override);
}
if (location.variant().equals("standalone") || location.variant().equals("fabric_resource")) {
return this.loadStandaloneModel(location.id());
} else {
Optional<UnbakedBlockStateModel> unbakedModelOpt = Optional.ofNullable(this.unbakedBlockStateModelOverrides.get(location));
if (unbakedModelOpt.isEmpty()) {
var optLoadedModels = this.loadedStateDefinitions.getUnchecked(location.id());
unbakedModelOpt = optLoadedModels.map(loadedModels -> {
var loadedModel = loadedModels.models().get(location);
if(loadedModel != null) {
return loadedModel.model();
} else {
return null;
}
});
}
return unbakedModelOpt.map(unbakedModel -> {
return this.bakeModel(unbakedModel, location);
});
}
}
private Optional<BakedModel> loadStandaloneModel(ResourceLocation location) {
var override = this.standaloneModelOverrides.get(location);
if (override != null) {
return Optional.of(override);
}
return this.loadedBlockModels.getUnchecked(location).map(unbakedModel -> {
return this.bakeModel(unbakedModel, location);
});
}
private Optional<UnbakedModel> loadBlockModelDefault(ResourceLocation location) {
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Loading block model '{}'", location);
}
if (location.equals(ItemModelGenerator.GENERATED_ITEM_MODEL_ID)) {
return Optional.of(this.itemModelGenerator);
} else if (location.equals(MissingBlockModel.LOCATION)) {
return Optional.of(this.unbakedMissingModel);
}
var resource = this.resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "models/" + location.getPath() + ".json"));
if(resource.isPresent()) {
try(Reader reader = resource.get().openAsReader()) {
BlockModel blockModel = BlockModel.fromStream(reader);
return Optional.of(blockModel);
} catch(Exception e) {
ModernFix.LOGGER.error("Failed to load block model {} from '{}'", location, resource.get().sourcePackId(), e);
return Optional.empty();
}
} else {
ModernFix.LOGGER.warn("Model '{}' does not exist in any resource packs", location);
return Optional.empty();
}
}
private Optional<UnbakedModel> loadBlockModel(ResourceLocation location) {
Optional<UnbakedModel> value = loadBlockModelDefault(location);
for (var plugin : this.pluginList) {
value = plugin.modifyModelOnLoad(value, location);
}
return value;
}
private Optional<ClientItem> loadClientItemProperties(ResourceLocation location) {
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Loading client item '{}'", location);
}
var resource = this.resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "items/" + location.getPath() + ".json"));
if(resource.isPresent()) {
try(Reader reader = resource.get().openAsReader()) {
ClientItem clientItem = ClientItem.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow();
return Optional.of(clientItem);
} catch(Exception e) {
ModernFix.LOGGER.error("Failed to load client item {} from '{}'", location, resource.get().sourcePackId(), e);
return Optional.empty();
}
} else {
ModernFix.LOGGER.warn("Client item '{}' does not exist in any resource packs", location);
return Optional.empty();
}
}
private Optional<ItemModel> loadItemModel(ResourceLocation location) {
if (DEBUG_DYNAMIC_MODEL_LOADING) {
ModernFix.LOGGER.info("Loading item model '{}'", location);
}
var override = this.itemStackModelOverrides.get(location);
if (override != null) {
return Optional.of(override);
}
return this.loadedClientItemProperties.getUnchecked(location).map(clientItem -> {
var bakingContext = new ItemModel.BakingContext(new DynamicBaker(location::toString), this.entityModelSet, this.missingItemModel);
return clientItem.model().bake(bakingContext);
});
}
public BakedModel getModel(ModelResourceLocation location) {
return this.loadedBakedModels.getUnchecked(location).orElse(this.missingModel);
}
public ClientItem.Properties getClientItemProperties(ResourceLocation location) {
return this.loadedClientItemProperties.getUnchecked(location).map(ClientItem::properties).orElse(ClientItem.Properties.DEFAULT);
}
public ItemModel getItemModel(ResourceLocation location) {
return this.loadedItemModels.getUnchecked(location).orElse(this.missingItemModel);
}
public BakedModel getStandaloneModel(ResourceLocation location) {
return this.loadedStandaloneModels.getUnchecked(location).orElse(this.missingModel);
}
public void addUnbakedBlockStateOverride(ModelResourceLocation location, UnbakedBlockStateModel model) {
this.unbakedBlockStateModelOverrides.put(location, model);
}
private class DynamicBaker implements ModelBaker {
private final ModelDebugName modelDebugName;
private DynamicBaker(ModelDebugName modelDebugName) {
this.modelDebugName = modelDebugName;
}
@Override
public BakedModel bake(ResourceLocation location, ModelState transform) {
return DynamicModelProvider.this.loadedBlockModels.getUnchecked(location).map(unbakedModel -> {
DynamicModelProvider.this.resolver.clearResolver();
unbakedModel.resolveDependencies(DynamicModelProvider.this.resolver);
return UnbakedModel.bakeWithTopModelValues(unbakedModel, this, transform);
}).orElse(DynamicModelProvider.this.missingModel);
}
@Override
public SpriteGetter sprites() {
return DynamicModelProvider.this.textureGetter.bind(this.modelDebugName);
}
@Override
public ModelDebugName rootName() {
return this.modelDebugName;
}
}
/**
* Based on the Mojang impl but with some changes to make it slightly more efficient.
*/
private class DynamicResolver implements UnbakedModel.Resolver {
private final Set<ResourceLocation> stack = new ObjectOpenHashSet<>(4);
private final Set<ResourceLocation> resolvedModels = new ObjectOpenHashSet<>();
@Override
public UnbakedModel resolve(ResourceLocation resourceLocation) {
if (this.stack.contains(resourceLocation)) {
ModernFix.LOGGER.warn("Detected model loading loop: {}->{}", this.stacktraceToString(), resourceLocation);
return DynamicModelProvider.this.unbakedMissingModel;
} else {
UnbakedModel unbakedModel = DynamicModelProvider.this.loadedBlockModels.getUnchecked(resourceLocation).orElse(DynamicModelProvider.this.unbakedMissingModel);
if (this.resolvedModels.add(resourceLocation)) {
this.stack.add(resourceLocation);
unbakedModel.resolveDependencies(this);
this.stack.remove(resourceLocation);
}
return unbakedModel;
}
}
private String stacktraceToString() {
return this.stack.stream().map(ResourceLocation::toString).collect(Collectors.joining("->"));
}
public void clearResolver() {
this.stack.clear();
this.resolvedModels.clear();
}
}
public static WeakReference<DynamicModelProvider> currentReloadingModelProvider = new WeakReference<>(null);
public interface ModelManagerExtension {
DynamicModelProvider mfix$getModelProvider();
}
public interface DynamicModelPlugin {
Optional<UnbakedModel> modifyModelOnLoad(Optional<UnbakedModel> model, ResourceLocation id);
UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, ModelResourceLocation id, BlockState state);
UnbakedModel modifyModelBeforeBake(UnbakedModel model, ResourceLocation id, ModelState state, ModelBaker baker);
BakedModel modifyModelAfterBake(BakedModel bakedModel, UnbakedModel model, ResourceLocation id, ModelState state, ModelBaker baker);
UnbakedBlockStateModel modifyBlockModelBeforeBake(UnbakedBlockStateModel model, ModelResourceLocation id, ModelBaker baker);
BakedModel modifyBlockModelAfterBake(BakedModel bakedModel, UnbakedBlockStateModel model, ModelResourceLocation id, ModelBaker baker);
}
}

View File

@ -0,0 +1,280 @@
package org.embeddedt.modernfix.dynamicresources;
import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.ModernFix;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class FabricDynamicModelHandler implements DynamicModelProvider.DynamicModelPlugin {
private final List<ModelLoadingPlugin> pluginList;
// Borrowed from Fabric API, this dispatching logic is extremely trivial
private static final ResourceLocation[] MODEL_MODIFIER_PHASES = new ResourceLocation[] { ModelModifier.OVERRIDE_PHASE, ModelModifier.DEFAULT_PHASE, ModelModifier.WRAP_PHASE, ModelModifier.WRAP_LAST_PHASE };
private final Event<ModelModifier.OnLoad> onLoadModifiers = EventFactory.createWithPhases(ModelModifier.OnLoad.class, modifiers -> (model, context) -> {
for (ModelModifier.OnLoad modifier : modifiers) {
try {
model = modifier.modifyModelOnLoad(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify unbaked model on load", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
private final Event<ModelModifier.OnLoadBlock> onLoadBlockModifiers = EventFactory.createWithPhases(ModelModifier.OnLoadBlock.class, modifiers -> (model, context) -> {
for (ModelModifier.OnLoadBlock modifier : modifiers) {
try {
model = modifier.modifyModelOnLoad(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify unbaked block model on load", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
private final Event<ModelModifier.BeforeBakeBlock> beforeBakeBlockModifiers = EventFactory.createWithPhases(ModelModifier.BeforeBakeBlock.class, modifiers -> (model, context) -> {
for (ModelModifier.BeforeBakeBlock modifier : modifiers) {
try {
model = modifier.modifyModelBeforeBake(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify unbaked block model before bake", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
private final Event<ModelModifier.AfterBakeBlock> afterBakeBlockModifiers = EventFactory.createWithPhases(ModelModifier.AfterBakeBlock.class, modifiers -> (model, context) -> {
for (ModelModifier.AfterBakeBlock modifier : modifiers) {
try {
model = modifier.modifyModelAfterBake(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify baked block model after bake", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
private final Event<ModelModifier.BeforeBake> beforeBakeModifiers = EventFactory.createWithPhases(ModelModifier.BeforeBake.class, modifiers -> (model, context) -> {
for (ModelModifier.BeforeBake modifier : modifiers) {
try {
model = modifier.modifyModelBeforeBake(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify unbaked model before bake", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
private final Event<ModelModifier.AfterBake> afterBakeModifiers = EventFactory.createWithPhases(ModelModifier.AfterBake.class, modifiers -> (model, context) -> {
for (ModelModifier.AfterBake modifier : modifiers) {
try {
model = modifier.modifyModelAfterBake(model, context);
} catch (Exception exception) {
ModernFix.LOGGER.error("Failed to modify baked model after bake", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
public FabricDynamicModelHandler(DynamicModelProvider provider) {
this.pluginList = ModelLoadingPlugin.getAll();
var context = new PluginContext(provider);
for (var plugin : this.pluginList) {
plugin.initialize(context);
}
context.fireResolvers();
}
@Override
public Optional<UnbakedModel> modifyModelOnLoad(Optional<UnbakedModel> model, ResourceLocation id) {
return Optional.ofNullable(this.onLoadModifiers.invoker().modifyModelOnLoad(model.orElse(null), () -> id));
}
@Override
public UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, ModelResourceLocation id, BlockState state) {
return this.onLoadBlockModifiers.invoker().modifyModelOnLoad(model, new ModelModifier.OnLoadBlock.Context() {
@Override
public ModelResourceLocation id() {
return id;
}
@Override
public BlockState state() {
return state;
}
});
}
@Override
public UnbakedModel modifyModelBeforeBake(UnbakedModel model, ResourceLocation id, ModelState state, ModelBaker baker) {
return beforeBakeModifiers.invoker().modifyModelBeforeBake(model, new ModelModifier.BeforeBake.Context() {
@Override
public ResourceLocation id() {
return id;
}
@Override
public ModelState settings() {
return state;
}
@Override
public ModelBaker baker() {
return baker;
}
});
}
@Override
public BakedModel modifyModelAfterBake(BakedModel bakedModel, UnbakedModel model, ResourceLocation id, ModelState state, ModelBaker baker) {
return afterBakeModifiers.invoker().modifyModelAfterBake(bakedModel, new ModelModifier.AfterBake.Context() {
@Override
public ResourceLocation id() {
return id;
}
@Override
public UnbakedModel sourceModel() {
return model;
}
@Override
public ModelState settings() {
return state;
}
@Override
public ModelBaker baker() {
return baker;
}
});
}
@Override
public UnbakedBlockStateModel modifyBlockModelBeforeBake(UnbakedBlockStateModel model, ModelResourceLocation id, ModelBaker baker) {
return beforeBakeBlockModifiers.invoker().modifyModelBeforeBake(model, new ModelModifier.BeforeBakeBlock.Context() {
@Override
public ModelResourceLocation id() {
return id;
}
@Override
public ModelBaker baker() {
return baker;
}
});
}
@Override
public BakedModel modifyBlockModelAfterBake(BakedModel bakedModel, UnbakedBlockStateModel model, ModelResourceLocation id, ModelBaker baker) {
return afterBakeBlockModifiers.invoker().modifyModelAfterBake(bakedModel, new ModelModifier.AfterBakeBlock.Context() {
@Override
public ModelResourceLocation id() {
return id;
}
@Override
public UnbakedBlockStateModel sourceModel() {
return model;
}
@Override
public ModelBaker baker() {
return baker;
}
});
}
private class PluginContext implements ModelLoadingPlugin.Context {
private final DynamicModelProvider provider;
private final Map<Block, BlockStateResolver> resolvers = new HashMap<>();
private PluginContext(DynamicModelProvider provider) {
this.provider = provider;
}
@Override
public void addModels(ResourceLocation... ids) {
/* no-op on dynamic model loader */
}
@Override
public void addModels(Collection<? extends ResourceLocation> ids) {
/* no-op on dynamic model loader */
}
@Override
public void registerBlockStateResolver(Block block, BlockStateResolver resolver) {
resolvers.put(block, resolver);
}
public void fireResolvers() {
resolvers.forEach((block, resolver) -> {
resolver.resolveBlockStates(new BlockStateResolver.Context() {
@Override
public Block block() {
return block;
}
@Override
public void setModel(BlockState state, UnbakedBlockStateModel model) {
provider.addUnbakedBlockStateOverride(BlockModelShaper.stateToModelLocation(state), model);
}
});
});
}
@Override
public Event<ModelModifier.OnLoad> modifyModelOnLoad() {
return onLoadModifiers;
}
@Override
public Event<ModelModifier.OnLoadBlock> modifyBlockModelOnLoad() {
return onLoadBlockModifiers;
}
@Override
public Event<ModelModifier.BeforeBake> modifyModelBeforeBake() {
return beforeBakeModifiers;
}
@Override
public Event<ModelModifier.AfterBake> modifyModelAfterBake() {
return afterBakeModifiers;
}
@Override
public Event<ModelModifier.BeforeBakeBlock> modifyBlockModelBeforeBake() {
return beforeBakeBlockModifiers;
}
@Override
public Event<ModelModifier.AfterBakeBlock> modifyBlockModelAfterBake() {
return afterBakeBlockModifiers;
}
}
}

View File

@ -2,58 +2,15 @@ package org.embeddedt.modernfix.dynamicresources;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import java.util.*;
import java.util.function.BiFunction;
public class ModelBakeryHelpers {
/**
* The maximum number of baked models kept in memory at once.
*/
public static final int MAX_BAKED_MODEL_COUNT = 10000;
/**
* The maximum number of unbaked models kept in memory at once.
*/
public static final int MAX_UNBAKED_MODEL_COUNT = 10000;
/**
* The time in seconds after which a model becomes eligible for eviction if not used.
*/
public static final int MAX_MODEL_LIFETIME_SECS = 300;
/**
* These folders will have all textures stitched onto the atlas when dynamic resources is enabled.
*/
public static String[] getExtraTextureFolders() {
return new String[] {
"attachment",
"bettergrass",
"block",
"blocks",
"cape",
"entity/bed",
"entity/chest",
"item",
"items",
"model",
"models",
"part",
"pipe",
"ropebridge",
"runes",
"solid_block",
"spell_effect",
"spell_projectile"
};
}
private static <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
return state.setValue(prop, (V)o);
}
@ -108,13 +65,4 @@ public class ModelBakeryHelpers {
}
return ImmutableList.copyOf(finalList);
}
public static ModernFixClientIntegration bakedModelWrapper(BiFunction<ResourceLocation, Pair<UnbakedModel, BakedModel>, BakedModel> consumer) {
return new ModernFixClientIntegration() {
@Override
public BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
return consumer.apply(location, Pair.of(baseModel, originalModel));
}
};
}
}

View File

@ -1,8 +0,0 @@
package org.embeddedt.modernfix.dynamicresources;
import net.minecraft.client.renderer.block.model.BlockFaceUV;
public class UVController {
public static final ThreadLocal<Boolean> useDummyUv = ThreadLocal.withInitial(() -> Boolean.FALSE);
public static final BlockFaceUV dummyUv = new BlockFaceUV(new float[4], 0);
}

View File

@ -1,57 +0,0 @@
package org.embeddedt.modernfix.entity;
import com.mojang.datafixers.util.Pair;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EntityDataIDSyncHandler {
private static Map<Class<? extends Entity>, List<Pair<String, Integer>>> fieldsToSyncMap;
@SuppressWarnings("unchecked")
public static void onDatapackSyncEvent(ServerPlayer targetPlayer) {
if(targetPlayer != null) {
/* Compute the current set of serializer IDs in use and send them */
if(fieldsToSyncMap == null) {
fieldsToSyncMap = new HashMap<>();
Map<Class<? extends Entity>, Integer> entityPoolMap = SynchedEntityData.ENTITY_ID_POOL;
List<Field> fieldsToSync = new ArrayList<>();
for(Class<? extends Entity> eClass : entityPoolMap.keySet()) {
fieldsToSync.clear();
try {
Field[] classFields = eClass.getDeclaredFields();
for(Field field : classFields) {
if(!Modifier.isStatic(field.getModifiers()))
continue;
field.setAccessible(true);
Object o = field.get(null);
if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) {
fieldsToSync.add(field);
}
}
for(Field field : fieldsToSync) {
int id = ((EntityDataAccessor<?>)field.get(null)).id;
fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id));
}
} catch(Throwable e) {
ModernFix.LOGGER.error("Skipping entity ID sync for {}: {}", eClass.getName(), e);
}
}
}
EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap);
ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes");
ModernFixPlatformHooks.INSTANCE.sendPacket(targetPlayer, packet);
}
}
}

View File

@ -17,9 +17,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>> {
public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?, ?>> {
private final Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders;
private final LoadingCache<EntityType<?>, EntityRenderer<?>> rendererMap;
private final LoadingCache<EntityType<?>, EntityRenderer<?, ?>> rendererMap;
private final EntityRendererProvider.Context context;
public EntityRendererMap(Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders, EntityRendererProvider.Context context) {
@ -28,12 +28,12 @@ public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>>
this.rendererMap = CacheBuilder.newBuilder().build(new RenderConstructor());
}
class RenderConstructor extends CacheLoader<EntityType<?>, EntityRenderer<?>> {
class RenderConstructor extends CacheLoader<EntityType<?>, EntityRenderer<?, ?>> {
@Override
public EntityRenderer<?> load(EntityType<?> key) throws Exception {
public EntityRenderer<?, ?> load(EntityType<?> key) throws Exception {
EntityRendererProvider<?> provider = rendererProviders.get(key);
synchronized(EntityRenderers.class) {
EntityRenderer<?> renderer;
EntityRenderer<?, ?> renderer;
try {
if(provider == null)
throw new RuntimeException("Provider not registered");
@ -69,9 +69,9 @@ public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>>
}
@Override
public EntityRenderer<?> get(Object o) {
public EntityRenderer<?, ?> get(Object o) {
try {
EntityRenderer<?> renderer = rendererMap.get((EntityType<?>)o);
EntityRenderer<?, ?> renderer = rendererMap.get((EntityType<?>)o);
if(renderer == null)
throw new AssertionError("Returned entity renderer should never be null");
return renderer;
@ -84,21 +84,21 @@ public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>>
@Nullable
@Override
public EntityRenderer<?> put(EntityType<?> entityType, EntityRenderer<?> entityRenderer) {
EntityRenderer<?> old = rendererMap.getIfPresent(entityType);
public EntityRenderer<?, ?> put(EntityType<?> entityType, EntityRenderer<?, ?> entityRenderer) {
EntityRenderer<?, ?> old = rendererMap.getIfPresent(entityType);
rendererMap.put(entityType, entityRenderer);
return old;
}
@Override
public EntityRenderer<?> remove(Object o) {
EntityRenderer<?> r = rendererMap.getIfPresent(o);
public EntityRenderer<?, ?> remove(Object o) {
EntityRenderer<?, ?> r = rendererMap.getIfPresent(o);
rendererMap.invalidate(o);
return r;
}
@Override
public void putAll(@NotNull Map<? extends EntityType<?>, ? extends EntityRenderer<?>> map) {
public void putAll(@NotNull Map<? extends EntityType<?>, ? extends EntityRenderer<?, ?>> map) {
rendererMap.putAll(map);
}
@ -115,13 +115,13 @@ public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>>
@NotNull
@Override
public Collection<EntityRenderer<?>> values() {
public Collection<EntityRenderer<?, ?>> values() {
return rendererMap.asMap().values();
}
@NotNull
@Override
public Set<Map.Entry<EntityType<?>, EntityRenderer<?>>> entrySet() {
public Set<Map.Entry<EntityType<?>, EntityRenderer<?, ?>>> entrySet() {
return rendererMap.asMap().entrySet();
}
}

View File

@ -5,26 +5,25 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;
public class ErroredEntityRenderer<T extends Entity> extends EntityRenderer<T> {
public class ErroredEntityRenderer<T extends Entity> extends EntityRenderer<T, EntityRenderState> {
public ErroredEntityRenderer(EntityRendererProvider.Context arg) {
super(arg);
}
@Override
public ResourceLocation getTextureLocation(T entity) {
return TextureAtlas.LOCATION_BLOCKS;
}
@Override
public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
return false;
}
@Override
public void render(T entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight) {
public EntityRenderState createRenderState() {
return null;
}
@Override
public void render(EntityRenderState entityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) {
}
}

View File

@ -1,78 +0,0 @@
package org.embeddedt.modernfix.packet;
import com.mojang.datafixers.util.Pair;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFix;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
public class EntityIDSyncPacket {
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
this.map = map;
}
public Map<Class<? extends Entity>, List<Pair<String, Integer>>> getFieldInfo() {
return this.map;
}
public EntityIDSyncPacket() {
this.map = new HashMap<>();
}
public void serialize(FriendlyByteBuf buf) {
buf.writeVarInt(map.keySet().size());
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : map.entrySet()) {
buf.writeUtf(entry.getKey().getName());
buf.writeVarInt(entry.getValue().size());
for(Pair<String, Integer> field : entry.getValue()) {
buf.writeUtf(field.getFirst());
buf.writeVarInt(field.getSecond());
}
}
}
@SuppressWarnings("unchecked")
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
EntityIDSyncPacket self = new EntityIDSyncPacket();
int numEntityClasses = buf.readVarInt();
for(int i = 0; i < numEntityClasses; i++) {
String clzName = buf.readUtf();
try {
Class<?> clz;
try {
clz = Class.forName(clzName);
} catch(ClassNotFoundException e) {
ModernFix.LOGGER.warn("Entity class not found: {}", clzName);
break;
}
if(!Entity.class.isAssignableFrom(clz)) {
ModernFix.LOGGER.error("Not an entity: " + clzName);
break;
}
int numFields = buf.readVarInt();
for(int j = 0; j < numFields; j++) {
String fieldName = buf.readUtf();
int id = buf.readVarInt();
Field f = clz.getDeclaredField(fieldName);
if(!Modifier.isStatic(f.getModifiers()))
continue;
f.setAccessible(true);
if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) {
ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName);
continue;
}
self.map.computeIfAbsent((Class<? extends Entity>)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id));
}
} catch(ReflectiveOperationException e) {
ModernFix.LOGGER.error("Error deserializing packet", e);
}
}
return self;
}
}

View File

@ -2,16 +2,13 @@ package org.embeddedt.modernfix.platform;
import com.google.common.collect.Multimap;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import org.objectweb.asm.tree.ClassNode;
import java.nio.file.Path;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public interface ModernFixPlatformHooks {
@ -39,7 +36,7 @@ public interface ModernFixPlatformHooks {
Path getGameDirectory();
void sendPacket(ServerPlayer player, Object packet);
void sendPacket(ServerPlayer player, CustomPacketPayload packet);
Multimap<String, String> getCustomModOptions();
@ -47,7 +44,5 @@ public interface ModernFixPlatformHooks {
void onLaunchComplete();
void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator);
String getPlatformName();
}

View File

@ -4,7 +4,7 @@ import java.lang.reflect.Constructor;
class PlatformHookLoader {
static ModernFixPlatformHooks findInstance() {
String[] locations = new String[] { "forge", "fabric" };
String[] locations = new String[] { "neoforge", "fabric" };
for(String location : locations) {
try {
Class<?> clz = Class.forName("org.embeddedt.modernfix.platform." + location + ".ModernFixPlatformHooksImpl");

View File

@ -1,15 +1,16 @@
package org.embeddedt.modernfix.registry;
import com.mojang.serialization.Lifecycle;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.resources.ResourceKey;
public class LifecycleMap<T> extends Reference2ReferenceOpenHashMap<T, Lifecycle> {
public class LifecycleMap<T> extends Reference2ReferenceOpenHashMap<ResourceKey<T>, RegistrationInfo> {
public LifecycleMap() {
this.defaultReturnValue(Lifecycle.stable());
this.defaultReturnValue(RegistrationInfo.BUILT_IN);
}
@Override
public Lifecycle put(T t, Lifecycle lifecycle) {
public RegistrationInfo put(ResourceKey<T> t, RegistrationInfo lifecycle) {
if(lifecycle != defRetValue)
return super.put(t, lifecycle);
else {

View File

@ -2,7 +2,6 @@ package org.embeddedt.modernfix.render;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BakedModel;
@ -72,11 +71,6 @@ public class SimpleItemModelView implements BakedModel {
return wrappedItem.usesBlockLight();
}
@Override
public boolean isCustomRenderer() {
return wrappedItem.isCustomRenderer();
}
@Override
public TextureAtlasSprite getParticleIcon() {
return wrappedItem.getParticleIcon();
@ -86,9 +80,5 @@ public class SimpleItemModelView implements BakedModel {
public ItemTransforms getTransforms() {
return wrappedItem.getTransforms();
}
@Override
public ItemOverrides getOverrides() {
return wrappedItem.getOverrides();
}
}

View File

@ -1,51 +0,0 @@
package org.embeddedt.modernfix.render;
import org.embeddedt.modernfix.ModernFix;
import org.lwjgl.system.MemoryUtil;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
/**
* Helper that frees ByteBuffers allocated by BufferBuilders, and nulls out the address pointer
* to prevent double frees.
*
* @author Moulberry
*/
public class UnsafeBufferHelper {
private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false);
private static sun.misc.Unsafe UNSAFE = null;
private static long ADDRESS = -1;
static {
try {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe)theUnsafe.get(null);
final Field addressField = MemoryUtil.class.getDeclaredField("ADDRESS");
addressField.setAccessible(true);
ADDRESS = addressField.getLong(null);
} catch(Throwable t) {
ModernFix.LOGGER.error("Could load unsafe/buffer address", t);
}
}
public static void init() {
}
public static void free(ByteBuffer buf) {
if(UNSAFE != null && ADDRESS >= 0) {
// set the address to 0 to prevent double free
long address = UNSAFE.getAndSetLong(buf, ADDRESS, 0);
if(address != 0) {
ALLOCATOR.free(address);
}
} else {
ALLOCATOR.free(MemoryUtil.memAddress0(buf));
}
}
}

View File

@ -158,7 +158,7 @@ public class PackResourcesCacheEngine {
if(!fullTestPath.startsWith(testPath)) {
continue;
}
ResourceLocation foundResource = new ResourceLocation(resourceNamespace, fullPath);
ResourceLocation foundResource = ResourceLocation.fromNamespaceAndPath(resourceNamespace, fullPath);
if(!filter.test(foundResource))
continue;
resources.add(foundResource);

View File

@ -1,20 +1,21 @@
package org.embeddedt.modernfix.resources;
import net.minecraft.ReportType;
import net.minecraft.ReportedException;
import net.minecraft.TracingExecutor;
import net.minecraft.server.Bootstrap;
import org.embeddedt.modernfix.ModernFix;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.atomic.AtomicInteger;
public class ReloadExecutor {
public static ExecutorService createCustomResourceReloadExecutor() {
public static TracingExecutor createCustomResourceReloadExecutor() {
ClassLoader loader = ReloadExecutor.class.getClassLoader();
AtomicInteger workerCount = new AtomicInteger(0);
return new ForkJoinPool(ForkJoinPool.getCommonPoolParallelism(), (forkJoinPool) -> {
return new TracingExecutor(new ForkJoinPool(ForkJoinPool.getCommonPoolParallelism(), (forkJoinPool) -> {
ForkJoinWorkerThread forkJoinWorkerThread = new ForkJoinWorkerThread(forkJoinPool) {
protected void onTermination(Throwable throwOnTermination) {
if (throwOnTermination != null) {
@ -30,7 +31,7 @@ public class ReloadExecutor {
forkJoinWorkerThread.setContextClassLoader(loader);
forkJoinWorkerThread.setName("Worker-ResourceReload-" + workerCount.getAndIncrement());
return forkJoinWorkerThread;
}, ReloadExecutor::handleException, true);
}, ReloadExecutor::handleException, true));
}
private static void handleException(Thread thread, Throwable throwable) {
@ -39,7 +40,7 @@ public class ReloadExecutor {
}
if (throwable instanceof ReportedException) {
Bootstrap.realStdoutPrintln(((ReportedException)throwable).getReport().getFriendlyReport());
Bootstrap.realStdoutPrintln(((ReportedException)throwable).getReport().getFriendlyReport(ReportType.CRASH));
System.exit(-1);
}

View File

@ -42,11 +42,10 @@ public class ModernFixConfigScreen extends Screen {
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(guiGraphics);
super.render(guiGraphics, mouseX, mouseY, partialTicks);
this.optionList.render(guiGraphics, mouseX, mouseY, partialTicks);
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
this.doneButton.setMessage(madeChanges ? Component.translatable("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
super.render(guiGraphics, mouseX, mouseY, partialTicks);
}
public void setLastScrollAmount(double d) {

View File

@ -41,9 +41,8 @@ public class ModernFixOptionInfoScreen extends Screen {
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(guiGraphics);
super.render(guiGraphics, mouseX, mouseY, partialTicks);
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
this.drawMultilineString(guiGraphics, this.minecraft.font, description, 10, 50);
super.render(guiGraphics, mouseX, mouseY, partialTicks);
}
}

View File

@ -69,7 +69,7 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
}
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
super(arg2,arg.width + 45, arg.height, 43, arg.height - 32, 20);
super(arg2,arg.width + 45, arg.height - 52, 20, 20);
this.mainScreen = arg;
@ -91,8 +91,9 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
}
}
protected int getScrollbarPosition() {
return super.getScrollbarPosition() + 15 + 20;
@Override
protected int scrollBarY() {
return super.scrollBarY() + 15 + 20;
}
public int getRowWidth() {
@ -166,7 +167,7 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
}).tooltip(toggleTooltip).pos(0, 0).size(55, 20).build();
updateStatus();
this.helpButton = new Button.Builder(Component.literal("?"), (arg) -> {
mainScreen.setLastScrollAmount(getScrollAmount());
mainScreen.setLastScrollAmount(scrollAmount());
Minecraft.getInstance().setScreen(new ModernFixOptionInfoScreen(mainScreen, optionName));
}).pos(75, 0).size(20, 20).build();
if(!I18n.exists("modernfix.option." + optionName)) {

View File

@ -1,44 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import net.minecraft.client.searchtree.RefreshableSearchTree;
import net.minecraft.world.item.ItemStack;
import java.util.Collections;
import java.util.List;
/**
* Dummy search tree that stores nothing and returns nothing on searches.
*/
public class DummySearchTree<T> implements RefreshableSearchTree<T> {
public DummySearchTree() {
super();
}
@Override
public void refresh() {
}
@Override
public List<T> search(String pSearchText) {
return Collections.emptyList();
}
static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
@Override
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
return new DummySearchTree<>();
}
@Override
public boolean canUse() {
return true;
}
@Override
public String getName() {
return "Dummy";
}
};
}

View File

@ -1,109 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.gui.ingredients.IngredientFilter;
import mezz.jei.gui.ingredients.IngredientFilterApi;
import mezz.jei.library.runtime.JeiRuntime;
import net.minecraft.client.searchtree.RefreshableSearchTree;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Uses JEI to handle search tree lookups.
*/
public class JEIBackedSearchTree extends DummySearchTree<ItemStack> {
private final boolean filteringByTag;
private String lastSearchText = "";
private final List<ItemStack> listCache = new ArrayList<>();
private static final Field filterField;
private static final MethodHandle getIngredientListUncached;
static {
MethodHandle m;
Field f;
try {
Method jeiMethod = IngredientFilter.class.getDeclaredMethod("getIngredientListUncached", String.class);
jeiMethod.setAccessible(true);
m = MethodHandles.lookup().unreflect(jeiMethod);
f = IngredientFilterApi.class.getDeclaredField("ingredientFilter");
f.setAccessible(true);
} catch(ReflectiveOperationException | RuntimeException | NoClassDefFoundError e) {
m = null;
f = null;
}
getIngredientListUncached = m;
filterField = f;
}
public JEIBackedSearchTree(boolean filteringByTag) {
this.filteringByTag = filteringByTag;
}
@Override
public List<ItemStack> search(String pSearchText) {
Optional<JeiRuntime> runtime = JEIRuntimeCapturer.runtime();
if(runtime.isPresent()) {
IngredientFilterApi iFilterApi = (IngredientFilterApi)runtime.get().getIngredientFilter();
IngredientFilter filter;
try {
filter = (IngredientFilter)filterField.get(iFilterApi);
} catch(ReflectiveOperationException e) {
ModernFix.LOGGER.error(e);
return Collections.emptyList();
}
return this.searchJEI(filter, pSearchText);
} else {
/* Use the default, dummy implementation */
return super.search(pSearchText);
}
}
private List<ItemStack> searchJEI(IngredientFilter filter, String pSearchText) {
if(!pSearchText.equals(lastSearchText)) {
listCache.clear();
Stream<ITypedIngredient<?>> ingredients;
String finalSearchTerm = filteringByTag ? ("$" + pSearchText) : pSearchText;
try {
ingredients = (Stream<ITypedIngredient<?>>)getIngredientListUncached.invokeExact(filter, finalSearchTerm);
} catch(Throwable e) {
ModernFix.LOGGER.error("Error searching", e);
ingredients = Stream.empty();
}
ingredients.toList().forEach(ingredient -> {
if(ingredient.getIngredient() instanceof ItemStack) {
listCache.add((ItemStack)ingredient.getIngredient());
}
});
lastSearchText = pSearchText;
}
return listCache;
}
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
@Override
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
return new JEIBackedSearchTree(tag);
}
@Override
public boolean canUse() {
return ModernFixPlatformHooks.INSTANCE.modPresent("jei") && !ModernFixPlatformHooks.INSTANCE.modPresent("emi") && getIngredientListUncached != null && filterField != null;
}
@Override
public String getName() {
return "JEI";
}
};
}

View File

@ -1,34 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.runtime.IJeiRuntime;
import mezz.jei.library.runtime.JeiRuntime;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.ModernFix;
import java.util.Optional;
@JeiPlugin
public class JEIRuntimeCapturer implements IModPlugin {
private static JeiRuntime runtimeHandle = null;
public static Optional<JeiRuntime> runtime() {
return Optional.ofNullable(runtimeHandle);
}
@Override
public ResourceLocation getPluginUid() {
return new ResourceLocation(ModernFix.MODID, "capturer");
}
@Override
public void onRuntimeAvailable(IJeiRuntime jeiRuntime) {
runtimeHandle = (JeiRuntime)jeiRuntime;
}
@Override
public void onRuntimeUnavailable() {
runtimeHandle = null;
}
}

Some files were not shown because too many files have changed in this diff Show More