Compare commits

...

301 Commits

Author SHA1 Message Date
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
187 changed files with 1258 additions and 5469 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,8 @@ 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}")
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
}

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,7 +32,7 @@ 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;
}

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,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,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,6 +1,6 @@
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
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;
@ -11,7 +11,7 @@ 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<>());

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");
@ -41,24 +40,24 @@ public abstract class ServerChunkCacheMixin {
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(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

@ -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

@ -48,8 +48,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,16 +1,14 @@
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.util.blockpos.SectionBlockPosIterator;
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
public class RebuildTaskMixin {
/**
@ -21,13 +19,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

@ -12,7 +12,7 @@ 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))
@Redirect(method = { "<init>", "reloadResourcePacks(ZLnet/minecraft/client/Minecraft$GameLoadCookie;)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
private ExecutorService 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;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 5)
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

@ -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()) {

View File

@ -0,0 +1,78 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.core.DefaultedRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.profiling.ProfilerFiller;
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.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IBlockStateModelLoader;
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiConsumer;
@Mixin(BlockStateModelLoader.class)
@ClientOnlyMixin
public abstract class BlockStateModelLoaderMixin implements IBlockStateModelLoader {
@Shadow protected abstract void loadBlockStateDefinitions(ResourceLocation resourceLocation, StateDefinition<Block, BlockState> stateDefinition);
@Shadow @Mutable @Final private Object2IntMap<BlockState> modelGroups;
private ImmutableList<BlockState> filteredStates;
@Inject(method = "<init>", at = @At("RETURN"))
private void makeModelGroupsSynchronized(Map map, ProfilerFiller profilerFiller, UnbakedModel unbakedModel, BlockColors blockColors, BiConsumer biConsumer, CallbackInfo ci) {
this.modelGroups = Object2IntMaps.synchronize(this.modelGroups);
}
@Override
public void loadSpecificBlock(ModelResourceLocation location) {
var optionalBlock = BuiltInRegistries.BLOCK.getOptional(location.id());
if(optionalBlock.isPresent()) {
try {
// Only filter states if we are not in the loading overlay
filteredStates = Minecraft.getInstance().getOverlay() == null ? ModelBakeryHelpers.getBlockStatesForMRL(optionalBlock.get().getStateDefinition(), location) : null;
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception filtering states on {}", location, e);
filteredStates = null;
}
try {
this.loadBlockStateDefinitions(location.id(), optionalBlock.get().getStateDefinition());
} finally {
filteredStates = null;
}
}
}
@Redirect(method = "loadAllBlockStates", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;iterator()Ljava/util/Iterator;"))
private Iterator<?> skipIteratingBlocks(DefaultedRegistry instance) {
return Collections.emptyIterator();
}
@Redirect(method = "loadBlockStateDefinitions", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
private ImmutableList<BlockState> getFilteredStates(StateDefinition<Block, BlockState> instance) {
return this.filteredStates != null ? this.filteredStates : instance.getPossibleStates();
}
}

View File

@ -33,7 +33,7 @@ public abstract class ItemModelShaperMixin {
super();
}
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel");
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath("modernfix", "sentinel"), "sentinel");
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);

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

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.ModelManager;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IExtendedModelManager;
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(Minecraft.class)
@ClientOnlyMixin
public abstract class MinecraftMixin_ModelTicking {
@Shadow public abstract ModelManager getModelManager();
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/Tutorial;tick()V"))
private void tickModels(CallbackInfo ci) {
((IExtendedModelManager)this.getModelManager()).mfix$tick();
}
}

View File

@ -0,0 +1,233 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.client.color.block.BlockColors;
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.ModelBakery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.core.DefaultedRegistry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.profiling.ProfilerFiller;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.duck.IBlockStateModelLoader;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.util.DynamicOverridableMap;
import org.embeddedt.modernfix.util.LRUMap;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
@Mixin(ModelBakery.class)
@ClientOnlyMixin
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@Unique
private BlockStateModelLoader dynamicLoader;
@Unique
private final ReentrantLock modelBakeryLock = new ReentrantLock();
@Unique
private ModelBakery.TextureGetter textureGetter;
@Unique
private BakedModel bakedMissingModel;
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
@Shadow @Final private UnbakedModel missingModel;
@Unique
private static final boolean DEBUG_MODEL_LOADS = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
/**
* Bake a model using the provided texture getter and location. The model is stored in {@link ModelBakeryMixin#bakedTopLevelModels}.
*/
@Shadow(aliases = "lambda$bakeModels$6") protected abstract void method_61072(ModelBakery.TextureGetter getter, ModelResourceLocation location, UnbakedModel model);
@Shadow @Mutable @Final private Map<ModelResourceLocation, BakedModel> bakedTopLevelModels;
@Shadow @Mutable @Final public Map<ModelResourceLocation, UnbakedModel> topLevelModels;
@Shadow @Mutable @Final private Map<ResourceLocation, UnbakedModel> unbakedCache;
@Shadow @Mutable @Final public Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_VARIANT;
private final Map<ModelResourceLocation, BakedModel> mfix$emulatedBakedRegistry = new DynamicOverridableMap<>(ModelResourceLocation.class, this::loadBakedModelDynamic);
@Override
public UnbakedModel mfix$loadUnbakedModelDynamic(ModelResourceLocation location) {
if(location.equals(MISSING_MODEL_VARIANT)) {
return missingModel;
}
modelBakeryLock.lock();
try {
UnbakedModel existing = this.topLevelModels.get(location);
if (existing != null) {
return existing;
}
if(DEBUG_MODEL_LOADS) {
ModernFix.LOGGER.info("Loading model {}", location);
}
if(location.variant().equals("inventory")) {
this.loadItemModelAndDependencies(location.id());
} else {
((IBlockStateModelLoader)dynamicLoader).loadSpecificBlock(location);
}
return this.topLevelModels.getOrDefault(location, this.missingModel);
} finally {
modelBakeryLock.unlock();
}
}
@Override
public UnbakedModel mfix$getMissingModel() {
return missingModel;
}
@Unique
private BakedModel loadBakedModelDynamic(ModelResourceLocation location) {
if(location.equals(MISSING_MODEL_VARIANT)) {
return bakedMissingModel;
}
BakedModel model;
modelBakeryLock.lock();
try {
model = bakedTopLevelModels.get(location);
if(model == null) {
UnbakedModel prototype = mfix$loadUnbakedModelDynamic(location);
if(prototype == missingModel) {
model = bakedMissingModel;
} else {
prototype.resolveParents(this::getModel);
if(DEBUG_MODEL_LOADS) {
ModernFix.LOGGER.info("Baking model {}", location);
}
this.method_61072(this.textureGetter, location, prototype);
model = bakedTopLevelModels.remove(location);
if(model == null) {
ModernFix.LOGGER.error("Failed to load model " + location);
model = bakedMissingModel;
}
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
model = integration.onBakedModelLoad(location, prototype, model, BlockModelRotation.X0_Y0, (ModelBakery)(Object)this, this.textureGetter);
}
}
}
} finally {
modelBakeryLock.unlock();
}
return model;
}
@ModifyExpressionValue(method = "<init>", at = @At(value = "CONSTANT", args = "stringValue=missing_model"))
private String replaceBackingMaps(String original) {
this.unbakedCache = new LRUMap<>(this.unbakedCache);
this.bakedCache = new LRUMap<>(this.bakedCache);
this.topLevelModels = new LRUMap<>(this.topLevelModels);
this.bakedTopLevelModels = new LRUMap<>(this.bakedTopLevelModels);
return original;
}
@WrapOperation(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadAllBlockStates()V"))
private void noInitialBlockStateLoad(BlockStateModelLoader instance, Operation<Void> original) {
dynamicLoader = instance;
original.call(instance);
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;keySet()Ljava/util/Set;"))
private Set<?> skipLoadingItems(DefaultedRegistry instance) {
return Collections.emptySet();
}
@Inject(method = "bakeModels", at = @At("HEAD"))
private void storeTextureGetterAndBakeMissing(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) {
this.textureGetter = textureGetter;
this.method_61072(textureGetter, MISSING_MODEL_VARIANT, Objects.requireNonNull(this.topLevelModels.get(MISSING_MODEL_VARIANT)));
this.bakedMissingModel = this.bakedTopLevelModels.get(MISSING_MODEL_VARIANT);
}
private boolean inInitialLoad = true;
@Inject(method = "bakeModels", at = @At("RETURN"))
private void onInitialBakeFinish(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) {
var permanentMRLs = new ObjectOpenHashSet<>(this.bakedTopLevelModels.keySet());
((LRUMap<ModelResourceLocation, BakedModel>)this.bakedTopLevelModels).setPermanentEntries(permanentMRLs);
ModernFix.LOGGER.info("Dynamic model bakery initial baking finished, with {} permanent top level baked models", this.bakedTopLevelModels.size());
}
@Inject(method = "<init>", at = @At("RETURN"))
private void onInitialLoadFinish(BlockColors blockColors, ProfilerFiller profilerFiller, Map map, Map map2, CallbackInfo ci) {
var permanentMRLs = new ObjectOpenHashSet<>(this.topLevelModels.keySet());
((LRUMap<ModelResourceLocation, UnbakedModel>)this.topLevelModels).setPermanentEntries(permanentMRLs);
ModernFix.LOGGER.info("Dynamic model bakery loading finished, with {} permanent top level models", this.topLevelModels.size());
}
@Unique
private int tickCount;
@Unique
private static final int MAXIMUM_CACHE_SIZE = 1000;
private void runCleanup() {
((LRUMap<?, ?>)this.unbakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
((LRUMap<?, ?>)this.bakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
((LRUMap<?, ?>)this.topLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
((LRUMap<?, ?>)this.bakedTopLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
}
@Override
public void mfix$finishLoading() {
inInitialLoad = false;
}
@Override
public void mfix$tick() {
if(inInitialLoad) {
return;
}
tickCount++;
if((tickCount % 200) == 0) {
if(modelBakeryLock.tryLock()) {
try {
runCleanup();
} finally {
modelBakeryLock.unlock();
}
}
}
}
/**
* @author embeddedt
* @reason We provide a fake baked registry to the rest of Minecraft, that dynamically loads models.
*/
@Overwrite
public Map<ModelResourceLocation, BakedModel> getBakedTopLevelModels() {
return this.mfix$emulatedBakedRegistry;
}
}

View File

@ -1,26 +1,35 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.google.common.collect.ImmutableList;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.resources.model.AtlasSet;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.profiling.ProfilerFiller;
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.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.duck.IExtendedModelManager;
import org.embeddedt.modernfix.util.CacheUtil;
import org.embeddedt.modernfix.util.LambdaMap;
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.Coerce;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.io.BufferedReader;
import java.io.IOException;
@ -34,9 +43,12 @@ import java.util.stream.Collectors;
@Mixin(ModelManager.class)
@ClientOnlyMixin
public class ModelManagerMixin {
public class ModelManagerMixin implements IExtendedModelManager {
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
@Unique
private Runnable tickHandler = () -> {};
@Inject(method = "<init>", at = @At("RETURN"))
private void injectDummyBakedRegistry(CallbackInfo ci) {
if(this.bakedRegistry == null) {
@ -51,8 +63,8 @@ public class ModelManagerMixin {
}
@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);
private CompletableFuture<Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
var cache = CacheUtil.<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
}
@ -72,14 +84,29 @@ public class ModelManagerMixin {
}).orElse(null);
}
private List<ModelBakery.LoadedJson> loadSingleBlockState(ResourceManager manager, ResourceLocation location) {
private List<BlockStateModelLoader.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));
return new BlockStateModelLoader.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());
}
@Inject(method = "loadModels", at = @At("RETURN"))
private void storeTicker(ProfilerFiller profilerFiller, Map<ResourceLocation, AtlasSet.StitchResult> map, ModelBakery modelBakery, CallbackInfoReturnable<?> cir) {
tickHandler = ((IExtendedModelBakery)modelBakery)::mfix$tick;
}
@Inject(method = "apply", at = @At("RETURN"))
private void freezeBakery(@Coerce Object reloadState, ProfilerFiller profilerFiller, CallbackInfo ci, @Local(ordinal = 0) ModelBakery bakery) {
((IExtendedModelBakery)bakery).mfix$finishLoading();
}
@Override
public void mfix$tick() {
tickHandler.run();
}
}

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

@ -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,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

@ -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

@ -0,0 +1,15 @@
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(BlockBehaviour.class)
public interface BlockBehaviourInvoker {
@Invoker
FluidState invokeGetFluidState(BlockState blockState);
@Invoker
boolean invokeIsRandomlyTicking(BlockState blockState);
}

View File

@ -1,7 +1,7 @@
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.MapCodec;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
@ -17,9 +17,10 @@ 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) {
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> immutableMap, MapCodec<BlockState> mapCodec) {
super(object, immutableMap, mapCodec);
}
@ -93,7 +94,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
if(!buildingCache) {
buildingCache = true;
try {
this.fluidState = this.owner.getFluidState(this.asState());
this.fluidState = ((BlockBehaviourInvoker)this.owner).invokeGetFluidState(this.asState());
} finally {
buildingCache = false;
}
@ -112,7 +113,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
))
private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base) {
if(this.cacheInvalid)
return this.owner.isRandomlyTicking(this.asState());
return ((BlockBehaviourInvoker)this.owner).invokeIsRandomlyTicking(this.asState());
return this.isRandomlyTicking;
}

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

@ -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)
@ -181,9 +180,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,240 +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) {
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

@ -2,17 +2,13 @@ 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 {
/**
@ -108,13 +104,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,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

@ -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

@ -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,5 +1,6 @@
package org.embeddedt.modernfix.resources;
import net.minecraft.ReportType;
import net.minecraft.ReportedException;
import net.minecraft.server.Bootstrap;
import org.embeddedt.modernfix.ModernFix;
@ -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;

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;
}
}

View File

@ -1,150 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import com.google.common.base.Predicates;
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
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.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
private final AsyncSearchManager searchManager = createSearchManager();
private final boolean filteringByTag;
private String lastSearchText = "";
private final List<ItemStack> listCache = new ArrayList<>();
public REIBackedSearchTree(boolean filteringByTag) {
this.filteringByTag = filteringByTag;
}
@Override
public List<ItemStack> search(String pSearchText) {
if(true) {
return this.searchREI(pSearchText);
} else {
/* Use the default, dummy implementation */
return super.search(pSearchText);
}
}
private List<ItemStack> searchREI(String pSearchText) {
if(!pSearchText.equals(lastSearchText)) {
listCache.clear();
this.searchManager.updateFilter(pSearchText);
List stacks;
try {
stacks = this.searchManager.getNow();
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
stacks = Collections.emptyList();
}
for(Object o : stacks) {
EntryStack<?> stack;
if(o instanceof EntryStack<?>)
stack = (EntryStack<?>)o;
else if(o instanceof HashedEntryStackWrapper) {
stack = ((HashedEntryStackWrapper)o).unwrap();
} else {
ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName());
continue;
}
if(stack.getType() == VanillaEntryTypes.ITEM) {
listCache.add(stack.cheatsAs().getValue());
}
}
lastSearchText = pSearchText;
}
return listCache;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static AsyncSearchManager createSearchManager() {
Method m, normalizeMethod;
try {
try {
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList");
m.setAccessible(true);
normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize");
normalizeMethod.setAccessible(true);
} catch(NoSuchMethodException e) {
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList");
m.setAccessible(true);
normalizeMethod = EntryStack.class.getDeclaredMethod("normalize");
normalizeMethod.setAccessible(true);
}
final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m);
final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod);
final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance();
Supplier stackListSupplier = () -> {
try {
return (List)getListMethod.invokeExact(registry);
} catch(Throwable e) {
if(e instanceof RuntimeException)
throw (RuntimeException)e;
throw new RuntimeException(e);
}
};
UnaryOperator normalizeOperator = o -> {
try {
return normalize.invoke(o);
} catch(Throwable e) {
if(e instanceof RuntimeException)
throw (RuntimeException)e;
throw new RuntimeException(e);
}
};
Supplier<Predicate<Boolean>> shouldShowStack = () -> {
return Predicates.alwaysTrue();
};
try {
try {
// Old constructor taking Supplier as first arg
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Supplier.class, Supplier.class, UnaryOperator.class));
return (AsyncSearchManager)cn.invoke(stackListSupplier, shouldShowStack, normalizeOperator);
} catch(NoSuchMethodException e) {
// New constructor taking Function as first arg
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Function.class, Supplier.class, UnaryOperator.class));
return (AsyncSearchManager)cn.invoke((Function<?, ?>)o -> stackListSupplier.get(), shouldShowStack, normalizeOperator);
}
} catch(Throwable mhThrowable) {
throw new ReflectiveOperationException(mhThrowable);
}
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
@Override
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
return new REIBackedSearchTree(tag);
}
@Override
public boolean canUse() {
return ModernFixPlatformHooks.INSTANCE.modPresent("roughlyenoughitems");
}
@Override
public String getName() {
return "REI";
}
};
}

View File

@ -1,55 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.client.searchtree.SearchTree;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class RecipeBookSearchTree extends DummySearchTree<RecipeCollection> {
private final SearchTree<ItemStack> stackCollector;
private Map<Item, List<RecipeCollection>> collectionsByItem = null;
private final List<RecipeCollection> allCollections;
public RecipeBookSearchTree(SearchTree<ItemStack> stackCollector, List<RecipeCollection> allCollections) {
this.stackCollector = stackCollector;
this.allCollections = allCollections;
}
private Map<Item, List<RecipeCollection>> populateCollectionMap() {
Map<Item, List<RecipeCollection>> collections = this.collectionsByItem;
if(collections == null) {
collections = new Object2ObjectOpenHashMap<>();
Map<Item, List<RecipeCollection>> finalCollection = collections;
for(RecipeCollection collection : allCollections) {
collection.getRecipes().stream().map(recipe -> recipe.getResultItem(collection.registryAccess()).getItem()).distinct().forEach(item -> {
finalCollection.computeIfAbsent(item, k -> new ArrayList<>()).add(collection);
});
}
this.collectionsByItem = collections;
}
return collections;
}
@Override
public void refresh() {
this.collectionsByItem = null;
}
@Override
public List<RecipeCollection> search(String pSearchText) {
// Avoid constructing the recipe collection map until the first real search
if(pSearchText.trim().length() == 0) {
return this.allCollections;
}
List<ItemStack> stacks = stackCollector.search(pSearchText);
Map<Item, List<RecipeCollection>> collections = this.populateCollectionMap();
return stacks.stream().map(ItemStack::getItem).distinct().flatMap(item -> collections.getOrDefault(item, Collections.emptyList()).stream()).collect(Collectors.toList());
}
}

View File

@ -1,34 +0,0 @@
package org.embeddedt.modernfix.searchtree;
import net.minecraft.client.searchtree.RefreshableSearchTree;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import java.util.ArrayList;
import java.util.List;
public class SearchTreeProviderRegistry {
private static final List<Provider> searchTreeProviders = new ArrayList<>();
public static synchronized Provider getSearchTreeProvider() {
for(Provider p : searchTreeProviders) {
if(p.canUse())
return p;
}
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.blast_search_trees.force.Registry"))
return DummySearchTree.PROVIDER;
else
return null;
}
public static synchronized void register(Provider p) {
if(p.canUse())
searchTreeProviders.add(p);
}
public interface Provider {
RefreshableSearchTree<ItemStack> getSearchTree(boolean tag);
boolean canUse();
String getName();
}
}

View File

@ -1,133 +0,0 @@
package org.embeddedt.modernfix.structure;
import com.mojang.datafixers.DataFixer;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.SharedConstants;
import net.minecraft.core.HolderGetter;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.util.FileUtil;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
public class CachingStructureManager {
private static ThreadLocal<MessageDigest> digestThreadLocal = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("SHA-256");
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
});
private static final File STRUCTURE_CACHE_FOLDER = FileUtil.childFile(ModernFixPlatformHooks.INSTANCE.getGameDirectory().resolve("modernfix").resolve("structureCacheV1").toFile());
static {
STRUCTURE_CACHE_FOLDER.mkdirs();
}
public static StructureTemplate readStructure(ResourceLocation location, DataFixer datafixer, InputStream stream, HolderGetter<Block> blockGetter) throws IOException {
CompoundTag tag = readStructureTag(location, datafixer, stream);
StructureTemplate template = new StructureTemplate();
template.load(blockGetter, tag);
return template;
}
private static String encodeHex(byte[] byteArray) {
StringBuilder sb = new StringBuilder();
for(byte b : byteArray) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private static final Set<String> laggyStructureMods = new ObjectOpenHashSet<>();
private static final int MAX_HASH_LENGTH = 9;
private static String truncateHash(String hash) {
return hash.substring(0, MAX_HASH_LENGTH + 1);
}
public static CompoundTag readStructureTag(ResourceLocation location, DataFixer datafixer, InputStream stream) throws IOException {
byte[] structureBytes = toBytes(stream);
CompoundTag currentTag = NbtIo.readCompressed(new ByteArrayInputStream(structureBytes));
if (!currentTag.contains("DataVersion", 99)) {
currentTag.putInt("DataVersion", 500);
}
int currentDataVersion = currentTag.getInt("DataVersion");
int requiredMinimumDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
if(currentDataVersion < requiredMinimumDataVersion) {
/* Needs upgrade, try looking up from cache */
MessageDigest hasher = digestThreadLocal.get();
hasher.reset();
String hash = encodeHex(hasher.digest(structureBytes));
CompoundTag cachedUpgraded = getCachedUpgraded(location, truncateHash(hash));
if(cachedUpgraded == null)
cachedUpgraded = getCachedUpgraded(location, hash); /* pick up old cache */
if(cachedUpgraded != null && cachedUpgraded.getInt("DataVersion") == requiredMinimumDataVersion) {
ModernFix.LOGGER.debug("Using cached upgraded version of {}", location);
currentTag = cachedUpgraded;
} else {
/*
synchronized (laggyStructureMods) {
if(laggyStructureMods.add(location.getNamespace())) {
ModernFix.LOGGER.warn("The namespace {} contains an outdated structure file, which can cause worldgen lag. Please view debug.log for the full filename, determine which mod provides the structure, and report to the mod/datapack author, including the debug log.", location.getNamespace());
}
}
*/
ModernFix.LOGGER.debug("Structure {} is being run through DFU (hash {}), this will cause launch time delays", location, hash);
currentTag = DataFixTypes.STRUCTURE.update(datafixer, currentTag, currentDataVersion,
SharedConstants.getCurrentVersion().getDataVersion().getVersion());
currentTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
saveCachedUpgraded(location, hash, currentTag);
}
}
return currentTag;
}
private static File getCachePath(ResourceLocation location, String hash) {
String fileName = location.getNamespace() + "_" + location.getPath().replace('/', '_') + "_" + hash + ".nbt";
return new File(STRUCTURE_CACHE_FOLDER, fileName);
}
private static synchronized CompoundTag getCachedUpgraded(ResourceLocation location, String hash) {
File theFile = getCachePath(location, hash);
try {
return NbtIo.readCompressed(theFile);
} catch(FileNotFoundException e) {
return null;
} catch(IOException e) {
e.printStackTrace();
return null;
}
}
private static synchronized void saveCachedUpgraded(ResourceLocation location, String hash, CompoundTag tagToSave) {
File theFile = getCachePath(location, truncateHash(hash));
try {
NbtIo.writeCompressed(tagToSave, theFile);
} catch(IOException e) {
e.printStackTrace();
}
}
private static byte[] toBytes(InputStream stream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] tmp = new byte[16384];
int n;
while ((n = stream.read(tmp, 0, tmp.length)) != -1) {
buffer.write(tmp, 0, n);
}
return buffer.toByteArray();
}
}

View File

@ -1,25 +0,0 @@
package org.embeddedt.modernfix.util;
import org.embeddedt.modernfix.ModernFix;
public enum BakeReason {
FREEZE,
REMOTE_SNAPSHOT_INJECT,
LOCAL_SNAPSHOT_INJECT,
REVERT,
UNKNOWN;
private static BakeReason currentBakeReason = null;
private static boolean bakeReasonWarned = false;
public static BakeReason getCurrentBakeReason() {
if(currentBakeReason == null && !bakeReasonWarned) {
ModernFix.LOGGER.warn("No bake reason found, mixin probably not applied correctly", new IllegalStateException());
bakeReasonWarned = false;
}
return currentBakeReason;
}
public static void setCurrentBakeReason(BakeReason reason) {
currentBakeReason = reason;
}
}

View File

@ -15,4 +15,4 @@ public class CacheUtil {
}
});
}
}
}

View File

@ -96,7 +96,7 @@ public class DummyServerConfiguration implements WorldData {
}
@Override
public boolean getAllowCommands() {
public boolean isAllowCommands() {
return false;
}

View File

@ -13,7 +13,7 @@ import java.util.function.Function;
public class DynamicInt2ObjectMap<V> extends DynamicMap<Integer, V> implements Int2ObjectMap<V> {
public DynamicInt2ObjectMap(Function<Integer, V> function) {
super(function);
super(Integer.class, function);
}
@Override

View File

@ -11,8 +11,10 @@ import java.util.function.Function;
public class DynamicMap<K, V> implements Map<K, V> {
protected final Function<K, V> function;
private final Class<K> keyClass;
public DynamicMap(Function<K, V> function) {
public DynamicMap(Class<K> keyClass, Function<K, V> function) {
this.keyClass = keyClass;
this.function = function;
}
@ -38,6 +40,9 @@ public class DynamicMap<K, V> implements Map<K, V> {
@Override
public V get(Object o) {
if(!keyClass.isInstance(o)) {
return null;
}
return function.apply((K)o);
}

View File

@ -10,8 +10,8 @@ import java.util.function.Function;
public class DynamicOverridableMap<K, V> extends DynamicMap<K, V> {
private final Map<K, V> overrideMap;
public DynamicOverridableMap(Function<K, V> function) {
super(function);
public DynamicOverridableMap(Class<K> keyClass, Function<K, V> function) {
super(keyClass, function);
overrideMap = new Object2ObjectOpenHashMap<>();
}

View File

@ -0,0 +1,47 @@
package org.embeddedt.modernfix.util;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import java.util.Map;
import java.util.Set;
public class LRUMap<K, V> extends Object2ObjectLinkedOpenHashMap<K, V> {
private Set<K> permanentEntries = Set.of();
public LRUMap(Map<K, V> map) {
super(map);
}
@Override
public V put(K k, V v) {
return putAndMoveToLast(k, v);
}
@Override
public V get(Object k) {
return getAndMoveToLast((K)k);
}
public void setPermanentEntries(Set<K> permanentEntries) {
this.permanentEntries = permanentEntries;
}
public void dropEntriesToMeetSize(int size) {
// Increase allowed size quota to include permanent entries
size += permanentEntries.size();
int prevSize = size();
if(size() > size) {
var iterator = entrySet().iterator();
while(size() > size && iterator.hasNext()) {
var entry = iterator.next();
if(!this.permanentEntries.contains(entry.getKey())) {
iterator.remove();
}
}
if(ModernFixPlatformHooks.INSTANCE.isDevEnv()) {
ModernFix.LOGGER.warn("Trimmed map from {} to {} entries", prevSize, size);
}
}
}
}

View File

@ -1,8 +1,11 @@
package org.embeddedt.modernfix.world;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.saveddata.SavedData;
@ -17,6 +20,11 @@ public class StrongholdLocationCache extends SavedData {
chunkPosList = new ArrayList<>();
}
public static SavedData.Factory<StrongholdLocationCache> factory(ServerLevel serverLevel) {
// FIXME datafixer will probably throw on update
return new SavedData.Factory<>(StrongholdLocationCache::new, StrongholdLocationCache::load, DataFixTypes.SAVED_DATA_FORCED_CHUNKS);
}
public List<ChunkPos> getChunkPosList() {
return new ArrayList<>(chunkPosList);
}
@ -26,7 +34,7 @@ public class StrongholdLocationCache extends SavedData {
this.setDirty();
}
public static StrongholdLocationCache load(CompoundTag arg) {
public static StrongholdLocationCache load(CompoundTag arg, HolderLookup.Provider provider) {
StrongholdLocationCache cache = new StrongholdLocationCache();
if(arg.contains("Positions", Tag.TAG_LONG_ARRAY)) {
long[] positions = arg.getLongArray("Positions");
@ -38,7 +46,7 @@ public class StrongholdLocationCache extends SavedData {
}
@Override
public CompoundTag save(CompoundTag compoundTag) {
public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) {
long[] serialized = new long[chunkPosList.size()];
for(int i = 0; i < chunkPosList.size(); i++) {
ChunkPos thePos = chunkPosList.get(i);

View File

@ -32,10 +32,6 @@ accessible field net/minecraft/client/renderer/texture/Stitcher$Holder width I
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
accessible class net/minecraft/client/resources/model/ModelBakery$BlockStateDefinitionException
accessible field net/minecraft/network/syncher/SynchedEntityData itemsById Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
accessible field net/minecraft/network/syncher/SynchedEntityData ENTITY_ID_POOL Lit/unimi/dsi/fastutil/objects/Object2IntMap;
accessible field net/minecraft/network/syncher/SynchedEntityData lock Ljava/util/concurrent/locks/ReadWriteLock;
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
@ -44,13 +40,10 @@ accessible method net/minecraft/resources/ResourceKey <init> (Lnet/minecraft/res
accessible field net/minecraft/client/renderer/block/model/BlockModel GSON Lcom/google/gson/Gson;
accessible class net/minecraft/server/level/ChunkMap$DistanceManager
accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
accessible field net/minecraft/client/resources/model/ModelBakery bakedCache Ljava/util/Map;
accessible field net/minecraft/client/resources/model/ModelBakery ITEM_MODEL_GENERATOR Lnet/minecraft/client/renderer/block/model/ItemModelGenerator;
accessible method net/minecraft/client/resources/model/ModelBakery loadTopLevel (Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
accessible field net/minecraft/client/resources/model/ModelBakery topLevelModels Ljava/util/Map;
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/ModelBakery$TextureGetter;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
accessible method net/minecraft/client/resources/model/ModelBakery getModel (Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
@ -60,4 +53,5 @@ accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart d
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
accessible class net/minecraft/client/multiplayer/SessionSearchTrees$Key

View File

@ -29,13 +29,11 @@ configurations {
dependencies {
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${rootProject.mixinextras_version}")))
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
if(project.use_fabric_api_at_runtime.toBoolean()) {

View File

@ -3,7 +3,6 @@ package org.embeddedt.modernfix.fabric.mappings;
import net.fabricmc.loader.api.*;
import net.fabricmc.loader.impl.launch.FabricLauncherBase;
import net.fabricmc.loader.impl.launch.MappingConfiguration;
import net.fabricmc.mapping.tree.TinyMappingFactory;
import org.embeddedt.modernfix.util.CommonModUtil;
import java.lang.reflect.Constructor;
@ -28,7 +27,9 @@ public class MappingsClearer {
@SuppressWarnings("unchecked")
public static void clear() {
if(FabricLoader.getInstance().isDevelopmentEnvironment())
// TODO: Port this to Fabric Loader 0.15
if(true) //FabricLoader.getInstance().isDevelopmentEnvironment())
return; // never do this in dev
Optional<Version> loaderVersion = FabricLoader.getInstance().getModContainer("fabricloader").map(m -> m.getMetadata().getVersion());
@ -50,7 +51,7 @@ public class MappingsClearer {
MappingConfiguration config = FabricLauncherBase.getLauncher().getMappingConfiguration();
Field mappingsField = MappingConfiguration.class.getDeclaredField("mappings");
mappingsField.setAccessible(true);
mappingsField.set(config, TinyMappingFactory.EMPTY_TREE);
//mappingsField.set(config, TinyMappingFactory.EMPTY_TREE);
// clear useless intermediary->intermediary mappings
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();

View File

@ -0,0 +1,18 @@
package org.embeddedt.modernfix.fabric.mixin.core;
import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl;
import org.embeddedt.modernfix.ModernFixClientFabric;
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(ClientConfigurationPacketListenerImpl.class)
@ClientOnlyMixin
public class ClientCommonPacketListenerImplMixin {
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
private void signalTags(CallbackInfo ci) {
ModernFixClientFabric.commonMod.onTagsUpdated();
}
}

View File

@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftServer.class)
@ClientOnlyMixin
public class ClientMinecraftServerMixin {
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0))
private void markServerStarted(CallbackInfo ci) {
ModernFixClient.INSTANCE.onServerStarted((MinecraftServer)(Object)this);
}

View File

@ -15,9 +15,4 @@ public class ClientPlayNetHandlerMixin {
private void signalRecipes(CallbackInfo ci) {
ModernFixClientFabric.commonMod.onRecipesUpdated();
}
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
private void signalTags(CallbackInfo ci) {
ModernFixClientFabric.commonMod.onTagsUpdated();
}
}

View File

@ -17,7 +17,7 @@ public class MinecraftServerMixin {
ModernFixFabric.theServer = new WeakReference<>((MinecraftServer)(Object)this);
}
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0))
private void hookServerStarted(CallbackInfo ci) {
ModernFix.INSTANCE.onServerStarted();
}

View File

@ -1,31 +0,0 @@
package org.embeddedt.modernfix.fabric.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.BakedModel;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelState;
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 ItemOverridesFabricMixin {
/**
* @author embeddedt
* @reason servers insist on generating invalid item overrides that have missing models
*/
@WrapOperation(method = "bakeModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/resources/model/BakedModel;"))
private BakedModel bake(ModelBaker instance, ResourceLocation resourceLocation, ModelState modelState, Operation<BakedModel> original) {
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
try {
return original.call(instance, resourceLocation, modelState);
} finally {
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
}
}
}

View File

@ -1,19 +0,0 @@
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
import net.fabricmc.fabric.impl.client.model.ModelLoadingRegistryImpl;
import net.minecraft.client.resources.model.ModelBakery;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.annotation.RequiresMod;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ModelLoadingRegistryImpl.LoaderInstance.class)
@RequiresMod("fabric-models-v0")
@ClientOnlyMixin
public class LoaderInstanceMixin {
@Redirect(method = "finish", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/impl/client/model/ModelLoadingRegistryImpl$LoaderInstance;loader:Lnet/minecraft/client/resources/model/ModelBakery;"), require = 0)
private void keepLoader(ModelLoadingRegistryImpl.LoaderInstance instance, ModelBakery value) {
/* allow loading models to happen later */
}
}

View File

@ -1,185 +0,0 @@
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
import com.google.common.collect.ImmutableList;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.core.registries.BuiltInRegistries;
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 org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.ModelMissingException;
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.CallbackInfoReturnable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600)
@ClientOnlyMixin
public abstract class ModelBakerImplMixin implements IExtendedModelBaker {
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
@Shadow @Final private ModelBakery field_40571;
@Shadow public abstract UnbakedModel getModel(ResourceLocation arg);
@Shadow @Final private Function<Material, TextureAtlasSprite> modelTextureGetter;
private static final MethodHandle blockStateLoaderHandle;
static {
try {
blockStateLoaderHandle = MethodHandles.lookup().unreflect(ModelBakery.class.getDeclaredMethod(
FabricLoader.getInstance().getMappingResolver().mapMethodName(
"intermediary",
"net.minecraft.client.resources.model.ModelBakery",
"method_4716",
"(Lnet/minecraft/world/level/block/state/BlockState;)V"
),
BlockState.class
));
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private boolean throwIfMissing;
@Override
public boolean throwOnMissingModel(boolean flag) {
boolean old = throwIfMissing;
throwIfMissing = flag;
return old;
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void obtainModel(ResourceLocation arg, CallbackInfoReturnable<UnbakedModel> cir) {
if(debugDynamicModelLoading)
ModernFix.LOGGER.info("Baking {}", arg);
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
if(arg instanceof ModelResourceLocation && arg != ModelBakery.MISSING_MODEL_LOCATION) {
// synchronized because we use topLevelModels
synchronized (this.field_40571) {
/* to emulate vanilla model loading, treat as top-level */
Optional<Block> blockOpt = Objects.equals(((ModelResourceLocation)arg).getVariant(), "inventory") ? Optional.empty() : BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(arg.getNamespace(), arg.getPath()));
boolean invalidMRL = false;
if(blockOpt.isPresent()) {
/* load via lambda for mods that expect blockstate to get loaded */
ImmutableList<BlockState> states;
try {
states = extendedBakery.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), (ModelResourceLocation)arg);
} catch(RuntimeException e) {
states = ImmutableList.of();
invalidMRL = true;
// Fall back to getModel
cir.setReturnValue(this.field_40571.getModel(arg));
}
for(BlockState state : states) {
try {
blockStateLoaderHandle.invokeExact(this.field_40571, state);
} catch(Throwable e) {
ModernFix.LOGGER.error("Error loading model", e);
}
}
} else {
this.field_40571.loadTopLevel((ModelResourceLocation)arg);
}
if(!invalidMRL) {
cir.setReturnValue(this.field_40571.topLevelModels.getOrDefault(arg, extendedBakery.mfix$getUnbakedMissingModel()));
// avoid leaks
this.field_40571.topLevelModels.clear();
}
}
} else
cir.setReturnValue(this.field_40571.getModel(arg));
UnbakedModel toReplace = cir.getReturnValue();
if(true) {
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
try {
toReplace = integration.onUnbakedModelPreBake(arg, toReplace, this.field_40571);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e);
}
}
}
cir.setReturnValue(toReplace);
cir.getReturnValue().resolveParents(this.field_40571::getModel);
if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) {
if(arg != ModelBakery.MISSING_MODEL_LOCATION) {
if(debugDynamicModelLoading)
ModernFix.LOGGER.warn("Model {} not present", arg);
if(throwIfMissing)
throw new ModelMissingException();
}
}
}
@WrapOperation(method = "bake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/UnbakedModel;bake(Lnet/minecraft/client/resources/model/ModelBaker;Ljava/util/function/Function;Lnet/minecraft/client/resources/model/ModelState;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;"))
private BakedModel callBakedModelIntegration(UnbakedModel unbakedModel, ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, ResourceLocation location, Operation<BakedModel> operation) {
BakedModel model = operation.call(unbakedModel, baker, spriteGetter, state, location);
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
model = integration.onBakedModelLoad(location, unbakedModel, model, state, this.field_40571, spriteGetter);
}
return model;
}
/**
* @author embeddedt
* @reason emulate old function, to allow injectors to work
*/
/*
@Overwrite
public BakedModel bake(ResourceLocation arg, ModelState arg2) {
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked());
BakedModel existing = this.field_40571.bakedCache.get(key);
if (existing != null) {
return existing;
} else {
synchronized (this.field_40571) {
if(debugDynamicModelLoading)
ModernFix.LOGGER.info("Baking {}", arg);
UnbakedModel iunbakedmodel = this.getModel(arg);
// TODO: make sure parent resolution doesn't re-run many times
iunbakedmodel.resolveParents(this::getModel);
BakedModel ibakedmodel = null;
if (iunbakedmodel instanceof BlockModel) {
BlockModel blockmodel = (BlockModel)iunbakedmodel;
if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) {
ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake((ModelBaker)this, blockmodel, this.modelTextureGetter, arg2, arg, false);
}
}
if(ibakedmodel == null) {
// leave the original assignment in the same spot so wrapping injectors work
// this means two bakes might happen for missing models, but not much we can do
ibakedmodel = iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg);
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) {
// use a shared baked missing model
createBakedMissingModelIfNeeded(extendedBakery, iunbakedmodel, arg2, arg);
ibakedmodel = extendedBakery.getBakedMissingModel();
}
}
this.field_40571.bakedCache.put(key, ibakedmodel);
return ibakedmodel;
}
}
}
*/
}

View File

@ -1,328 +0,0 @@
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
/* low priority so that our injectors are added after other mods' */
@Mixin(value = ModelBakery.class, priority = 1100)
@ClientOnlyMixin
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
@Shadow @Final @Mutable public Map<ResourceLocation, UnbakedModel> unbakedCache;
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
@Shadow @Final private Set<ResourceLocation> loadingStack;
@Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception;
@Shadow @Final @Mutable
private Map<ResourceLocation, BakedModel> bakedTopLevelModels;
@Shadow @Final @Mutable private Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
@Shadow @Final @Mutable private BlockColors blockColors;
@Shadow @Final private static Logger LOGGER;
@Shadow
public abstract void loadTopLevel(ModelResourceLocation modelResourceLocation);
@Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation);
private Cache<ModelBakery.BakedCacheKey, BakedModel> loadedBakedModels;
private Cache<ResourceLocation, UnbakedModel> loadedModels;
private HashMap<ResourceLocation, UnbakedModel> smallLoadingCache = new HashMap<>();
private boolean ignoreModelLoad;
// disable fabric recursion
@SuppressWarnings("unused")
private boolean fabric_enableGetOrLoadModelGuard;
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;"))
private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) {
// we can handle recursion in getModel without issues
fabric_enableGetOrLoadModelGuard = false;
this.blockColors = val;
this.loadedBakedModels = CacheBuilder.newBuilder()
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
.maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT)
.concurrencyLevel(8)
.removalListener(this::onModelRemoved)
.softValues()
.build();
this.loadedModels = CacheBuilder.newBuilder()
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
.maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT)
.concurrencyLevel(8)
.removalListener(this::onModelRemoved)
//.softValues()
.build();
this.bakedCache = loadedBakedModels.asMap();
ConcurrentMap<ResourceLocation, UnbakedModel> unbakedCacheBackingMap = loadedModels.asMap();
this.unbakedCache = new ForwardingMap<ResourceLocation, UnbakedModel>() {
@Override
protected Map<ResourceLocation, UnbakedModel> delegate() {
return unbakedCacheBackingMap;
}
@Override
public UnbakedModel put(ResourceLocation key, UnbakedModel value) {
smallLoadingCache.put(key, value);
return super.put(key, value);
}
};
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
}
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0))
private void ignoreFutureModelLoads(CallbackInfo ci) {
this.ignoreModelLoad = true;
}
private <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
if(!debugDynamicModelLoading)
return;
Object k = notification.getKey();
if(k == null)
return;
ResourceLocation rl;
boolean baked = false;
if(k instanceof ResourceLocation) {
rl = (ResourceLocation)k;
} else {
rl = ((ModelBakery.BakedCacheKey)k).id();
baked = true;
}
/* can fire when a model is replaced */
if(!baked && this.loadedModels.getIfPresent(rl) != null)
return;
ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl);
}
private UnbakedModel missingModel;
private Set<ResourceLocation> blockStateFiles;
private Set<ResourceLocation> modelFiles;
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), index = 1)
private Object captureMissingModel(Object model) {
this.missingModel = (UnbakedModel)model;
this.blockStateFiles = new HashSet<>();
this.modelFiles = new HashSet<>();
return this.missingModel;
}
/**
* @author embeddedt
* @reason don't actually load most models
*/
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadTopLevel(Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
private void addTopLevelFile(ModelBakery bakery, ModelResourceLocation location) {
if(location == MISSING_MODEL_LOCATION || !this.ignoreModelLoad) {
loadTopLevel(location);
}
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0))
private void fetchStaticDefinitions(Map<ResourceLocation, StateDefinition<Block, BlockState>> map, BiConsumer<ResourceLocation, StateDefinition<Block, BlockState>> func) {
/* no-op */
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;", ordinal = 0))
private ImmutableList<BlockState> fetchBlocks(StateDefinition<Block, BlockState> def) {
/* no-op */
return ImmutableList.of();
}
/**
* Make a copy of the top-level model list to avoid CME if more models get loaded here.
*/
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;values()Ljava/util/Collection;", ordinal = 0))
private Collection<?> copyTopLevelModelList(Map<?, ?> map) {
return new ArrayList<>(map.values());
}
private BiFunction<ResourceLocation, Material, TextureAtlasSprite> textureGetter;
@Inject(method = "bakeModels", at = @At("HEAD"))
private void captureGetter(BiFunction<ResourceLocation, Material, TextureAtlasSprite> getter, CallbackInfo ci) {
this.ignoreModelLoad = false;
textureGetter = getter;
DynamicBakedModelProvider.currentInstance = (DynamicBakedModelProvider)this.bakedTopLevelModels;
}
@Redirect(method = "bakeModels", at = @At(value = "INVOKE", target = "Ljava/util/Map;keySet()Ljava/util/Set;"))
private Set<ResourceLocation> skipBake(Map<ResourceLocation, UnbakedModel> instance) {
Set<ResourceLocation> modelSet = new HashSet<>(instance.keySet());
if(modelSet.size() > 0)
ModernFix.LOGGER.info("Early baking {} models", modelSet.size());
return modelSet;
}
/**
* Use the already loaded missing model instead of the cache entry (which will probably get evicted).
*/
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1))
private Object getMissingModel(Map map, Object rl) {
if(rl == MISSING_MODEL_LOCATION && map == unbakedCache)
return missingModel;
return unbakedCache.get(rl);
}
@ModifyVariable(method = "cacheAndQueueDependencies", at = @At("HEAD"), argsOnly = true)
private UnbakedModel fireUnbakedEvent(UnbakedModel model, ResourceLocation location) {
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
try {
model = integration.onUnbakedModelLoad(location, model, (ModelBakery)(Object)this);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception firing model load event for {}", location, e);
}
}
return model;
}
private int mfix$nestedLoads = 0;
/**
* @author embeddedt
* @reason synchronize
*/
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
public void getOrLoadModelDynamic(ResourceLocation modelLocation, CallbackInfoReturnable<UnbakedModel> cir) {
if(modelLocation.equals(MISSING_MODEL_LOCATION)) {
cir.setReturnValue(missingModel);
return;
}
UnbakedModel existing = this.unbakedCache.get(modelLocation);
if (existing != null) {
cir.setReturnValue(existing);
} else {
synchronized(this) {
if (this.loadingStack.contains(modelLocation)) {
throw new IllegalStateException("Circular reference while loading " + modelLocation);
} else {
this.loadingStack.add(modelLocation);
UnbakedModel iunbakedmodel = missingModel;
while(!this.loadingStack.isEmpty()) {
ResourceLocation resourcelocation = this.loadingStack.iterator().next();
mfix$nestedLoads++;
try {
existing = this.unbakedCache.get(resourcelocation);
if (existing == null) {
if(debugDynamicModelLoading)
LOGGER.info("Loading {}", resourcelocation);
this.loadModel(resourcelocation);
} else
smallLoadingCache.put(resourcelocation, existing);
} catch (ModelBakery.BlockStateDefinitionException var9) {
LOGGER.warn(var9.getMessage());
this.unbakedCache.put(resourcelocation, iunbakedmodel);
smallLoadingCache.put(resourcelocation, iunbakedmodel);
} catch (Exception var10) {
LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10);
this.unbakedCache.put(resourcelocation, iunbakedmodel);
smallLoadingCache.put(resourcelocation, iunbakedmodel);
} finally {
mfix$nestedLoads--;
this.loadingStack.remove(resourcelocation);
}
}
// We have to get the result from the temporary cache used for a model load
// As in pathological cases (e.g. Pedestals on 1.19) unbakedCache can lose
// the model immediately
UnbakedModel result = smallLoadingCache.getOrDefault(modelLocation, iunbakedmodel);
try {
// required as some mods (e.g. EBE) call bake directly on the returned model, without resolving parents themselves
result.resolveParents(this::getModel);
} catch(RuntimeException ignored) {}
// We are done with loading, so clear this cache to allow GC of any unneeded models
if(mfix$nestedLoads == 0)
smallLoadingCache.clear();
cir.setReturnValue(result);
}
}
}
}
private <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
return state.setValue(prop, (V)o);
}
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
private ImmutableList<BlockState> loadOnlyRelevantBlockState(StateDefinition<Block, BlockState> stateDefinition, ResourceLocation location) {
if(!(location instanceof ModelResourceLocation) || Minecraft.getInstance().getOverlay() != null)
return stateDefinition.getPossibleStates();
return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location);
}
@Override
public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) {
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(modelLocation, BlockModelRotation.X0_Y0.getRotation(), BlockModelRotation.X0_Y0.isUvLocked());
BakedModel m = loadedBakedModels.getIfPresent(key);
if(m != null)
return m;
ModelBakery self = (ModelBakery) (Object) this;
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
((IExtendedModelBaker)theBaker).throwOnMissingModel(true);
synchronized(this) { m = theBaker.bake(modelLocation, state); }
if(m != null)
loadedBakedModels.put(key, m);
return m;
}
@Override
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
return loadOnlyRelevantBlockState(stateDefinition, location);
}
public UnbakedModel mfix$getUnbakedMissingModel() {
return missingModel;
}
}

View File

@ -9,12 +9,10 @@ import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.CustomValue;
import net.fabricmc.loader.api.metadata.ModMetadata;
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.CreativeModeTabs;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.ModernFixFabric;
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
@ -24,9 +22,7 @@ import org.embeddedt.modernfix.util.CommonModUtil;
import org.objectweb.asm.tree.ClassNode;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
@ -70,7 +66,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
return FabricLoader.getInstance().getGameDir();
}
public void sendPacket(ServerPlayer player, Object packet) {
public void sendPacket(ServerPlayer player, CustomPacketPayload packet) {
//PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
}
@ -108,13 +104,6 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
return modOptions;
}
public void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator) {
CreativeModeTabs.searchTab().setSearchTreeBuilder((list) -> {
populator.accept(SearchRegistry.CREATIVE_NAMES, list);
populator.accept(SearchRegistry.CREATIVE_TAGS, list);
});
}
public void onLaunchComplete() {
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");

View File

@ -34,15 +34,15 @@
"modmenu": [ "org.embeddedt.modernfix.fabric.modmenu.ModernFixModMenuApiImpl" ],
"preLaunch": [
"org.embeddedt.modernfix.ModernFixPreLaunchFabric"
],
"jei_mod_plugin": [ "org.embeddedt.modernfix.searchtree.JEIRuntimeCapturer"]
]
},
"mixins": [
"modernfix-fabric.mixins.json",
"modernfix-common.mixins.json"
],
"depends": {
"minecraft": ">=1.16.2"
"minecraft": ">=1.16.2",
"fabricloader": ">=0.15.0"
},
"breaks": {
"dashloader": "<5.0.0-beta.1"

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